개발 slecs

DDL 스키마·위젯 에디터 기술 부채 정리로 유지보수성 개선

목차

기술 부채는 상환 시점을 미룰수록 이자가 쌓인다. 이 표현이 진부하게 느껴질 수도 있는데, 막상 오래된 코드를 들여다볼 때마다 새삼 실감한다. 이번 작업은 DDL 스키마 네이밍 정리와 위젯 에디터 쪽 구조 조정이 주를 이뤘다. 기능 변경은 없고 내부 구조만 손댄 작업이지만, 그래서 오히려 미루기 쉬운 종류다.

왜 지금이었나

기능 개발을 하다 보면 '이 부분은 나중에 정리하자'는 메모를 백로그나 코드 주석에 남기게 된다. 문제는 그 '나중'이 계속 밀린다는 거다. DDL 스키마의 경우 초기에는 대충 지어도 큰 문제가 없다. 테이블 몇 개, 컬럼 몇 개 수준이라면 만든 사람이 맥락을 기억하니까. 그런데 프로젝트가 커지면서 스키마 수가 늘고, 명명 규칙 없이 자라난 이름들이 혼재하기 시작하면 이야기가 완전히 달라진다.

같은 개념을 가리키는 테이블 이름이 컨텍스트마다 다르면 쿼리 작성할 때마다 "어느 테이블에서 가져오는 거지?"를 반복하게 된다. JOIN이 늘어날수록 이 비용이 선형이 아니라 기하급수적으로 커진다. 스키마가 의도를 드러내지 않으면 코드 리뷰할 때도 쿼리를 보면서 테이블 정의를 따로 열어둬야 하는 상황이 생긴다. 익숙한 사람은 그냥 외워서 쓰게 되고, 새로운 사람이 들어오면 그 학습 비용이 고스란히 전달된다.

위젯 에디터 쪽도 비슷한 문제가 있었다. 프론트엔드 스크립트에 유사한 로직이 여러 파일에 흩어져 있으면, 버그 하나 고칠 때 전부 찾아서 같이 수정해야 한다. 한 군데 빠뜨리면 그게 또 다른 버그가 된다. 뭔가 이상해서 고쳤더니 다른 데서 같은 증상이 나오는 패턴, 한 번쯤 다들 겪어봤을 거다.

이번에 기능 추가 작업을 진행하다가 이 부분들을 다시 마주쳤고, 더 미루지 않고 먼저 정리하고 들어가기로 했다. 기술 부채를 안고 기능을 얹으면 나중에 정리할 때 범위가 더 커진다.

이번에 건드린 것들

크게 세 축으로 나눌 수 있다.

DDL 스키마 네이밍 통일. 일관성 없이 지어진 테이블·컬럼 이름을 규칙 아래 정비했다. 이 작업이 겉보기엔 단순해 보이지만 실제로는 꽤 조심스럽다. 이름 변경은 해당 스키마를 참조하는 모든 지점을 함께 바꿔야 하고, 마이그레이션 스크립트 하나 잘못 작성하면 데이터 손실이나 서비스 중단으로 이어진다. 그래서 변경 전에 참조 지점을 전부 추적하고, 단계적으로 적용하면서 각 단계마다 검증하는 게 기본이다.

일반적으로 DDL 네이밍에서 혼선이 자주 생기는 지점은 단수/복수 혼용, 축약어와 전체 단어 혼용, 조인 테이블 명명 방식 불일치 같은 것들이다. 어느 쪽이 맞다기보다 팀 내에서 한 방향으로 통일하는 게 중요하다.

위젯 에디터 스크립트 정리. 프론트엔드 쪽에서 중복으로 흩어져 있던 로직을 공통 위치로 통합했고, 더 이상 사용하지 않는 dead code도 같이 제거했다. dead code는 코드베이스에 노이즈를 만든다. "이게 왜 있지?"라는 의문이 생기고, 그 의문 해소에 시간이 들고, 때로는 건드리면 뭔가 터질 것 같아서 아무도 손 못 대는 코드로 남아있게 된다. 삭제가 가장 깔끔한 해결책인 경우가 많다.

