개발 slecs

수수료 조회 화면 내부 로직 안정성 개선

목차

수수료 조회 화면은 운영하다 보면 은근히 로직이 복잡해지는 영역이다. 처음엔 단순히 기간·지점·담당자 같은 조건을 받아 집계 결과 뿌리는 구조인데, 시간이 지나면서 예외 케이스가 하나씩 붙고, SQL이 조금씩 늘어나고, 내부 클래스도 살이 붙는다. 이번 작업은 그 쌓인 것들을 한 번 정리한 케이스였음.

내부 클래스 구조에서 로직이 흩어지는 문제

Spring MVC + MyBatis 조합에서 내부 클래스(inner class나 별도 커맨드 객체)는 요청 바인딩과 비즈니스 로직을 같이 들고 있는 경우가 많다. 처음엔 편하다. Controller에서 @ModelAttribute로 받아 그대로 Service로 넘기면 되니까. 근데 화면이 복잡해질수록 파라미터 수가 늘고, 바인딩 오류나 null 처리가 여기저기 흩어지기 시작한다.

이번에 건드린 부분도 비슷했다. 요청 파라미터 검증이 Controller 레이어에 절반, 내부 클래스 안에 절반 있었고, null 체크 로직이 중복되는 구조였음. 정리 방향은:

  • 요청 바인딩과 검증은 한 레이어에서만 처리
  • 내부 클래스는 화면 제어용 DTO 역할로만 제한
  • 비즈니스 로직은 Service로 위임

단순해 보이지만, "기존 흐름에 영향 없이" 가 조건이라 건드릴 수 있는 범위가 제한적이었다. 전면 리팩터링이 아니라 안정성 위주의 점진적 개선.

트랜잭션·예외 처리에서 실제로 선택한 것들

트랜잭션 범위는 어노테이션 설정으로 잡았는데, 조회 화면이더라도 이력 로그 같은 부수 쓰기가 껴 있으면 범위를 명확히 해야 한다. 이번 케이스도 실제로 그랬는지 확인하고, 읽기 전용 흐름에는 불필요한 트랜잭션이 걸리지 않도록 정리했음.

예외 처리는 항상 두 갈래에서 고민하게 된다:

방식 장점 단점
RuntimeException 그대로 전파 코드 단순, 상위에서 일괄 처리 가능 사용자에게 500 내려갈 위험
잡아서 응답 변환 화면에 명확한 메시지 제어 가능 모든 레이어에서 try-catch 늘어남

조회 화면에서 발생하는 예외는 대부분 잘못된 파라미터나 빈 결과 케이스다. RuntimeException을 그냥 올려보내는 게 아니라, 화면에서 의미 있는 메시지를 보여줄 수 있는 경우엔 잡아서 변환하는 걸 선택했음. 반대로 예상 외 DB 오류처럼 핸들링이 의미 없는 경우는 전파.

MyBatis SQL XML에서 null 처리도 같이 봤다. <if test="param != null and param != ''"> 같은 조건절이 여러 군데 중복돼 있는 경우 가독성도 떨어지고, 의도가 불분명해진다. 필요한 곳에만 쓰고, 공통 조건은 묶어서 정리.

<!-- 수정 전: 파편화된 null 체크 -->
<if test="startDate != null and startDate != ''">
  AND reg_dt >= #{startDate}
</if>
<if test="endDate != null and endDate != ''">
  AND reg_dt &lt;= #{endDate}
</if>

<!-- 수정 후: 의도를 명확히 -->
<if test="startDate != null and endDate != null">
  AND reg_dt BETWEEN #{startDate} AND #{endDate}
</if>

조건 자체는 크게 달라지지 않지만, 둘 다 있어야 의미가 있는 범위 조건임을 코드로 표현하는 게 낫다고 판단했다.

이런 작업에서 로그 전략이 의외로 중요하다

운영에서 실제로 오류 추적이 어려운 경우가 대부분 "어떤 파라미터가 들어왔는지 로그가 없어서"다. 조회 화면은 사용자가 잘못된 조건을 넣거나 빈 값을 그냥 조회하는 경우가 많다. 그때 서버 로그에 아무것도 안 남아 있으면 재현이 안 된다.

이번에 중요 입출력값을 INFO로 남겨두는 작업도 같이 했는데, 기준은 간단히 두 가지였음:

  • 요청 들어올 때: 주요 조건 파라미터 (날짜 범위, 조회 키 등)
  • 응답 나갈 때: 결과 건수 또는 오류 코드

디버그 레벨로 죄다 찍는 건 운영 환경에선 노이즈만 늘리고, 정작 필요한 맥락을 놓치게 만든다. INFO 로그는 적게, 의미 있게.

배포 순서는 로컬 기동 확인 → 개발 DB 반영 → 배포 순으로 했다. 조회 화면이라도 SQL 변경이 있으면 실제 데이터로 한 번 돌려보는 게 맞다. 테스트 데이터만으로는 못 잡는 케이스가 있다.

작업 규모 자체는 크지 않았는데, 이런 류의 정리 작업이 쌓여야 나중에 큰 변경할 때 발목을 안 잡힌다. 건드릴 때마다 "여기 왜 이렇게 돼 있지?"가 나오지 않는 코드베이스로 만드는 것-그게 결국 유지보수 비용을 낮추는 방향이다.

댓글 0

첫 댓글 달아줘.