에러 메시지 'null pointer exception': 다시는 싫다
- 05 Dec, 2025
에러 메시지 ‘null pointer exception’: 다시는 싫다
오전 10시 32분
커피 마시다가 슬랙 알림이 울렸다. QA팀이다.
“개발님, 회원가입 안 돼요.”
손이 떨렸다. 어제 배포한 거다. 30분 전에 올라갔다. 벌써 터졌다.
로그를 켰다. 빨간 글씨가 가득하다.
java.lang.NullPointerException
at com.company.user.UserService.register(UserService.java:247)
또 null이다. 247번째 줄이다. 코드를 열었다.
String email = userDto.getEmail().toLowerCase();
getEmail()이 null을 뱉었다. 당연히 toLowerCase()에서 터진다.
“아…”
한숨이 나왔다. 어제 급하게 짠 코드다. null 체크를 안 했다. 3년 전 신입도 아니고, 7년 차가 이러면 안 되는데.
핫픽스 브랜치를 땄다. 손가락이 자동으로 움직인다.
if (userDto.getEmail() == null) {
throw new IllegalArgumentException("이메일은 필수입니다");
}
5분 컷이다. 커밋, 푸시, PR, 머지. 배포는 10분.
QA팀에 “해결했습니다” 보냈다. “감사합니다^^” 답장이 왔다.
고맙진 않다. 부끄럽다.

점심시간, 김치찌개집
사장님이 물었다. “오늘 표정 안 좋네요?”
“일이 좀…”
“개발자는 힘들어요. 우리 아들도 그래요.”
김치찌개를 떴다. 뜨거웠다. 혀가 데었다.
null pointer exception. NPE. 개발자라면 누구나 안다. 제일 흔한 에러다. 제일 짜증 나는 에러다.
왜 짜증 나냐면, 내 잘못이기 때문이다.
문법 에러는 컴파일러가 잡아준다. 네트워크 에러는 인프라 탓을 할 수 있다. DB 에러는… 뭐, DBA한테 물어보면 된다.
근데 NPE는 다르다. 내가 null을 만들었다. 내가 null 체크를 안 했다. 내가 실수했다.
그래서 더 화난다. 나한테.
후배 민수가 작년에 물었다. “형, null 체크 매번 하는 게 맞아요? 코드가 너무 길어져요.”
“해야지. 안 하면 터져.”
“Optional 쓰면 안 돼요?”
“그것도 결국 체크는 해야 해. 다른 방식일 뿐이지.”
민수는 고개를 끄덕였다. 그러고 2주 뒤에 NPE로 배포 롤백했다. 내가 리뷰할 때 못 봤다. 둘 다 잘못이다.

오후 3시, 회의실
장애 회고다. 팀장이 물었다.
“원인이 뭐였어요?”
“null 체크 누락이었습니다.”
“왜 누락됐죠?”
대답이 안 나왔다. ‘급했어요’라고 할 순 없다. 그건 핑계다.
“제가 놓쳤습니다.”
팀장이 한숨을 쉬었다. “다음부턴 조심하죠.”
회의가 끝났다. 자리로 돌아왔다.
모니터를 봤다. 내 코드에 null 체크가 몇 개나 있을까. 세어봤다. 파일 하나에 12개다.
if (user == null) return;
if (user.getName() == null) return;
if (request == null) throw new Exception();
if (request.getBody() == null) throw new Exception();
지겹다. 정말 지겹다.
코틀린으로 짜는 팀이 부럽다. Null Safety가 언어 레벨에서 된다. 자바는 안 된다. Optional이 있긴 한데, 레거시 코드는 다 null 투성이다.
“리팩토링 해야 하는데…”
혼잣말이 나왔다. 옆자리 수진이 물었다.
“뭘요?”
“아니, 아무것도.”
리팩토링할 시간이 어딨나. 기획자는 매주 새 기능을 요구한다. PM은 일정을 당긴다. 기술 부채는 쌓인다.
그래서 또 null 체크를 빼먹는다. 그래서 또 터진다. 악순환이다.

