07***
2025-03-27

왜 이런 일이 발생했는가? 처음부터 실수를 예방할 수는 없는가? 앞으로도 비슷한 문제가 발생할 수 있는가? 팀원들이 똑같은 실수를 반복하지 않게 하려면 무엇을 해야 하는가? 결국 필자는 모든 실수에 통용되는 만병통치약은 없다는 것을 깨달았다. ⋯ 그럼에도 실수란 모름지기 반복되는 법이다. 따라서 실수는 분류할 수 있으며 각각의 유형마다 일반화된 대응책도 있다. 앞길에 존재하는 함정을 미리 아는 것만으로도 함정을 피할 준비는 이미 갖춰진 셈이다.
세상 그 어떤 유능한 개발자도 실수할 수 있다. 그리고 그로 인해 발생한 서비스 오류는 엄청난 손실을 야기한다. 저자 타기르 발레예프는 약 15 여년간 상업용 소프트웨어 개발자로서 근무하며, 여느 때처럼 오작동을 바로잡기 위해 일하고 있었다. 그러다 FindBugs(현 SpotBugs)라는 자바 정적 분석 도구를 접하게 되었고, 몇 분만에 수십 개의 실수를 지적해주는 정적 분석의 열렬한 팬이 되어버렸다. 그러나 이 도구를 적극적으로 활용하려 할 수록, 오탐지와 심각하지 않은 문제를 마주하게 되었고, 결국 은탄환은 없음을 깨닫게 되었다. (No Silver Bullet)
IntelliJ IDEA의 정적 분석팀으로 10년 동안 근무하며, 그는 한계가 있음을 깨닫는 데 그치지 않았고, 그럼에도 문제를 줄이기 위한 방법을 고민했다. 이 책은 그의 고민이 오롯이 담긴, 살아 숨 쉬는 100종의 정수(精髓; essence)이다. 책에서는 컴파일 시점에는 알 수 없었던 '진짜' 문제만을 대상으로 이를 유형화하고, 떄로는 실제 프로덕션 코드로 이를 설명한다. 개발자의 의도는 무엇이었고, 문제의 원인은 무엇이고, 어떻게 문제를 해결하는지 또 실수를 예방할 수 있는지, 각 문제의 유형에 최적화된 해결책을 제시한다.


개발자의 의도와 달리, 컴파일러가 실제로 어떻게 동작하는지 설명한다.
Java 프로그램을 작성할 때, 컴파일 오류를 해결하기 위해 그렇게 많은 시간을 쏟는 개발자는 없을 것이다. Java 컴파일러는 꽤나 엄격한 편이고, 빌드 시 발생한 예외와 그 위치, 심지어는 코드 작성 시점에 IDE에 그어진 빨간 줄만 보더라도 '아차, 이걸 놓쳤었네' 하고 뚝딱 해결할 것이다. 그러니 실질적으로 개발자들을 열받게 만드는 것은 런타임 오류라는 데에는 모두 이견이 없으리라 생각한다.
비교적 우리가 익숙하게 인지하고 있는 기본적인 유형은 좀 더 자세히 다루고 있다는 느낌을 받았다, 예를 들어, 표현식을 다루는 2장에서는 연산자에 대한 내용이 많다. 우선순위의 경우, 1 + 1 * 4 != 8 라는 자명한 사례 대신, 1 + 1 << 2 == 8 처럼 비트 시프트 등의 다소 이색적인 사례를 소개한다. 논리 연산의 경우라면? 논리 합 또는 곱에 &&, || 연산자를 사용하는 것은 자명한데, 이러한 단락 연산자(short-circuit operator)는 비단락 연산자 &, |와 어떻게 다른고, 어떻게 문제가 생길 수 있는지, 또 복합 할당 연산으로 활용될 때는 어떤 점을 조심해야 하는지 보다 심도있는 내용을 다룬다. 이 책에서 기본이란 이런 느낌이다.
한 가지 예시를 살펴보자. 나는 다음 코드를 보며 고개를 갸우뚱했다.
"음.. 여기에 문제가 있다고?"
2.7.2 - using non-short-circuit operator / before, after
일단 개발자의 의도는 세 종류의 검사 모두 통과하는 경우에만 true를 반환하는 것이다. 그리고 이 과정을 복합 할당 연산자로서의 비단락 연산 &=으로 구현했다. 비단락 연산은 논리곱 &&과는 달리, 피연산자(operand) 내 false가 있더라도 연산을 수행한다. 따라서, 검사 로직이 복잡하다면 불필요하게 프로그램의 복잡도를 높이는 안티 패턴이기에 저자는 이를 지적한 것이다. 다음 두 코드를 통해 실행되는 횟수에 차이가 있음을 확인할 수 있다.


