![[도서 리뷰] 자바 코드의 품질을 높이는 100가지 방법, 자바 베테랑이 전하는 실전 오류 패턴과 해법](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxDbT0%2FbtsMWDbQk6S%2FCL2WN6EqX0bDaWFD8Aky60%2Fimg.jpg)

한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 협찬 받아 작성된 서평입니다.
📖 도서 정보
https://www.hanbit.co.kr/store/books/look.php?p_code=B4010786144
자바 코드의 품질을 높이는 100가지 방법
자바 코드 작성의 함정, 정적 분석 도구로 해결하라
www.hanbit.co.kr
📖 리뷰
챕터 목차
- 코드 품질 관리
- 표현식
- 프로그램 구조
- 숫자
- 일반적인 예외
- 문자열
- 객체 비교
- 컬렉션과 맵
- 라이브러리 메서드
- 유닛 테스트
이 책은 자바 개발자가 흔히 저지르는 실수 100가지를 분석하고, 구체적인 사례와 함께 해결 방법을 제시한다는 점에서 인상깊었습니다.
챕터1에서는 코드 품질을 높이고 실수를 방지하기 위해 개발 문화와 도구를 어떻게 활용해야 하는지 설명합니다. 코드를 작성하는 것은 결국 사람이기 때문에 모든 실수를 사전에 방지하는 것은 현실적으로 불가능합니다. 그러나 코드 리뷰, 페어 프로그래밍, 코드 스타일, 정적 분석 도구, 테스트 자동화 등을 활용하면 실수를 줄이고 코드 품질을 유지하는데 도움이 된다고 설명합니다.
👨🏻💻 개인적으로 Spring & Java 개발을 하면서 아래 조합은 기본으로 가져갑니다
- IDE: IntelliJ
- 빌드 툴: Gradle
- 테스트: JUnit5, Mockito, etc..
- 테스트 커버리지: Jacoco
- 정적 분석: SonarQube
- 컨벤션: google-java-format
개인적으로 책을 읽으며 뮤테이션 커버리지, 동적 분석 등 접해보지 않았던 새로운 도구를 알게 된 점이 유익했습니다. 또한, 도구라는게 모든 문제를 해결해주지 않기 때문에 이를 간과해서는 안된다는 점을 저자는 강조합니다. 이를 통해 기술과 도구는 비즈니스 문제 해결을 위한 수단이어야 하며, 그 자체가 목적이 되어서는 안 된다는 점을 다시 생각해 볼 수 있었습니다.
🤦 경험해 봤던 실수 5 가지
챕터2. 표현식부터 각 주제별로 "실수 설명 → 해결 방법 → 사례 → 정리" 구조로 설명합니다. 이때 연산자 우선순위, 음수, 언더플로, 오버플로, 비트연산, 유니코드 등과 관련된 실수를 다룹니다. 경험이 부족한 개발자라면 이해하기 어려울 수도 있지만, 컴퓨터 공학 지식과 함께 실수를 찾아낼 수 있는 능력을 갖춘다면 개발자로서의 경쟁력을 강화할 수 있을 거라는 배움을 얻을 수 있었습니다. 챕터3. 프로그램 구조는 가벼운 Java 문법적인 내용이 많았는데 코딩 테스트 연습을 할 때 한번씩 했던 실수라서 쉽게 공감할 수 있었습니다.
아래에는 도서를 읽으면서 공감했던 실수를 간단히 정리해보았습니다.
💩 실수018. 구형 for 루프 오류
- 중접된 루프를 작성할 때 같은 실수를 범하기 쉽다
- 언뜻 보면 눈에 잘 띄지 않지만 내부 루프에서 외부 루프의 변수 i 를 증가시킨다
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; i++) { // 💩
// do something
}
}
+ 이러한 실수는 IDE를 사용하지 않는 경우 찾기도 힘들고 경우에 따라 무한 루프를 발생시키기도 한다
✨ 실수 방지 가이드
- 지정한 범위를 순회하는 관용적인 루프문을 만들 때는 IDE가 제공하는 코드 템플릿을 활용한다.
- IntelliJ IDEA는 fori라는 라이브 템플릿이 있다
- 루프 코드 테스트는 내부와 외부 루프가 모두 최소한 두 번 이상 반복되도록 구성한다. (..생략)
💩 실수020. 잘못된 루프 방향
- 루프 방향을 바꿀 때는 조건의 초기값이나 경계값을 잘 살피고 1씩 어긋나는 일이 없도록 해야 한다
// before
for(int i = lo; i < hi; i++)
// 💩 i 증감연산자 틀림
for(int i = hi - 1; i >= lo; i++)
✨ 실수 방지 가이드
- IDE에는 기존 루프의 방향을 반전시키는 기능이 있다.
- 예로 IntelliJ IDEA는 루프 선언부에서 "Reverse Direction of for Loop" 기능을 실행하면 루프가 반전된다
- 그리고 배열 및 List에 역방향 루프를 만드는 forr 접미사 템플릿이 있다
- 자바 21에서 List를 역순으로 순회할 때는 List 인터페이스에 새롭게 추가된 reversed() 메서드를 먼저 고려한다. (..생략)
💩 실수027. 수치 오버플로
- 오버플로 문제는 조용히 발생한다.
- 즉시 감지하지 못하면 훨씬 나중에 많은 추가 계산을 동원해야만 결과가 잘못되었다는 것을 알아차릴 수 있다.
- 즉, 실수의 원인을 식별하기 어렵다.
✨ 실수 방지 가이드
- 정수 연산을 다룰 때에는 늘 오버플로 가능성을 고려한다. 곱셈은 연산 결과가 피연산자보다 상당히 커질 수 있으므로 가장 위험한 연산이다. 경험적으로 int 피연산자 중 50,000이 넘는 수가 있으면 오버플로 가능성을 염두에 두고 더 주의 깊에 확인하는 것이 좋다.
- 정수 계산에서 오버플로우를 방지하려면 BigInteger 클래스를 사용하면 된다. 단순 연산보다 상당히 느려질 가능성은 있지만, 오버플로는 확실히 예방할 수 있다, int 대신 long 타입을 사용하는 방법도 실제로 많은 경우에 효과가 있으며 성능 저하도 심하지 않다. 프로젝트 개발 초기에는 20억 이상의 수치가 비현실적으로 느껴짐에도 데이터는 빠르게 증가한다는 점을 잊지 말아야 한다
int * int 곱셈 연산에서 int 최대치 21억을 넘어가는 경우 long 캐스팅 전이기 때문에 오버플로가 발생한다. 이 경우 피연산자 하나를 long 캐스팅 하거나 L을 붙여서 오버플로 방지할 수 있다.
//💩
long result = second * 10;
//✨
long result = (long) second * 10;
long result = second * 10L;
+ 백준 문제 풀이 했을 때 오버플로 문제로 고생했던 기억이 짜릿하게 남아 공유해본다. 특정 문자열을 base 진법으로 변환했을 때 최대 2^63, 즉 long의 최대치를 초과할 수 없는 문제였다. 그래서 💩 같이 if 조건문을 작성했는데, long 범위 초과해서 오버플로 발생했었다.
private long convertTo(String str, int base) {
long result = 0L;
for(char c : str.toCharArray()) {
if(convert(c) >= base) return -1;
if(result * base + convert(c) > Long.MAX_VALUE) { // 💩
return -1;
}
result *= base;
result += convert(c);
}
return result;
}
해결 방법은 간단하다. 좌변에 있는 항을 우변으로 이항시키면 안정적으로 처리가능했다 (🍯Tip)
if(result > (MAX_VALUE - convert(c)) / base) return - 1; // ✨
💩 실수028. 정수 나눗셈 중 반올림
- 나눗셈은 주의 깊게 사용한다.
- 소수점 이후를 잘라내고 싶은지 포함시키고 싶은지 명확히 표현한다.
소수점 부분이 필요하다면 다음과 같이 명시적인 부동소수점 상수나 캐스트 연산자를 사용한다
// 💩
double half = value / 2;
// ✨
double half = (double) value / 2;
double half = value / 2.0;
+ 나눗셈의 밑수가 0인 경우 java.lang.ArithmeticException: / by zero 예외가 발생하므로 이 또한 주의가 필요하다
💩 실수032. 무조건적인 축소 변환
- 때로는 양쪽의 숫자 타입이 일치하지 않아 명시적으로 타입을 변환해야 할 때가 있다
- 이때 가장 위험한 변환은 아마도 long 에서 int 타입으로 변환일 것이다
✨ 실수 방지 가이드
- long을 int로 변환하는 명시적 축소 캐스트는 해당 값이 대상 타입의 최댓값을 초과하지 않는지 꼭 확인한다. 또한 그러한 캐스트 연산이 안전한 이유를 설명하는 주석을 추가하는 것이 좋다
- 확신이 서지 않으면 (int) 캐스트 대신 Math.toIntExact() 메서드를 고려한다. 이 메서드는 long을 int로 정확히 표현할 수 없을 때 ArithmeticException 예외를 던진다. 예외 정보를 통해 문제 지점을 파악하고 프로그램의 오작동이나 데이터 손실을 방지할 수 있다.
+ Stream의 count()를 사용할 때 int로 강제 캐스팅하는 경우가 많았다. 예제 학습이였기 때문에 최대값을 초과하지 않는다는 확신은 있었지만, 지금 생각해보면 좋지 않은 습관으로 보였다.
// 리팩터링 2판 예시 中
public int numAdvancedCourses() {
return (int) this.courses.stream()
.filter(Course::isAdvanced)
.count();
}
👨🏻💻 그 밖에 좋았던 점
챕터5. 일반적인 예외 ~ 챕터 10. 유닛 테스트에서는 구현 경험이 있다면 한번씩 접해 볼 수 있는 실수라 공감하기 쉬웠고 새로운 내용도 알 수 있어 유익했습니다. 쉽게 접할 수 있는 NullPointException을 줄이기 위한 주요 원칙으로 ① null 자체를 사용하지 않거나, ② Optional 사용, ③ 어노테이션 명시, ④ 정적 도구 분석 도구 활용 등 실무에서 적용할 수 있는 유용한 가이드를 제시했습니다. 또한 잠재적으로 ClassCastException 야기할 수 있는 메서드에 대해 리팩터링 하기 위해 자바 9부터 추가된 @Deprecated(forRemoval = true) 활용해 클라이언트 코드가 마이그레이션 할 수 있도록 유도하는 예시가 인상 깊었습니다.
또한, 대체 라이브러리, 자바 버전별 기능, 실제 유명 기업의 사례를 보면서 단순한 기술 서적이 아니라 자바의 역사까지 함께 공부하는 느낌이 들어 좋았습니다.
💩 실수100. 양식이 잘못된 테스트 메서드 中
OpenJDK 처럼 큰 프로젝트에서도 이런 문제가 간헐적으로 발생한다. 자바 12에 단일 파일 프로그램(single-file program)이 도입됐을 당시, 이를 검사하는 유닛 테스트도 함께 추가되었다. 그러나 해당 테스트는 @Test 어노테이션이 빠져 있었다. 물론 테스트는 실행되지 않았고, 이 문제가 발견되기 전까지 해당 기능은 3년이 넘도록 테스트 없이 유지되었다
대체 라이브러리
- Stack ➡️ ArrayDeque 권장
- DateFormat, SimpleDateFormat ➡️ DateTimeForamatter 권장
문자열 포메팅 메서드 (자바 15 이상 지원)
return String.format("Value: %s", value != null ? value : "(unknwon)");
// ✨
return "Value: %s".formatted(value != null ? value : "(unknwon)");
+ 아쉽지만 현재 JVM에서 포맷팅 메서드는 문자열 연결보다 상당히 느리므로 빈번하게 실행되는 코드 경로에는 무턱대고 쓸 수 없다
null 체크 메서드
return "Value " + Objects.requireNonNullElse(value, "(unknwon)")
텍스트 블록 (자바 15 이상 지원)
String greetingPage = """
<h1>Hello, $user$</h1>
""".replace("$user$", userName);
향상된 switch문 (자바 14 이상 지원)
// 리팩터링 2판 10.4 예시
private String plumage(Bird bird) {
return switch (bird.type()) {
case "유럽 제비" -> "보통이다";
case "아프리카 제비" -> (bird.numberOfCoconuts() > 2) ? "지쳤다" : "보통이다";
case "노르웨이 파랑 앵무" -> (bird.voltage() > 100) ? "그을렸다" : "예쁘다";
default -> "알 수 없다";
};
}
📖 마치며
"자바 코드의 품질을 높이는 100가지 방법"을 읽으면서 모르는 내용도 많았지만 앞으로 개발자로서 가야 할 방향을 또 하나 찾을 수 있었던거 같습니다. (Spring & Java 웹 개발자로 커리어를 시작한지 6년이 넘었지만 아직 배울 게 있다는 것이 신나네요😄) 이 책은 기본기가 부족한 초보에게는 조금 부담되는 내용일지도 모릅니다. OOP, 테스트 자동화, 자료구조와 알고리즘 등 배워야 할 건 많은데, 커리큘럼은 명확하지도 않고 좋은 멘토라도 만나지 않으면 방향을 잃고 길을 헤메는 것이 너무나 당연한 현실입니다. 그럼에도 불구하고, 이 책은 자바 실수 방지 가이드북으로 충분히 추천할 만하다고 생각합니다. "자바 코드의 품질을 높이는 100가지 방법" 도서를 알게 된 덕분에 오늘도 어제보다 한 걸음 더 성장할 수 있었습니다.
버킷 리스트 중 하나인 산티아고 순례길(800km)에 여행자들 사이에서 전해 내려오는 말을 소개하며 글을 마무리하려 합니다.
- 방향만 맞다면 멀리 돌아가든 쉬었다 가든, 결국 목적지에 도착하는 것은 같다
이 리뷰가 도서 구매를 고민하는 분들에게 도움이 되었기를 바라며, 긴 글 읽어주셔서 감사합니다 😊
'독서 > 📚' 카테고리의 다른 글
[도서 리뷰] 그로킹 알고리즘(개정판) (1) | 2025.02.21 |
---|---|
[도서 리뷰] 단위 테스트의 기술 (Jest, JavaScript, TypeScript, 1~5장) (0) | 2025.01.25 |
[도서] 만화로 배우는 리눅스 시스템 관리1 - 요약 정리 (4) | 2025.01.21 |
[도서] 이것이 취업을 위한 코딩테스트다 (with 파이썬) 후기 (0) | 2025.01.10 |
[개발도서]프로그래머 열정을 말하다 (채드 파울러) (2) | 2024.05.02 |

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!