155 lines
5.7 KiB
Markdown
155 lines
5.7 KiB
Markdown
# duplication 기준
|
|
|
|
## 목적
|
|
|
|
중복은 무조건 제거하지 않는다.
|
|
반복 수정 비용이 큰 **진짜 중복**은 제거하고, 서로 다른 이유로 바뀔 가능성이 있는 **우연히 비슷한 코드**는 성급히 합치지 않는다.
|
|
|
|
## 공식/실무 기준 요약
|
|
|
|
- 중복 코드는 수정/리팩터링 시 같은 변경을 여러 곳에 반복하게 만들고, 누락 위험을 높인다.
|
|
- 정적 분석 도구는 일정 크기 이상의 구조적 중복을 duplication으로 탐지한다.
|
|
- 하지만 DRY를 너무 빨리 적용하면 premature abstraction이 생겨 이후 변경을 더 어렵게 만들 수 있다.
|
|
- 특히 테스트 코드는 production code보다 DRY보다 가독성을 더 우선할 수 있다.
|
|
|
|
## 기본 규칙
|
|
|
|
### 1. 기본 원칙: “같아 보인다”와 “같은 이유로 바뀐다”를 구분
|
|
다음 둘을 구분한다.
|
|
|
|
- **진짜 중복**
|
|
- 같은 의미
|
|
- 같은 정책
|
|
- 같은 이유로 바뀜
|
|
- 한 군데 고치면 나머지도 같이 바뀌어야 함
|
|
|
|
- **우연한 유사성**
|
|
- 지금은 비슷해 보여도
|
|
- 맥락/책임/변화 이유가 다름
|
|
- 나중에 독립적으로 진화할 가능성이 큼
|
|
|
|
기본적으로 **같은 이유로 바뀌는 경우만 추출**한다.
|
|
|
|
### 2. Rule of Three를 기본값으로 사용
|
|
추상화는 보통 아래 순서를 따른다.
|
|
|
|
- 1회: 그냥 작성
|
|
- 2회: 비슷한 패턴을 인식하되 아직 참음
|
|
- 3회 이상: 변화 이유가 같다면 추출을 적극 검토
|
|
|
|
단, 보안/예외 번역/외부 API 호출처럼 실수 비용이 큰 중복은 2회부터도 추출 가능하다.
|
|
|
|
### 3. 반복 수정 비용이 크면 추출
|
|
다음 조건이 크면 중복 제거를 우선 검토한다.
|
|
|
|
- 같은 정책 변경을 여러 파일에 반복해야 함
|
|
- 누락 시 장애/보안/데이터 불일치 위험이 큼
|
|
- 테스트도 같이 여러 군데 바뀌어야 함
|
|
- 팀원이 쉽게 한쪽만 수정할 수 있음
|
|
|
|
### 4. 추상화 비용이 더 크면 중복 허용
|
|
다음 경우는 중복을 허용한다.
|
|
|
|
- 서로 다른 레이어 책임을 억지로 합쳐야 하는 경우
|
|
- 공통화하면 이름이 모호해지는 경우
|
|
- 분기 옵션이 계속 늘어나 helper가 더 복잡해지는 경우
|
|
- 미래 변화 방향이 아직 불확실한 경우
|
|
- 테스트 가독성이 helper 때문에 더 나빠지는 경우
|
|
|
|
### 5. 레이어를 넘는 중복 제거는 특히 신중
|
|
중복 제거를 위해 레이어 경계를 깨지 않는다.
|
|
|
|
금지 예:
|
|
- presentation과 infrastructure의 비슷한 코드라는 이유로 공통 유틸로 합치기
|
|
- domain 규칙과 controller 검증을 한 helper로 합치기
|
|
- 외부 API 포맷과 내부 도메인 규칙을 같은 mapper로 합치기
|
|
|
|
### 6. 복붙보다 작은 추출부터
|
|
중복 제거는 아래 순서로 작게 시작한다.
|
|
|
|
1. local variable 추출
|
|
2. private method 추출
|
|
3. mapper/helper 추출
|
|
4. 같은 클래스 계층이면 pull up / template method 검토
|
|
5. 그래도 명확할 때만 더 큰 추상화
|
|
|
|
처음부터 범용 util/service/common으로 키우지 않는다.
|
|
|
|
### 7. helper는 “짧아진 코드”보다 “명확해진 이름”이 있을 때만
|
|
다음 중 하나가 아니면 helper 추출을 보류한다.
|
|
|
|
- 반복되는 의미를 정확히 설명하는 이름이 있음
|
|
- 공통 정책/계약을 하나로 모아야 함
|
|
- 테스트/검증/예외 처리 중복을 안정적으로 줄임
|
|
|
|
“줄 수 있으니까 줄인다”는 이유만으로 추출하지 않는다.
|
|
|
|
### 8. 테스트는 DRY보다 가독성 우선 가능
|
|
테스트는 production code와 기준이 다를 수 있다.
|
|
|
|
기본:
|
|
- 테스트는 사람이 바로 읽어 이해할 수 있어야 함
|
|
- 과한 helper, loop, setup 공유로 의미가 숨겨지면 중복을 허용
|
|
- DAMP를 우선하고, 진짜 반복 보일러플레이트만 줄인다
|
|
|
|
### 9. 생성/변환/정책 중복은 추출 우선
|
|
다음은 실수 비용이 높아 추출을 우선 검토한다.
|
|
|
|
- 에러 응답 조립
|
|
- 외부 API request/response 변환
|
|
- 시간 생성/포맷 정책
|
|
- 권한/역할 판별 정책
|
|
- 공통 validation 규칙
|
|
- persistence <-> domain 매핑 규칙
|
|
- exception translation 규칙
|
|
|
|
### 10. 우연한 한두 줄 중복은 허용
|
|
다음은 무리해서 추출하지 않는다.
|
|
|
|
- 간단한 guard clause
|
|
- 명확한 builder/setter 호출 몇 줄
|
|
- 테스트의 준비/검증 코드
|
|
- 각 레이어에서 맥락상 당연한 짧은 변환 코드
|
|
|
|
### 11. 중복 제거는 behavior-preserving으로 작게
|
|
중복 제거는 리팩터링이다.
|
|
행동 보존을 전제로 작은 단계로 진행한다.
|
|
|
|
기본:
|
|
- 테스트가 있으면 먼저 보호
|
|
- 한 번에 큰 범용 추상화로 가지 않음
|
|
- 단계적으로 추출 후 검증
|
|
|
|
### 12. common/util 모듈은 중복 제거 수단으로 남용 금지
|
|
중복을 본다고 바로 `common`, `util`, `helper`로 보내지 않는다.
|
|
|
|
먼저 묻는다:
|
|
- 이 중복의 소유 레이어는 어디인가?
|
|
- 정말 여러 모듈이 같은 이유로 바뀌는가?
|
|
- 경계를 안 깨고도 추출 가능한가?
|
|
|
|
### 13. 도구 경고는 “신호”이지 자동 수정 명령은 아님
|
|
Sonar/PMD가 duplication을 잡았다고 무조건 추출하지 않는다.
|
|
다음 둘을 함께 본다.
|
|
|
|
- 구조적 중복 크기
|
|
- 변화 이유의 동일성
|
|
|
|
### 14. 문서화 기준
|
|
중복을 의도적으로 남겼다면 이유를 짧게 남길 수 있다.
|
|
특히 아래 경우:
|
|
- 테스트 가독성
|
|
- 레이어 분리 유지
|
|
- 조기 추상화 방지
|
|
- 외부 계약의 독립 진화 가능성
|
|
|
|
## 프로젝트 기준 요약
|
|
|
|
- 진짜 중복만 제거
|
|
- 같은 이유로 바뀌는 경우만 추출
|
|
- Rule of Three 기본
|
|
- 레이어 경계를 깨는 공통화 금지
|
|
- 작은 추출부터 시작
|
|
- 테스트는 DAMP 우선 가능
|
|
- 도구 경고는 신호일 뿐 자동 추출 근거가 아님
|