여섯 도메인에 퍼진 사이트별 하드코딩 분기를 세 추상화로 일괄 제거
목차
여러 Controller 레이어에 박혀있던 sysId 하드코딩 분기를 걷어내고, SIGNUP_POLICY / FeatureChecker / SiteContentResolver 세 축으로 흡수시켰다.
왜 이 작업이 필요했나
사내 서비스는 복수의 사이트(시스템)를 단일 코드베이스로 운영하는 구조다. 문제는 그 "사이트별 분기"가 Controller 곳곳에 if (sysId.equals("XXX")) 형태로 흩어져 있었다는 점이다.
변경 파일 목록을 보면 system, cart, faq, member, mypage, notice — 도메인이 전혀 다른 여섯 개 패키지가 동시에 수정됐다. 같은 패턴의 하드코딩이 그만큼 광범위하게 퍼져 있었다는 뜻이다. 이런 상태가 계속되면 "새 사이트 추가" or "정책 변경" 시 파일을 얼마나 열어봐야 하는지 예측이 안 된다. 팀원 누군가 하나라도 빠뜨리면 사이트별 동작이 조용히 틀어진다.
리뷰할 때마다 "여기도 sysId 분기 있네요"라는 코멘트가 반복되고 있었고, 그게 리뷰 비용으로 계속 쌓이고 있었다. 그래서 이번에 한 번 정리하기로 했다.
세 개의 추상화로 흡수시킨 구조
하드코딩 분기를 세 역할로 나눠서 흡수했다.
| 추상화 | 담당 역할 | 흡수한 분기 유형 |
|---|---|---|
SIGNUP_POLICY |
회원가입 정책 (가입 허용 여부, 방식 등) | member 관련 sysId 조건 |
FeatureChecker |
기능 On/Off 여부 판단 | cart, faq, notice 등 기능 노출 조건 |
SiteContentResolver |
사이트별 콘텐츠/설정 해석 | system, mypage 등 콘텐츠 분기 |
Controller 입장에서는 더 이상 sysId가 뭔지 알 필요가 없다. 그냥 "이 기능 켜져 있어?" / "이 사이트 가입 정책이 뭐야?" 를 물어보면 된다.
Before 패턴은 대략 이런 식이었다.
// Controller 직접 분기 — 안티패턴
if ("SITE_A".equals(sysId)) {
model.addAttribute("joinEnabled", true);
} else if ("SITE_B".equals(sysId)) {
model.addAttribute("joinEnabled", false);
}
After 패턴은 이렇게 바뀐다.
// FeatureChecker / SIGNUP_POLICY 위임
model.addAttribute("joinEnabled", signupPolicy.isJoinEnabled(siteContext));
sysId 리터럴이 Controller에서 완전히 사라지고, 정책 판단은 각 추상화 안으로 들어간다. 나중에 사이트가 하나 더 추가되더라도 Controller는 건드리지 않아도 된다.
팀 관점에서 본 이 리팩터링의 의미
이런 작업을 할 때 팀장으로서 가장 신경 쓰는 건 "변경 범위가 넓은 리팩터링을 어떻게 안전하게 배포하느냐" 다.
여섯 개 도메인 Controller가 동시에 수정됐다는 건, 리뷰어 입장에서 꽤 부담스러운 PR이다. 이번엔 몇 가지를 의도적으로 챙겼다.
- 동작 변경 없음을 먼저 선언: PR 설명에 "동작 결과는 동일, 분기 위치만 이동"을 명시
- Before/After 코드 스니펫을 PR에 직접 삽입: 리뷰어가 diff만 보고 의도를 오해하지 않도록
- 도메인별로 커밋 분리 고려: 이번엔 한 커밋으로 묶었지만, 팀 규모가 더 컸다면 도메인별로 쪼개는 게 낫다
리팩터링은 기능 추가보다 리뷰가 더 어렵다. 기능 추가는 "이게 맞냐"를 보면 되는데, 리팩터링은 "이게 기존이랑 똑같냐"를 증명해야 하기 때문이다. 그 증명 책임은 PR 올린 사람이 지는 게 맞다.
회고
sysId 하드코딩이 이렇게까지 퍼진 데는 이유가 있다. 초기엔 사이트가 하나였고, 분기가 두어 개쯤 될 땐 그냥 if 문이 제일 빠르다. 문제는 그게 관성이 돼서 세 번째, 네 번째 분기가 생길 때도 같은 방식으로 복사됐다는 거다.
멘토링할 때 자주 하는 말인데 — "하드코딩이 나쁜 게 아니라, 하드코딩이 두 군데 이상 생기는 순간 그게 신호"다. 그 신호를 빨리 잡을수록 수습 비용이 적다. 이번엔 좀 늦게 잡았지만, 그래도 Controller 레이어 전체를 한 번에 정리했으니 다음 사이트 추가 때는 훨씬 편할 거다.
다음은 SiteContentResolver 내부에 아직 남아있는 조건들을 설정 기반으로 더 걷어내는 작업이 남아있다. 끝.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.