Pay 정책 생성 시 대상 시스템 유효성 검증 강화
목차
Pay 정책을 생성할 때 targetSysId가 실제로 유효한 시스템을 가리키는지 검증하는 로직이 빠져 있었다. 기능 자체는 돌아가고 있었지만, 잘못된 ID가 들어와도 그냥 통과되는 구조였다. 당장 문제가 터지진 않더라도, 나중에 잘못 생성된 정책 때문에 적용 대상이 엉키면 디버깅 비용이 크게 올라가는 전형적인 시한폭탄 패턴이다.
이번 작업은 그 틈새를 메우는 것이었고, 변경 범위는 크지 않았다. 백엔드 로직 파일 1개, 쿼리 XML 1개. 하지만 Pay 정책은 여러 시스템에 걸쳐 적용되는 도메인이라 영향 범위를 생각하면 단순 줄 수로 무게를 재면 안 된다.
targetSysId 검증이 왜 빠지기 쉬운가
외래 키(FK) 제약을 DB 레벨에서 걸어두면 이런 문제가 자연스럽게 잡힌다. 그런데 레거시 시스템이거나 여러 DB를 넘나드는 구조이거나, 아니면 "유효한 시스템 ID인지"를 판단하는 기준이 단순 테이블 조회가 아닐 때는 얘기가 달라진다. 검증 책임이 애플리케이션 레이어로 올라온다.
이런 상황에서 자주 보이는 패턴이 있다.
// 검증 없이 바로 저장하는 기존 흐름
public void createPayPolicy(PayPolicyDto dto) {
payPolicyMapper.insert(dto); // targetSysId가 뭐든 그냥 들어감
}
// 검증 추가 후
public void createPayPolicy(PayPolicyDto dto) {
validateTargetSysId(dto.getTargetSysId());
payPolicyMapper.insert(dto);
}
private void validateTargetSysId(String targetSysId) {
if (targetSysId == null || targetSysId.isBlank()) {
throw new IllegalArgumentException("targetSysId는 필수값입니다.");
}
boolean exists = sysMapper.existsById(targetSysId);
if (!exists) {
throw new BusinessException("존재하지 않는 대상 시스템입니다: " + targetSysId);
}
}
분리하지 않고 createPayPolicy 안에 인라인으로 박아두면 나중에 테스트하기 까다롭다. 검증 로직을 메서드로 빼두는 게 단순해 보이지만, 단위 테스트에서 검증만 따로 찌르거나 다른 흐름에서 재사용할 때 훨씬 편하다.
쿼리 XML 쪽은 targetSysId로 시스템 존재 여부를 확인하는 조회가 새로 들어갔다. 이미 비슷한 패턴의 조회가 다른 곳에 있었는데, 그걸 복사-붙여넣기하지 않고 공통 쿼리로 정리했다. 중복 SQL이 여러 XML에 흩어지면 조건 하나 바꿀 때 다 찾아다녀야 한다.
에러 메시지를 소홀히 하지 않는 이유
검증 로직을 추가할 때 에러 메시지도 같이 손봤다. 어떤 ID가, 왜 유효하지 않은지가 메시지에 담겨야 한다. "유효하지 않은 요청입니다"처럼 범용 메시지만 남기면 로그를 봐도 재현이 안 된다.
// 나쁜 예
throw new BusinessException("잘못된 요청입니다.");
// 나은 예
throw new BusinessException("대상 시스템을 찾을 수 없습니다. targetSysId=" + targetSysId);
새벽에 알림 받고 들어왔을 때 로그 한 줄로 원인이 특정되는지 여부가 장애 대응 시간을 가른다. 개발할 때 "내가 이 로그 보면 뭘 알 수 있지?"를 한 번 더 생각하는 습관이 쌓이면 운영이 조용해진다.
이번 작업에서 재확인한 것들
| 원칙 | 이번 작업에서 확인한 지점 |
|---|---|
| 실패 우선 처리 | 검증은 로직 진입 직후에, happy path는 그 뒤에 |
| 단일 책임 | 검증 메서드 분리 → 테스트와 재사용 둘 다 편해짐 |
| 명시적 에러 메시지 | ID 값을 메시지에 포함 → 로그만 봐도 원인 파악 가능 |
| 중복 제거 | 공통 쿼리화 → 유지보수 포인트 단일화 |
변경 범위가 작을수록 오히려 더 꼼꼼히 봐야 한다고 생각한다. 규모가 작으니까 리뷰도 대충 넘어가기 쉽고, 그게 나중에 조용히 문제를 키운다. 이번엔 변경 전후 동작을 직접 확인하고 배포했고, 당분간 로그 모니터링도 챙길 생각이다.
당장 눈에 띄는 기능 추가는 아니지만, 이런 작업들이 쌓여야 나중에 큰 기능 올릴 때 토대가 흔들리지 않는다. 6개월 후에 이 코드를 처음 보는 사람이 "이 시스템 ID 어디서 검증하지?"를 찾아다니지 않아도 되는 것, 그게 오늘 작업의 실질적인 가치다.
댓글 0
첫 댓글 달아줘.