FindBugs 적용 후 흔히 발생하는 버그 패턴들 프로그래밍

회사의 모든 프로젝트는 FindBugs 지표 수집을 하게 돼 있는데 이를 SonarQube를 통해 프로젝트 건강성 지표로만 삼고 있었다(findbugs, jacoco code coverage, checkstyle, pmd 등을 수집한다).
하지만 지표는 참조일뿐 강제가 아니고, 바쁘면 바쁘다는 핑계로 무시하게 마련이다.

그러다가 팀원들과 팀 프로젝트에서 FindBugs 의 버그 탐지를 단순 지표가 아닌 build 실패로 간주하도록 (빌드가 실패하면 당연히 배포 불가이다) 하기로 논의하여 결정하고 적용했더니 매주 1~2건씩은 배포 전에 버그를 탐지해 막아주고 있다(물론 이 버그들은 코드 Coverage 100%의 테스트를 했다면 발생하지 않았겠지만 그건 다음에...).
물론 Build 행위는 Jenkins + Gradle 조합으로 이뤄지며 Jenkins의 FindBugs 플러그인으로 FindBugs 의 버그가 탐지되면 빌드 실패로 간주하고 배포를 불가능하게 설정하였다.

FindBugs는 가급적 단순 지표가 아닌 실제 배포 가능 여부를 가늠하는 도구로써 버그 탐지시에는 실질적으로 컴파일이 안 된 것과 마찬가지로 취급하는 것이 좋겠다.

지금까지 내가 본 흔히 나오는 버그 패턴은 다음과 같다(컴파일은 되지만 100% 버그이므로 따라하면 안됨!!).

1. null 체크해놓고 그 객체 호출하기(보통은 logging 시에 실수로 호출함)
if (a == null) {
....
    log.debug("a.something {}", a.getSomething()); // 로깅 코드에서 null pointer exception이 발생해서 로그도 안남고 에러가 남.
}


2. 서로 다른 타입간의 equals 호출
Integer a = ..;
Long b = ..;
if (a.equals(b)) { // 언제나 false
...
}


또한 흔한게 enum 과 String 비교.
String enumValue = "..";
if (SomeEnum.ENUMVALUE.equals(enumValue)) { // 언제나 false
...
}


enum은 아예 equals()를 사용하지 말고 == 비교를 하는 게 낫다.

3. 숫자 Wrapper 객체 동일성 == 비교
Integer a = new Integer(1);
Integer b = new Integer(1);
if (a == b) { ... } // false이다.


Wrapper 객체는 equals() 로 비교해야 한다.

4. List/Map 등 컬렉션에서 엉뚱한 객체로 get 하기
안타깝게도 List.indexOf()와 Map.get()은 인자로 지정된 Generic Type을 받는게 아니라 Object를 받는다. 이로 인해 버그가 발생한다(이렇게 한 이유가 있겠지...).
List<Integer> integers = ...;
Long someLongValue = 1L;

// Integer 1 값이 리스트에 있어도 올바로 판단 못함
if (integers.indexOf(someLongValue) >= 0) { // false이다.
...
}


Map.get()도 마찬가지이다. 특히 이 경우의 버그가 많이 발생한다.
Map<Integer,String> map = new HashMap<>();
map.put(1, "하나");
map.put(2, "둘");

System.out.println(map.get(1L)); // null


예제에서는 단순화하려고 값을 직접 넣었지만 대부분은 메소드 파라미터로 받았기 때문에 코드의 호출부에서는 파라미터의 타입이 눈에 안 띄어서 버그여부를 인지하기가 쉽지 않다.

위에서 보다시피가장 많은 버그는 메소드의 인자가 Object 일 때 발생한다(equals, List.indexOf, Map.get). 컴파일 에러가 안나기 때문이다.

FindBugs 가 정착이 다 되면, 이후에는 다음과 같은 것도 해볼까 생각중.
  1. Jenkins JaCoCo Plugin 을 통해 현 시점의 코드 커버리지를 임계치로 설정하고 임계치보다 떨어지면 빌드 실패하게, 그리고 1주일에 1%씩 임계치 증가시키키(아무튼 최소한 커버리지를 떨어뜨리지는 못하게)
  2. Jenkins CheckstyleJenkins PMD 적용하고 임계치를 서서히 증가시키기

위 툴들은 우리회사에서는 이미 SonarQube를 통해 지표 수집은 다 하고 있는 것들이다. 거기에 강제성을 부여하는 것만 더 하고 싶은 것.


마지막으로.. FindBugs가 버그를 찾아줬으니 버그가 없다는 착각 따위는 하지 말 것. 결국은 열심히 단위 테스트를 해야 한다..

단점 : 빌드 시간이 너무 길어져서 배포 시간이 증가했다... ㅜㅜ


덧글

댓글 입력 영역