회원가입 시 파트너 cascade 등급 누락 버그 수정
목차
회원가입 INSERT 흐름에서 파트너 cascade 등급 해석이 빠지면, 가입 직후 사용자에게 잘못된 등급이 배정된다. 겉으로 드러나는 증상은 늦게 나타나는 게 보통이다. 로그인 직후 첫 화면에서는 정상처럼 보이다가, 등급에 따른 혜택이나 접근 권한이 달라지는 시점에서야 이상하다는 걸 인식하게 된다.
이번에 잡은 버그가 딱 그 패턴이었다.
왜 조용히 터지는 버그가 제일 무섭나
문제 원인은 예외 처리와 분기 누락이었다. 파트너 cascade 등급을 해석하는 분기에서 특정 케이스가 처리되지 않고 조용히 넘어가고 있었다. 예외가 throw되지도 않았고, 로그에도 아무 흔적이 없었다. 코드는 "정상적으로" 실행됐지만, 등급 값은 기본값 또는 null 상태로 INSERT된 것.
이런 버그는 발견 자체가 늦다. 모니터링 알람이 울리지 않고, 에러 카운트도 올라가지 않으며, 서비스는 멀쩡히 돌아간다. 사용자가 직접 "내 등급이 이상한 것 같다"고 제보하거나, 특정 기능에서 숫자가 맞지 않는 걸 교차 확인하면서야 잡히는 유형이다.
비슷한 케이스를 묶어보면 공통 패턴이 있다.
- 분기 처리에서
else또는 default 케이스가 빠져 있음 - 예외를 잡되 아무것도 하지 않는 empty catch 블록
- 반환값이 null이거나 빈값인데 호출부에서 체크 없이 그대로 INSERT
// 위험한 패턴: 예외를 삼키고 아무것도 안 함
try {
grade = resolvePartnerCascadeGrade(partnerCode);
} catch (Exception e) {
// 나중에 처리하려 했다가 그냥 잊혀짐
}
member.setGrade(grade); // grade가 null인 채로 진행
// 수정 방향: 실패 시 명시적으로 처리하거나 최소한 로그라도 남김
try {
grade = resolvePartnerCascadeGrade(partnerCode);
} catch (Exception e) {
log.warn("파트너 cascade 등급 해석 실패. partnerCode={}", partnerCode, e);
grade = GradeType.DEFAULT; // 또는 정책에 따라 가입 자체를 차단
}
if (grade == null) {
throw new IllegalStateException("파트너 등급 해석 결과가 없음. partnerCode=" + partnerCode);
}
두 번째 패턴이 항상 정답은 아니다. null을 어떻게 처리할지는 도메인 정책에 달려 있다. 등급 미배정 상태로 가입을 허용할지, 아니면 가입 자체를 막을지는 요구사항 문제다. 중요한 건 그 판단을 코드가 조용히 생략해버리면 안 된다는 것. 실패를 감추지 말고 노출시켜야 한다.
이번 수정에서 변경한 내부 클래스는 2개였는데, 수정 범위를 좁히기 전에 비슷한 패턴이 다른 경로에도 있는지 먼저 훑었다. 같은 파트너 코드 해석 로직이 다른 진입점에도 존재했고, 위험한 케이스는 함께 손봤다. 수정 파일이 2개인 이유다.
버그 수정 프로세스 - 증상이 아니라 원인을 잡는다
증상 수정과 원인 수정은 다르다. grade가 null이면 DEFAULT를 때려넣으면 당장 에러는 사라진다. 그런데 왜 null이 됐는지를 모르면, 같은 구조의 버그가 다른 형태로 반드시 다시 나타난다.
이번 수정 흐름을 정리하면 이렇다.
| 단계 | 내용 |
|---|---|
| 증상 파악 | 특정 가입 케이스에서 파트너 cascade 등급이 잘못 배정됨 |
| 재현 | 해당 가입 플로우 직접 재현, 등급 확인 |
| 원인 추적 | INSERT 시점 로그 없음 확인 → 분기 누락 발견 |
| 범위 확인 | 유사 패턴이 다른 경로에도 있는지 코드 훑기 |
| 수정 | 내부 클래스 2개 로직 수정, 위험 케이스 포함 처리 |
| 검증 | 재현 케이스로 정상 동작 확인 + 관련 화면 숫자 cross-check |
"범위 확인" 단계를 빠뜨리는 경우가 많다. 수정 대상 파일만 고치면 그 경로는 해결된다. 하지만 같은 로직이 다른 클래스에 복사-붙여넣기 돼 있으면 거기선 여전히 같은 버그가 남아 있다. 이번에도 해당 단계를 건너뛰었으면 한 곳만 고치고 끝냈을 것이다.
검증 방식도 중요하다. 수정 후 버그를 직접 재현해서 동작을 확인했고, 등급이 연관되는 다른 화면의 숫자와 비교해서 정합성도 봤다. 숫자가 맞아떨어지는 걸 눈으로 확인하고 나서야 "이번엔 제대로 잡았다"고 판단할 수 있었다.
등급 도메인에서 정합성이 기본값이어야 하는 이유
사내 서비스를 만들다 보면 버튼 하나, 기능 하나가 실제로는 SQL 집계, 상태 머신, 예외 처리, 권한 체크가 다 엮여 있다는 걸 계속 체감한다. 그중 한 곳이라도 빠지면 특정 사용자에게만 이상한 화면이 나타나거나, 숫자가 맞지 않거나, 아무 에러 없이 조용히 데이터가 깨진다.
등급처럼 권한이나 혜택과 직결되는 값은 틀린 채로 저장되면 나중에 바로잡기가 복잡해진다. 이미 등급을 기준으로 집계된 데이터, 이미 발송된 메시지, 이미 적용된 조건들이 전부 엮여 있기 때문이다. INSERT 시점에 정확한 값이 들어가는 게 제일 중요하고, 그걸 보장하는 코드여야 한다.
특히 금융이나 멤버십 도메인은 숫자 하나가 틀리면 신뢰가 무너진다. 사용자 입장에서 "내 등급이 왜 이렇게 나오나요?"는 단순 불편이 아니라 서비스에 대한 불신으로 이어진다. "대충 맞는 것 같다"로 넘기면 반드시 다시 돌아오고, 그 비용이 지금 꼼꼼히 따지는 시간보다 항상 크다.
개발 습관 측면에서도 몇 가지를 유지하려 하는데, 커밋 메시지에 "무엇을"보다 "왜"를 담는 것이 그중 하나다. 이번처럼 내부 클래스 2개를 건드릴 때도 변경 이유가 다르면 분리해서 커밋하는 편이다. 나중에 git log를 봤을 때 맥락을 복원하는 시간이 확연히 달라진다. 작은 커밋을 자주 하는 게 귀찮아 보여도, 어느 변경에서 뭔가 깨졌는지 추적하는 상황이 오면 그 습관의 가치를 다시 실감하게 된다.
엣지 케이스를 꼼꼼히 따지는 것도 같은 맥락이다. 귀찮아 보이지만, 같은 버그로 다시 시간을 쓰는 것보다 지금 30분 더 쓰는 게 이득이다. 이번 버그가 그걸 다시 한번 확인시켜줬다.
댓글 0
첫 댓글 달아줘.