2.7.2 - using non-short-circuit operator / class impl, test count
물론 복잡하게 중첩되어 로직에 오류를 만드는 사례도 소개한다. 해당 사례에서는 정적 분석기에서 이를 어떻게 탐지하는지 소개하고, 실수 방지 가이드에 비단락 논리 연산자를 사용하지 않도록 권고한다. 위 코드는 분명 성능 최적화의 관점에서 잘못되었고, 단락 논리 연산자를 사용해서 해결하는 것이 합리적으로 보인다. 그러나 내가 처음 가졌던 의문은 '오류(error)를 만드는 것이 아니라면, && 대신 &를 사용했다고 이를 잘못되었다 판단하는 것이 과연 적절한가?' 그러니까 '&를 의도적으로 사용한 로직에 대해서는 false positive 아닌가?' 이었다.
저자는 '양쪽 피연산자 평가'라는 제목으로 절의 마지막에서 이러한 내용을 다루고 있다. check라는 피연산자가 모두 의도적으로 부수 효과(side effect)를 가지고 있다면, 단락 논리 연산자 &&를 사용했을 때 첫 번째의 결과에 의해 나머지가 실행되지 않을 수 있다. 이런 경우에는 & 사용이 합당한데, 심지어 이런 경우에도 &&를 사용하도록 리팩터링하거나, 명시적으로 정적 분석 억제 주석을 추가하도록 권고하고 있다.
그러니까 일반화하자면, 이 책에서 나오는 '의도하지 않았던 동작의 원리'는 사실 언제든 '의도적으로' 활용될 수 있다. 그리고 그 때, 비록 결과가 의도한대로 정상적으로 출력되더라도, 동일한 동작을 더 분명하게 표현할 수 있는 방법이 있다면 그렇게 바꾸어 나가야 한다. 그것이 리팩터링이고, 이 책에서 소개하는 자바 코드의 '품질을 높이는' 길이다. 이처럼 2장 7절 비단락 연산자 예제는 이 책에서 저자가 코드로부터 개발자의 의도를 파악하기 위해 노력하고, 깊이 있게 고민했던 것을 느낄 수 있는 대목 중 하나였다.
책을 읽다보면 군더더기 없이 깔끔하게 잘 정돈되어 있다는 느낌이 든다. 각 절마다 다양한 하위 주제로 유형을 상세히 구분하고, 문제 및 대응 방안을 요약 정리한다. 가령, 2장 표현식 마지막에서는 내용을 총 정리한다. 내용은 다음과 같다.
[서드 파티는 다루지 않는다] 간혹 어노테이션 패키지, 테스트 프레임워크에 대한 내용이 나오긴 하지만, 그 외 대부분 Java 표준 라이브러리에 대한 내용을 중심으로 다룬다. 따라서, Spring 프레임워크의 활용에 관심이 많다면 이 책은 적절하지 않다. 가령, 3장 프로그램 구조에서의 문제 중 일부는 프레임워크 기능을 활용하여 해결할 수도 있을 것이다.
[코드 전문이 제공되지 않는다] 직접 실행해보며 내용을 검증하려는 이들이 있다면 유감이다. 이 책에는 잘못된 코드의 패턴만을 간략히 소개하기 때문에, 실행 가능한 형태의 코드 전문을 항상 제공하지는 않는다. 이러한 이유로 나 또한 새롭게 레포를 생성하여 2장의 14개 절에 대해서는 나름대로 실습을 진행해보았다. 필요하다면 다음 레포와 사이트를 참고하자
의심할 여지가 없는 수작이다. Java 프로그래밍에 관심 있는 그 누가 보더라도 도움이 될만한 책이라고 감히 단언할 수 있다
...라고 생각하지만, 소수의 의견도 항상 존재하는 법 ? 해외 책 리뷰 사이트에서 4.29/5의 평점이었는데, 그 중 낮은 평점을 제시한 한 의견을 찾아보았고, 이렇게도 생각할 수 있겠구나 흥미로워 가져왔다.
goodreads.com: 그리고 chatGPT 번역
책을 쓰는 것은 대담한 도전이라고 생각한다. 원고를 제출하는 순간, 저자는 낯선 사람들이 몇 시간 동안 읽을 가치가 있는 글을 썼다고 주장하는 셈이니까. 하지만 이 책에서는 내가 기대했던 우아함과 '아하!' 하는 깨달음이 부족하다고 느꼈다. 마치 전형적인 "고전적인 엔지니어링 서적"에서 예상할 수 있는 것처럼, 다소 인공적이고 야심(또는 열정)이 부족한 느낌이었다.
...(중략)
내가 느낀 가장 큰 문제는, 저자가 단순히 소나(Sonar) 이슈 목록을 펼쳐놓고 하나씩 예제를 설명하는 것처럼 보였다는 점이다. 몇 가지 메모를 남기기는 했지만, 350페이지를 읽고 난 후에도 내가 자바나 프로그래밍 기법에 대한 사고가 기대했던 만큼 발전했다고 느끼지는 않았다.
아마 이 리뷰어는 로버트 마틴의 [클린 코드], 켄트 벡의 [테스트 주도 개발] 같은 느낌의 책을 기대하지 않았을까 싶다. 내 기억으로는 두 책 모두 일정한 형식을 가지고 잘 정돈된 느낌보다는, 서사에 맞춰 저자가 자연스럽게 자신의 이야기를 하는 느낌이었다. 두 책과 비교하자면, 이 책은 다소 실무 메뉴얼 같은 느낌이다. 나름의 정해진 템플렛이 있고, 주제에 따라 그 내용만 다르다. 개인적으로는 이렇게 잘 정돈된 책이야말로, 나중에 다시 폈을 때도 읽기 쉬워 좋은 책이라 생각하긴 한다. 사고력 증진에 대한 의견은, 아마 리뷰를 작성한 사람이 이 이슈들에 이미 충분히 익숙하기 때문이라 생각한다. 하지만 적어도 저자가 의도했던 이 책의 대상 독자 - 실무 경험이 충분하지 않은 중급 개발자에 해당된다면 큰 도움을 얻을 수 있을 것이라 생각한다.
[링크]
I believe that this book is most useful for middle-level software developers who already know the Java language but may have not enough practical programming experience. It is likely that some bug patterns described in the book may be unknown to senior software developers as well. Less experienced developers or even advanced students might also find this book interesting.