은행 코드 정비
목차
은행 코드 관련 정비와 배너 대상 테이블 삭제를 한 스프린트에 묶어서 처리했다. 두 작업 모두 기능 추가가 아닌 내부 정리 성격이라 커밋 로그에 feat이 붙긴 하지만 사용자 눈에는 아무것도 안 바뀐다. 그렇다고 안 하면 안 되는 작업들. 이런 게 쌓여야 나중에 뭔가 진짜 기능을 붙일 때 고생을 덜한다.
변경 범위가 백엔드 로직, JSP 화면, MyBatis XML 쿼리, CSS 스타일까지 여러 레이어에 걸쳐있었다. 레이어가 많을수록 작업 전에 영향 범위를 따지는 게 중요한데, 이걸 대충 넘기면 며칠 뒤에 "이상하게 이쪽에서 뭔가 깨진 것 같아요"라는 리포트를 받게 된다. 그래서 변경 전 현행 코드를 먼저 파악하고, 레이어별로 커밋 단위를 분리해서 진행했음.
은행 코드 정비, 어떻게 했나
은행 코드처럼 도메인 전반에서 참조되는 값은 처리 로직이 여러 곳에 산재하기 쉽다. 서비스 A에서 한 번 trim + uppercase 처리하고, 서비스 B에서 또 같은 처리를 하고, 쿼리 바인딩 직전에 또 한 번 방어 코드 넣고. 이게 쌓이면 나중에 은행 코드 유효성 조건이 바뀔 때 어디를 고쳐야 하는지조차 파악이 안 된다. 하나 고쳤는데 다른 경로에서 여전히 구 로직을 타고 있는 케이스가 생기는 것도 이런 이유다.
이번 작업의 핵심은 이 처리 로직을 하나의 진입점으로 모으는 것이었다.
// 개선 전 - 서비스 레이어 곳곳에서 반복
String bankCode = input.trim().toUpperCase();
if (bankCode == null || bankCode.length() == 0) {
log.warn("은행 코드 없음");
return;
}
// 개선 후 - 도메인 객체가 검증과 정규화를 한 번에 담당
BankCode code = BankCode.from(input);
// 내부에서 null 체크, trim, 대문자 변환, 유효 코드 여부 확인
이렇게 하면 은행 코드와 관련된 규칙이 바뀌어도 BankCode 클래스 한 곳만 수정하면 된다. 에러 메시지도 일관되고, 어느 레이어에서 어떤 형태로 값이 들어오든 동일한 검증을 타게 된다. 테스트 작성도 훨씬 수월해짐. 기존에는 서비스마다 비슷한 검증 케이스를 각각 짜야 했는데, 도메인 객체 단위로 한 번만 커버하면 되니까.
JSP 쪽은 중복된 include와 EL 표현식 정리가 주였다. 예전에 급하게 복붙한 흔적들이 남아있었는데, 화면에서 은행 코드를 보여주는 방식이 뷰마다 미묘하게 달랐음. 한쪽은 공백 포함해서 노출하고, 다른 쪽은 trim해서 노출하고. 사용자 눈에는 거의 안 보이는 차이지만, 자동화 스크립트나 테스트 픽스처에서 조용히 문제를 일으키는 류다. 이런 건 발견하면 바로 정리해두는 게 맞다.
CSS도 은행 코드 관련 컴포넌트에서 쓰지 않는 셀렉터가 몇 개 남아있어서 같이 걷어냈다. 스타일 정리는 기능 영향이 없어서 간과하기 쉬운데, 나중에 같은 클래스명을 새로 붙이려다 충돌 나는 경우를 몇 번 겪고 나서는 안 쓰는 건 바로바로 지우는 편임.
배너 대상 테이블 삭제
테이블 삭제는 코드 정리보다 훨씬 조심스럽게 다뤄야 한다. 코드는 git으로 되돌리기 쉽지만, 테이블을 DROP하고 나서 데이터가 날아가면 복구가 전혀 다른 문제가 된다.
배너 대상 테이블이 더 이상 활성 참조 없이 사실상 죽어있다는 걸 알고 있었지만, 그걸 확인하는 절차를 생략하면 안 된다. 확인 순서는 대략 이랬다:
- XML 쿼리 파일 전수 검색으로 해당 테이블 참조하는 쿼리 전부 추출
- 배치 잡, 스케줄러 코드에서 테이블명 직접 참조하는 부분 체크
- 혹시 뷰(VIEW)나 트리거로 연결된 DB 오브젝트 있는지 메타 테이블 조회
- 레거시 코드에서 쿼리 문자열을 하드코딩해서 쓰는 경우 grep으로 추가 확인
마지막 항목이 생각보다 자주 걸린다. ORM이나 XML로 쿼리 관리하는 게 기본이지만, 오래된 코드엔 자바 문자열로 SQL을 직접 이어 붙이는 부분이 남아있는 경우가 있어서 IDE 검색만으로는 안 잡힘.
| 단계 | 내용 | 확인 방법 |
|---|---|---|
| 참조 탐색 | XML, 배치, 뷰, 하드코딩 문자열 | grep + IDE 전체 검색 |
| 코드 제거 후 배포 | 쿼리 및 서비스 참조 제거 | 배포 후 에러 로그 모니터링 |
| 운영 안정 확인 | 실제 운영에서 정상 동작 검증 | 수일 이상 관찰 |
| 테이블 삭제 | DROP 전 스냅샷 백업 여부 확인 | DBA 협의 후 진행 |
다 확인하고 나서도 참조를 걷어낸 상태로 먼저 배포하고 바로 테이블을 지우지 않았다. 그 상태로 운영이 정상적으로 돌아가는 걸 며칠 확인한 다음에 테이블을 삭제했음. 이 대기 구간이 귀찮게 느껴질 수 있는데, 스킵하면 안 된다. "코드에서 참조 없다 = 실제로 안 쓴다"가 항상 같은 말은 아니라서.
XML 쿼리도 이참에 같이 정리했다. 더 이상 안 쓰는 쿼리 블록을 주석 처리로 남겨두는 관행이 있는데, 그러면 나중에 코드 읽는 사람이 "이거 살아있는 코드야, 죽은 코드야?" 하는 의문을 갖게 된다. git이 있으니까 삭제하는 게 맞다. 복구가 필요하면 git log 뒤지면 된다.
이런 작업에서 느끼는 것들
코드를 작성하거나 고칠 때 세 가지 관점을 항상 같이 돌린다.
- 6개월 후의 내가 처음 보는 것처럼: 지금 짜는 로직이 그때도 이해될까. 내가 짠 코드인데 3개월 뒤에 읽으면 모르겠는 경우가 생각보다 많다.
- 온보딩 중인 팀원이 봤을 때: 맥락 설명 없이도 의도가 읽히는가. 읽으려면 주변 코드를 다 파악해야 하는 코드는 좋은 코드가 아님.
- 새벽 장애 상황: 에러 로그 한 줄이 "어느 로직에서, 왜, 어떤 값 때문에" 터졌는지 알려주는가.
NullPointerException at line 247수준이면 장애 대응 시간이 배로 늘어난다.
세 번째가 실전에서 가장 체감이 크다. 에러 메시지 하나에 컨텍스트가 얼마나 담겨있느냐가, 그날 몇 시에 자느냐를 결정하는 경우가 있다.
| 좋은 코드 지표 | 나쁜 코드 신호 |
|---|---|
| 읽으면 의도가 바로 보임 | 주석 없으면 해석 불가 |
| 변경이 한 곳에만 영향 | 한 곳 바꾸면 여러 곳 연쇄 수정 |
| 에러 메시지가 원인을 설명 | 로그 뒤져도 원인 불명 |
| 테스트 작성이 자연스러움 | 테스트하려면 구조부터 뜯어야 |
결국 코드 정비는 미래의 디버깅 시간을 당겨서 지금 쓰는 작업이다. 당장은 비용처럼 보이지만, 반복적으로 해두면 팀 전체의 작업 흐름이 조금씩 가벼워진다. 기능 추가 속도보다 "기존 코드 파악하는 데 걸리는 시간"이 팀 생산성에 훨씬 더 큰 영향을 준다는 걸, 팀 규모가 커질수록 실감한다.
댓글 0
첫 댓글 달아줘.