리팩터링 — 코드 구조 개선의 카탈로그¶
정의¶
Martin Fowler가 정리한 이름 있는 작은 단계의 카탈로그. "외부 동작은 그대로 두고 내부 구조를 개선"하는 모든 변경을 24가지 악취(code smell) ↔ 66+ 리팩터링 기법 으로 매핑해 정리한 책.
본질: 즉흥적 "코드 수정"과 리팩터링의 차이는 이름·메커니즘·단계 분할. 이름 없는 변경은 위험하다.
책 메타데이터¶
| 항목 | 값 |
|---|---|
| 저자 | Martin Fowler (ThoughtWorks Chief Scientist) |
| 원서 2판 | Refactoring: Improving the Design of Existing Code, 2nd Edition (Addison-Wesley, 2018) |
| 1판 → 2판 차이 | 1999 Java 기반 → 2018 JavaScript 기반. 언어와 무관한 원칙은 그대로, 일부 기법(클래스 중심) 비중 조정 |
| 한국어판(추정) | 리팩터링 2판 (한빛미디어, 2020, 개앞맵시 옮김) |
| 분량 | 12장 + 부록 A·B, 약 580페이지 |
| 저자 공식 | https://refactoring.com/ |
| 같은 시리즈 | Patterns of Enterprise Application Architecture(2002), Domain-Specific Languages(2010) |
핵심 메시지 (목차에서 그대로 도출)¶
| 메시지 | 출처 절 | 풀이 |
|---|---|---|
| 두 개의 모자 | 2.2 | 새 기능 추가 ≠ 리팩터링. 한 번에 한 모자만. 동시에 하면 둘 다 깨진다 |
| 악취가 신호 | 3장 | 24가지 code smell이 리팩터링의 트리거. "더럽다" 가 아니라 "이 리팩터링이 필요하다"의 신호 |
| 테스트 없이는 리팩터링 못 한다 | 4장 | 외부 동작 보존을 증명할 안전망 = 자동화된 테스트 |
| 이름 있는 작은 단계 | 5·6~12장 | 모든 변경에 카탈로그 이름·메커니즘·예제. "그냥 고친다" ≠ 리팩터링 |
| 리팩터링과 성능은 별개 | 2.8 | 리팩터링 후 측정 → 진짜 병목만. 미세 최적화 강박 회피 |
| YAGNI | 2.6 | 미래 가정으로 추측성 일반화하지 마라. 필요할 때 리팩터링으로 진화 |
| 3의 규칙(추정) | 2.4 | 세 번째 비슷한 코드가 나타나면 리팩터링 |
| 리팩터링과 아키텍처 | 2.6 | 큰 아키텍처 결정도 작은 리팩터링의 누적으로 도달 가능 |
책의 구조 — 3부 구성¶
목차에는 부 구분이 없지만 흐름상 3부로 묶을 수 있다.
| 부 (논리적) | 장 | 목적 |
|---|---|---|
| I. 개념과 원칙 | 1·2·3·4·5 | 첫 예시 → 원칙 → 24 악취 → 테스트 → 카탈로그 보는 법 |
| II. 리팩터링 카탈로그 | 6·7·8·9·10·11·12 | 66+ 리팩터링 기법 (기본·캡슐화·이동·데이터·조건·API·상속) |
| 부록 | A·B | 리팩터링 목록·악취 제거 기법 |
3장: 24가지 코드 악취 (Code Smells)¶
리팩터링의 트리거. 이 책의 가장 자주 인용되는 자산.
| # | 악취 | 한 줄 의미 | 자주 쓰는 리팩터링 |
|---|---|---|---|
| 1 | 기이한 이름 | 의도가 안 드러나는 이름 | 함수·변수 이름 바꾸기 (6.7·6.5) |
| 2 | 중복 코드 | 같은 구조가 여러 곳 | 함수 추출(6.1), 메서드 올리기(12.1) |
| 3 | 긴 함수 | 한 함수가 너무 많은 일 | 함수 추출(6.1), 임시 변수를 질의 함수로(7.4) |
| 4 | 긴 매개변수 목록 | 인자 4개+ | 매개변수 객체 만들기(6.8), 객체 통째로 넘기기(11.4) |
| 5 | 전역 데이터 | 어디서든 변경 가능한 상태 | 변수 캡슐화하기(6.6) |
| 6 | 가변 데이터 | 변경 가능한 필드·변수 | 변수 캡슐화(6.6), 변수 쪼개기(9.1), 질의 함수화(9.3) |
| 7 | 뒤엉킨 변경 | 한 곳을 바꾸려면 여러 곳을 바꿔야 함 | 단계 쪼개기(6.11), 함수 옮기기(8.1), 클래스 추출(7.5) |
| 8 | 산탄총 수술 | 한 변경이 여러 곳에 흩어짐 | 함수·필드 옮기기(8.1·8.2), 여러 함수를 클래스로(6.9) |
| 9 | 기능 편애 | 다른 클래스 데이터에 더 관심 | 함수 옮기기(8.1), 클래스 추출(7.5) |
| 10 | 데이터 뭉치 | 늘 함께 다니는 데이터 | 클래스 추출(7.5), 매개변수 객체(6.8) |
| 11 | 기본형 집착 | String/int로 도메인 표현 | 기본형을 객체로 바꾸기(7.3) |
| 12 | 반복되는 switch문 | 같은 분기가 여러 곳 | 조건부 로직을 다형성으로(10.4) |
| 13 | 반복문 | 명령형 반복 | 반복문을 파이프라인으로(8.8) |
| 14 | 성의 없는 요소 | 거의 비어 있는 클래스·함수 | 클래스 인라인(7.6), 함수 인라인(6.2), 계층 합치기(12.9) |
| 15 | 추측성 일반화 | "나중에 필요할까 봐" | 클래스 인라인(7.6), 함수 인라인(6.2), 매개변수 제거 |
| 16 | 임시 필드 | 가끔만 채워지는 필드 | 클래스 추출(7.5), 특이 케이스 추가(10.5) |
| 17 | 메시지 체인 | a.b().c().d().e() |
위임 숨기기(7.7), 함수 추출(6.1) + 옮기기(8.1) |
| 18 | 중개자 | 위임만 하는 클래스 | 중개자 제거하기(7.8) |
| 19 | 내부자 거래 | 클래스 간 사적 정보 공유 과다 | 함수·필드 옮기기, 위임 숨기기 |
| 20 | 거대한 클래스 | 한 클래스가 너무 많은 책임 | 클래스 추출(7.5), 슈퍼클래스 추출(12.8) |
| 21 | 서로 다른 인터페이스의 대안 클래스 | 같은 일을 다른 시그니처로 | 함수 선언 바꾸기(6.5), 슈퍼클래스 추출(12.8) |
| 22 | 데이터 클래스 | 행동 없이 데이터만 | 캡슐화, 함수 옮기기로 행동 끌어오기 |
| 23 | 상속 포기 | 자식이 부모 기능 무시 | 메서드/필드 내리기(12.4·12.5), 서브클래스를 위임으로(12.10) |
| 24 | 주석 | 코드가 의도를 못 드러내 주석으로 보충 | 함수 추출(6.1), 함수 선언 바꾸기(6.5) — 주석이 진짜 필요한 곳만 남김 |
6~12장: 66+ 리팩터링 카탈로그 요약¶
| 장 | 카테고리 | 개수 | 대표 기법 |
|---|---|---|---|
| 6 | 기본적인 리팩터링 | 11 | 함수 추출/인라인, 변수 추출/인라인, 함수 선언 바꾸기, 매개변수 객체, 단계 쪼개기 |
| 7 | 캡슐화 | 9 | 레코드/컬렉션 캡슐화, 기본형→객체, 클래스 추출/인라인, 위임 숨기기, 중개자 제거 |
| 8 | 기능 이동 | 9 | 함수/필드 옮기기, 문장 이동, 반복문 쪼개기/파이프라인화, 죽은 코드 제거 |
| 9 | 데이터 조직화 | 6 | 변수 쪼개기, 필드 이름 바꾸기, 파생 변수→질의 함수, 참조↔값, 매직 리터럴 |
| 10 | 조건부 로직 간소화 | 7 | 조건문 분해/통합, 보호 구문, 조건부 로직→다형성, 특이 케이스, 어서션 |
| 11 | API 리팩터링 | 13 | 질의-변경 분리(CQS), 플래그 인수 제거, 객체 통째로 넘기기, 생성자→팩터리, 오류 코드↔예외 |
| 12 | 상속 다루기 | 11 | 메서드/필드 올리기·내리기, 타입 코드→서브클래스, 슈퍼클래스 추출, 서브클래스/슈퍼클래스→위임 |
→ 총 약 66개 리팩터링 기법. 각 기법은 책의 정해진 형식(이름·예시·동기·절차·예제) 으로 카탈로그화됨.
위키 기존 페이지와의 매핑¶
| 리팩터링 주제 | 출처 | 위키 기존 페이지 |
|---|---|---|
| OOP 4원칙 위배가 곧 악취 | 3장 | concept-oop |
| 책임 주도 설계 = 리팩터링의 도달점 | 12장 + 8장 | entity-object (3·5장) |
| 합성 > 상속 = 12.10·12.11 위임 패턴 | 12장 | entity-object 11장, entity-effective-java Item 18 |
| 매개변수 4개+ → 객체화 | 6.8 | entity-effective-java Item 51 |
| 기본형 집착 → enum/타입 | 3.11 → 7.3 | entity-effective-java Item 62, Item 34 |
| 전역/가변 데이터 → 캡슐화 | 3.5·3.6 → 6.6 | entity-effective-java Item 17·78 |
| 조건부 로직 → 다형성 | 10.4 | concept-design-patterns (Strategy), entity-object 12장 |
| 오류 코드 → 예외 | 11.12 | entity-effective-java Item 69~77 (10장 예외) |
| CQS (질의-변경 분리) | 11.1 | entity-object 6.04, entity-effective-java Item 70 |
| 자가 테스트 = 리팩터링의 전제 | 4장 | src-spring-testing-ref |
| 생성자 → 팩터리 | 11.8 | entity-effective-java Item 1 (정적 팩터리) |
| 리팩터링 + 디자인 패턴 | 부록 A | concept-design-patterns |
같은 인사이트 패턴 — "이름 있는 메커니즘이 즉흥보다 안전하다"¶
리팩터링 카탈로그의 핵심 원리: 모든 변경에 이름·전제·절차. 즉흥적 코드 수정과 이름 있는 리팩터링의 차이는 안전성·재현성에서 결정적이다.
같은 원리가 위키의 다른 영역에 누적되어 있음:
| 영역 | 즉흥적 처리 | 이름 있는 메커니즘 | 참조 |
|---|---|---|---|
| 코드 변경 | "그냥 고친다" | 리팩터링 66 카탈로그 + 테스트 | (이 페이지) |
| AI 에이전트 | "잘 해줘" 부탁 | CLAUDE.md·hooks·skills | concept-harness-engineering |
| AI 루프 | 개별 프롬프트 즉흥 입력 | 루프 자체를 코드로 작성 | concept-loop-engineering |
| 자기검증 | 사람이 매번 테스트 돌림 | back-pressure hook 자동화 | concept-claude-hooks |
| 트랜잭션 | "에러 나면 알아서 처리하겠지" | @Transactional + rollbackFor 명시 |
concept-transactional-rollback-policy |
| DB 풀 | "기본값으로 시작" | HikariCP 3타이머 + Leak 감지 명시 | concept-db-connection-pool |
| DB 락 회피 | "동시 실행 안 일어나겠지" | 크론잡 concurrencyPolicy + deadline 명시 |
concept-cronjob-concurrency-trap |
→ 공통 원리: 안전한 시스템은 이름·전제·절차가 명시된 메커니즘의 누적. "잘 알아서 한다"는 가정은 어디서나 위험.
세 책의 관계 — 오브젝트·Effective Java·리팩터링¶
같은 OO 설계 주제를 5권이 서로 다른 단위·시점으로 가리킨다.
| 책 | 관점 | 단위 | 시점 | 언어 |
|---|---|---|---|---|
| entity-object 오브젝트 | 책임 주도 설계 (목적지) | 객체·협력·역할 | 처음부터 잘 설계 | Java |
| entity-effective-java Effective Java | 90 권고 (매뉴얼) | 메서드·필드·생성자 | 매번 짤 때 | Java |
| (이 책) entity-refactoring 리팩터링 2판 | 카탈로그 (가는 길) | 1단계 변환 | 이미 짠 코드 | JS (2판) |
| entity-clean-code Clean Code | 미시 규칙 + 휴리스틱 | 줄·이름·함수 | 매 라인 | Java |
| entity-tdd TDD | 사이클 (만드는 과정) | 사이클 1회 (분) | 코드 짜기 전 | Java + Python |
리팩터링 의 자리 : OO 원칙으로 이미 짠 코드를 개선하는 카탈로그. 다른 4권이 처음 짤 때·매번·1줄·1사이클이라면 리팩터링 은 이미 있는 코드에 적용하는 1단계 변환의 사전. 24 악취 + 66+ 기법.
추천 순서 (5권 전체): 1. Effective Java 2·3·8·10장 발췌 — 매일 짜는 코드 권고 2. 리팩터링 3장(24 악취) + 6장(기본 리팩터링) — 기존 코드 개선 어휘 3. 오브젝트 1~5장 — OO 설계의 큰 그림 4. 세 책 교차 참조 — 같은 원리의 다른 진입
세 책이 같은 결론을 다른 길로 가리킴 → 위키에 누적되면 강력한 교차 참조 자산.
누구에게·언제 권할 책인가¶
| 독자 | 효과 |
|---|---|
| 레거시 코드를 매일 만지는 사람 | 24 악취로 "왜 이 코드가 불편한지" 어휘를 갖게 됨 |
| 코드 리뷰어 | 리뷰 코멘트에 "X 악취 → Y 리팩터링" 같은 합의된 어휘 |
| TDD 입문자 | 4장이 "테스트 → 리팩터링 → 테스트"의 안전망 의미를 잡아줌 |
| 신입 교육 자료 짜는 사람 | 1장 첫 예시(statement() 단계별 리팩터링)가 통째로 강의 자료 |
| 디자인 패턴 학습자 | 패턴 = 리팩터링의 도달점. 6·11·12장이 GoF로 가는 길 |
빠른 진단 — 이 책을 펴야 할 신호¶
- 코드를 손대기가 무서워 같은 함수에 분기만 자꾸 추가하고 있다
- PR 리뷰에서 "이거 너무 길다/뒤엉켜 있다"는 코멘트를 자주 본다 (악취 3·7)
- 한 변경을 위해 5+ 파일을 동시에 수정해야 한다 (산탄총 수술 — 악취 8)
- 테스트 없이 "조심해서" 코드를 바꾸고 있다 (4장 필수)
-
if/else또는switch가 같은 분기 조건으로 3+ 곳에 반복된다 (악취 12 → 10.4) - 도메인 값을 String/int로 들고 다닌다 (악취 11 → 7.3)
위 중 2개+ → 3장(악취)·6장(기본 리팩터링)·4장(테스트) 우선 발췌.
한계·주의¶
- 언어 예제가 JavaScript (2판). Java/Kotlin 개발자는 시그니처 약간 다름. 단, 원칙은 100% 동일
- 자동화 도구 의존: IntelliJ/Eclipse의 리팩터링 메뉴를 능숙하게 쓸수록 가성비 폭증. 수동 리팩터링은 위험
- 거대 시스템 아키텍처 변경 가이드는 부족: 책은 함수·클래스 단위. 모듈·서비스 단위는 Working Effectively with Legacy Code(Feathers) 등 보완
- 테스트 작성 자체는 다른 책으로: 4장은 "왜·언제" 만, "어떻게"는 xUnit Test Patterns(Meszaros) 또는 TDD(Beck) 필요
원본 출처¶
raw/refactoring/toc.md— 사용자 입력 목차 (2026-06-20)raw/clean-code/리팩터링 실전 강의 교재 1~12장.md— 12편 강의 교재 (사용자 입력 1·2장 + 본 세션 작성 3~12장, 약 5,000줄). 책 본문 직접 인용 아닌 강의용 재구성.- 통합 인덱스: src-refactoring-lecture
- 저자 공식 사이트: https://refactoring.com/
관련 페이지¶
- src-refactoring-lecture — 실전 강의 교재 12장 통합 인덱스 (각 장 raw 링크)
- entity-object — 오브젝트 (책임 주도 설계 = 리팩터링의 목적지)
- entity-effective-java — Effective Java (매일의 권고 = 리팩터링 트리거 사전)
- entity-clean-code — Clean Code (17장 휴리스틱 66개는 리팩터링 24 악취와 짝. 같은 Uncle Bob 진영)
- concept-oop — 4원칙 위배 = 악취
- concept-design-patterns — 패턴 = 리팩터링의 도달점
- src-spring-testing-ref — 4장 테스트 구축의 실무
- concept-harness-engineering / concept-loop-engineering / concept-claude-hooks — "이름 있는 메커니즘이 즉흥보다 안전" 같은 인사이트 패턴
- concept-transactional-rollback-policy / concept-db-connection-pool / concept-cronjob-concurrency-trap — 같은 패턴이 인프라 영역에 누적된 사례