퇴근 직전, 6시 10분
민수가 PR을 올렸다. 리뷰 요청이 왔다.
코드를 열었다. 200줄짜리 서비스 로직이다. 쭉 읽었다.
50번째 줄에서 멈췄다.
Payment payment = paymentRepository.findById(paymentId);
payment.setStatus("COMPLETE");
findById()는 null을 리턴할 수 있다. 민수는 체크 안 했다.
코멘트를 달았다. “findById null 체크 필요합니다.”
5분 뒤에 민수가 수정했다.
Payment payment = paymentRepository.findById(paymentId);
if (payment == null) {
throw new NotFoundException("결제 정보가 없습니다");
}
payment.setStatus("COMPLETE");
Approve 눌렀다.
그러고 내 코드를 열었다. 어제 짠 거다. 다시 읽었다.
3군데에서 null 체크가 없었다. 아직 배포 안 된 코드다. 고쳤다.
“휴…”
7년 차다. 아직도 이런다. 언제까지 이럴 건가.
검색창에 쳤다. “how to avoid null pointer exception java”
스택오버플로우가 떴다. 똑같은 질문이 1만 개다. 똑같은 대답이다.
- null 체크 하세요
- Optional 쓰세요
- 애초에 null 반환하지 마세요
다 안다. 다 해봤다. 그래도 터진다.
왜냐면 사람은 실수하니까. 피곤하면 놓친다. 급하면 까먹는다. 리뷰어도 사람이라 못 본다.
결국 답은 하나다. 조심하는 수밖에.
저녁 9시, 집
아내가 물었다. “오늘 힘들었어?”
“응. 장애 났었어.”
“또? 지난주에도 그랬잖아.”
“응… 내 실수야.”
아내가 맥주를 건넸다. 땄다. 마셨다.
NPE 이야기를 했다. 아내는 디자이너라 잘 모른다. 그래도 들어줬다.
“그게 그렇게 자주 나는 거야?”
“응. 진짜 많이 나.”
“그럼 자동으로 체크하는 프로그램 같은 거 없어?”
“있어. SonarQube, SpotBugs… 다 있어. 근데 완벽하진 않아. 못 잡는 케이스도 많고.”
“흠…”
아내는 더 안 물었다. 나도 더 안 말했다.
맥주를 다 마셨다. 냉장고를 열었다. 한 캔 더 꺼냈다.
노트북을 켰다. 회사 코드를 열었다. 출퇴근 기록부는 6시 퇴근이다. 근데 지금 코드를 본다.
UserService.java를 열었다. 쭉 읽었다. null 체크를 추가했다. 5군데.
커밋 메시지를 썼다. “add null checks for safety”
푸시했다. PR 올렸다. 내일 아침에 머지하면 된다.
시계를 봤다. 10시 반이다.
“또 일해?”
아내 목소리다. 돌아봤다. 문 앞에 서 있다.
“응… 좀.”
“그만 해. 내일 하면 안 돼?”
“응. 내일 해야지.”
노트북을 덮었다. 근데 머릿속은 아직 코드다.
247번째 줄. getEmail(). toLowerCase(). null.
또 떠올랐다. 자기 전까지 계속 떠오를 것이다.
그래도 해야 한다
NPE는 없앨 수 없다. 완벽한 코드는 없다. 사람은 실수한다.
근데 줄일 순 있다.
null 체크를 습관화한다. PR 리뷰 때 집중한다. 단위 테스트를 꼼꼼히 짠다. 로그를 잘 남긴다.
그렇게 하면 조금 덜 터진다. 조금 덜 야근한다. 조금 덜 부끄럽다.
7년 차 개발자의 결론이다.
“null은 적이다. 근데 없앨 순 없다. 그러니까 경계해야 한다.”
이게 끝이다.
내일도 출근한다. 내일도 코드 짠다. 내일도 null 체크한다.
그러다 보면 또 놓친다. 또 터진다.
그럼 또 고친다. 또 배운다.
이게 개발자다.
오늘도 null 체크 30개 추가. 내일은 40개.
