웰컴페이 회원 탈퇴 시 관리자 식별자 조회 로직 보완
목차
웰컴페이 회원 탈퇴 처리를 다루다 보면 외부 결제 시스템과의 연동 특성상 생각보다 챙겨야 할 케이스가 많다. 이번엔 탈퇴 플로우에서 mgtId(관리자 식별자) 조회가 누락된 부분을 보강했다.
기존 코드를 보면 탈퇴 요청이 들어왔을 때 내부 회원 상태를 처리하는 로직은 있었는데, 웰컴페이 쪽으로 탈퇴 처리를 연동할 때 필요한 mgtId를 적절한 시점에 조회하지 않고 있었다. 정상 케이스에서는 세션이나 상위 컨텍스트에 mgtId가 이미 들어 있는 경우가 많아서 문제없이 동작했을 거고, 그래서 오랫동안 눈에 안 띄었을 것 같다. 특정 진입 경로에서 mgtId가 없는 상태로 탈퇴 로직이 실행되면 처리가 제대로 안 될 여지가 있는 구조였음.
웰컴페이처럼 외부 결제사 연동에서 mgtId 같은 관리자 식별자가 쓰이는 이유를 짚어두면, 이 값은 일반 회원 ID와 별도로 관리되는 식별자로 외부 시스템에서 사업자 단위 트랜잭션을 묶거나 처리 권한을 확인하는 데 사용된다. 회원 가입이나 연동 설정 시점에 발급되고, 이후 탈퇴·해지 같은 생애주기 이벤트에서도 동일한 식별자로 요청해야 외부 시스템이 매칭을 제대로 한다. 탈퇴 처리 시 이 값이 없거나 잘못 넘어가면 외부 시스템에서 처리가 안 되거나 엉뚱한 대상을 조작하는 문제로 이어질 수 있다.
mgtId 조회 로직이 왜 빠지기 쉬운가
외부 결제사 연동 코드에서 이런 패턴이 반복된다. 처음 구현할 때는 "이 값은 위에서 내려오니까 여기서 다시 조회 안 해도 되겠지"라는 판단이 개발자 입장에서 자연스럽다. 실제로 대부분의 케이스에서 그 판단이 맞다. 문제는 플로우가 여러 경로로 늘어나면서, 어떤 진입점에서는 그 전제가 성립 안 하는 상황이 생긴다는 거다.
탈퇴 같은 단발성 플로우는 특히 그렇다. 가입이나 결제 플로우에 비해 테스트 빈도가 낮고, 구현 후에 한동안 건드릴 일이 없으니 코드가 오래 눈 밖에 머문다. "이 값이 항상 있다"는 암묵적 전제가 문서화도 안 된 채 남아 있다가 나중에 발견되는 경우가 많음.
이번에 보완한 방향은 단순했다. 탈퇴 처리 메서드 내부에서 필요한 시점에 mgtId를 명시적으로 조회하도록 했다. 상위에서 내려오면 쓰고 없으면 조회하는 식의 방어 코드보다, 해당 레이어에서 명시적으로 조회하는 편이 로직을 따라가기가 훨씬 쉽다.
// 수정 전 - mgtId를 파라미터로만 의존
public void withdrawMember(String memberId, String mgtId) {
// mgtId가 null이면 이후 연동 처리 실패, 원인 파악 어려움
welcomePayService.withdraw(memberId, mgtId);
}
// 수정 후 - 내부에서 명시적 조회
public void withdrawMember(String memberId) {
String mgtId = welcomePayRepository.findMgtIdByMemberId(memberId);
if (mgtId == null) {
log.error("mgtId 조회 실패 - memberId: {}", memberId);
throw new WelcomePayException("탈퇴 처리를 위한 관리자 식별자 조회에 실패했습니다.");
}
welcomePayService.withdraw(memberId, mgtId);
}
코드 자체는 단순해 보이지만 실제로는 mgtId가 null인 경우의 처리, 조회 실패 시 예외 핸들링, 로그 보강까지 같이 챙겼다. 기존에 발생했을 때 "NullPointerException" 류의 제네릭 예외로 끝나던 걸, 맥락을 포함한 명시적 메시지로 바꿨고, 로그에 memberId를 같이 남겨두니 나중에 조회도 훨씬 편해졌다. 중복 코드 제거와 공통화도 이 과정에서 함께 진행했다.
변경 범위와 영향
| 레이어 | 파일 수 | 주요 변경 내용 |
|---|---|---|
| 백엔드 로직 | 1개 | mgtId 조회 추가, 에러 처리 보강, 로그 개선 |
| 화면(JSP) | 0개 | 변경 없음 |
| 쿼리(XML) | 0개 | 변경 없음 |
백엔드 1개 파일만 변경했다. DB 쿼리나 화면 변경 없이 로직 레이어 단독 수정으로 끝난 건, 기존에 mgtId를 조회하는 레포지토리 메서드가 이미 있었고 그걸 적절히 호출하는 연결 부분이 빠져 있었기 때문이다. 인프라는 갖춰져 있는데 연결이 안 된 케이스.
변경 파일 수가 적다는 게 작업이 단순하다는 뜻은 아니다. 오히려 영향 범위를 파악하는 데 시간이 더 걸렸다. 탈퇴 플로우가 어떤 진입 경로를 갖는지, 현재 mgtId가 각 케이스에서 어떻게 전달되고 있는지 추적하는 게 핵심이었음. 기존 호출부를 하나씩 확인하면서 어느 경로에서는 mgtId가 있고 어느 경로에서는 없는지 파악해야 했다. 기존 동작을 깨지 않는 것도 신경 써야 했는데, mgtId를 파라미터로 받던 시그니처를 바꾸면 호출부 여러 곳에 연쇄 수정이 생기니까 영향 최소화 방향으로 접근했다.
작업 회고
이런 종류의 보완 작업이 쌓이면서 시스템이 점점 견고해진다는 걸 다시 느꼈다. 당장 티가 안 나는 작업이고, 배포해도 "기능 추가됐어요"라고 말할 게 없다. 근데 나중에 탈퇴 관련 문제가 생겼을 때 원인 추적하는 시간을 크게 줄여준다.
코드를 볼 때 항상 신경 쓰는 질문 세 가지가 있다.
- 6개월 후에 이 코드를 다시 봤을 때 내가 이해할 수 있는가
- 다른 개발자가 처음 보면 어떻게 읽힐까
- 새벽에 장애가 났을 때 이 로그가 문제를 빨리 찾게 해주는가
이번 수정은 세 번째에서 걸렸다. mgtId가 없어서 처리가 안 됐을 때 기존 코드에서는 어디서 뭐가 잘못됐는지 로그만 보고 판단하기가 애매한 구조였다. 명시적으로 조회하고 실패 시 명확한 예외와 로그를 남기도록 바꾸면서 디버깅 편의성도 같이 올라갔음.
| 기준 | 수정 전 | 수정 후 |
|---|---|---|
| mgtId 출처 | 호출부 의존, 암묵적 전제 | 내부 명시적 조회 |
| null 처리 | 미흡, 원인 파악 어려움 | 예외 + 명확한 로그 |
| 진입 경로 독립성 | 일부 경로에서 불안정 | 경로 무관하게 동일 동작 |
| 에러 메시지 | 제네릭 예외 | 맥락 포함한 명시적 메시지 |
작은 커밋, 변경 단위를 좁게 유지하는 건 코드 리뷰를 쉽게 해주기도 하지만 롤백이 필요할 때 범위가 명확하다는 장점도 있다. 운영 중인 서비스의 기존 플로우를 건드릴 때는 특히 그 원칙이 나중에 후회를 줄여준다.
엣지 케이스 처리, 로그 개선, 불필요한 코드 정리 같은 작업은 기능 개발 사이에 꼭 끼워 넣어야 하는 것들이다. 안 해도 당장 터지는 건 아닌데, 안 하면 결국 언젠가 터진다. 그때 가서 수습하는 비용이 훨씬 크다는 걸 반복해서 확인하게 된다.
댓글 0
첫 댓글 달아줘.