테스트 역할 분리. 하나의 테스트 파일이 너무 많은 책임을 담당하고 있었다. 단위 수준 검증과 통합에 가까운 검증이 섞여 있으면, 테스트가 깨졌을 때 원인 파악이 느려진다. 분리해두면 실패 범위가 좁혀지고, 어디서 무엇을 검증하는지 의도가 명확해진다.

수정 파일은 총 5개.

변경 유형 내용
중복 제거 프론트엔드 스크립트 공통 로직 통합
이름 변경 DDL 스키마 테이블·컬럼 네이밍 통일
파일 삭제 미사용 dead code 제거
테스트 분리 역할 불명확한 테스트 재구성

리팩토링할 때 지키는 것들

한 번에 너무 많이 바꾸지 않는다는 게 제일 중요하다. 욕심 부려서 이것저것 같이 건드리면 뭔가 깨졌을 때 원인 추적이 힘들어진다. 이번에도 논리적으로 연관된 것끼리 묶어서 커밋 단위를 나눴다.

기능 변경과 구조 변경을 한 커밋에 섞는 것도 피해야 한다. 리뷰어 입장에서 "이 커밋은 동작 변경 없는 리팩토링"이라는 전제를 깔고 볼 수 있으면, 리뷰 집중도가 다르다. 나중에 git log로 히스토리 추적할 때도 목적이 명확하게 드러난다.

// 피해야 하는 패턴 - 리팩토링 + 기능 추가 혼재
// feat: widget 에디터 신규 옵션 추가 + 스키마 네이밍 정리 + dead code 제거

// 이렇게 관심사를 분리해서 커밋
// refactor: DDL 스키마 테이블명 명명 규칙 통일
// refactor: widget editor 공통 로직 추출 및 중복 제거
// refactor: 미사용 dead code 제거
// test: widget 관련 단위/통합 테스트 역할 분리

리팩토링 전후로 기존 기능이 동일하게 동작하는지 검증하는 과정도 빠뜨리면 안 된다. 구조만 바꿨다고 해서 동작이 자동으로 보장되지 않는다. 특히 DDL 이름 변경처럼 영향 범위가 넓은 작업은 참조 지점 하나를 빠뜨리면 런타임에서 터진다. 이번 작업도 변경 후 기존 기능 동작을 전부 확인하고 나서 마무리했다.

트레이드오프도 있다. 리팩토링에 투자하는 시간은 당장 기능을 추가하거나 버그를 잡는 데 쓰이지 않는다. 팀 상황에 따라 이 판단이 달라질 수 있는데, 개인적으로는 다음 작업이 버거워질 것 같은 타이밍에 들어가는 편이다. 건드려야 할 코드가 지저분하면 그 위에 무언가를 얹는 것 자체가 느려지기 때문이다.

정리하고 나서

정리 직후 진행한 다음 기능 작업에서 효과가 바로 나왔다. 관련 로직 찾을 때 여러 파일을 뒤지는 대신 명확한 한 곳으로 찾아가면 됐고, DDL 스키마 이름도 의도가 바로 읽히니까 쿼리 작성할 때 왔다 갔다 하는 횟수가 줄었다.

효과 변화
코드 탐색 시간 줄었음
수정 포인트 명확성 향상됨
버그 발생 가능성 낮아짐

기술 부채 정리의 ROI는 이런 식이다. 직접적인 버그 수정보다 눈에 잘 안 띄고 정량화도 어렵지만, 쌓이면 개발 속도에 실질적인 영향을 준다. 코드 탐색 시간이 조금씩 줄고, 수정 범위를 파악하는 시간이 줄고, 틀릴 가능성이 낮아지는 것들이 누적된다.

코드는 처음부터 완벽할 수 없다. 요구사항도 계속 변하고, 코드를 작성했을 당시의 이해도와 지금의 이해도도 다르다. 주기적으로 다듬는 게 이상한 게 아니라 자연스러운 과정이다. '완성된 코드'란 없고, 현재 시점에서 가장 잘 정리된 코드가 있을 뿐이다.

댓글 0

첫 댓글 달아줘.