리팩토링 (Refactoring)
📌 기존 코드를 변경하여 코드의 구조를 개선하고, 가독성, 유지보수성을 높이는 프로세스.
- 기능의 결과(동작)는 변경하지 않고, 내부 구조를 더 효율적으로 바꾸는 것이 목적.
2. 리팩토링의 필요성
- 코드 가독성 향상:
- 다른 개발자와의 협업을 원활하게 하고, 새로운 팀원이 코드를 이해하기 쉽게 함.
- 유지보수성 개선:
- 수정과 추가가 쉬워져서 변화에 빠르게 대응 가능.
- 버그 감소:
- 복잡하고 중복된 로직을 단순화하여 버그 발생 가능성을 줄임.
- 기술 부채 관리:
- 과거의 빠른 개발로 인해 생긴 기술 부채를 해소.
3. 리팩토링을 진행하는 시점
- 기능 개발 완료 후:
- 동작이 정상적으로 구현된 후 코드 품질을 개선.
- 코드 리뷰 중:
- 팀원들과 코드를 리뷰하며 리팩토링 방향 논의.
- 새로운 기능 추가 전:
- 기존 코드가 복잡하다면 새로운 기능 추가 전에 정리.
- 코드에서 '나쁜 냄새'가 날 때:
- 중복 코드, 지나치게 긴 메서드, 과도한 의존성 등이 있을 때.
4. 좋은 리팩토링을 위한 기법
|
설명 |
예제 |
메서드 추출 |
길고 복잡한 메서드의 일부를 별도의 메서드로 분리. |
복잡한 조건문을 메서드로 분리하여 가독성 향상. |
클래스 분리 |
하나의 클래스가 너무 많은 책임을 질 경우, 역할별로 클래스를 나눔. |
데이터 처리와 비즈니스 로직을 분리. |
변수 이름 개선 |
의미 없는 이름 대신, 명확한 변수 이름을 사용. |
val a = 10 → val maxRetryCount = 10. |
조건문 단순화 |
중첩된 조건문을 단순화하거나, Guard Clause 사용. |
if (age < 18) return "Minor"; else return "Adult"; → if (age < 18) return "Minor"; |
매직 넘버 제거 |
의미 없는 상수 값을 상수로 정의하여 사용. |
if (user.age > 18) → if (user.age > MINIMUM_AGE). |
중복 코드 제거 |
여러 곳에서 반복되는 코드를 공통 메서드로 추출. |
특정 포맷팅 로직을 formatText() 메서드로 추출하여 재사용. |
인터페이스 도입 |
여러 클래스에서 공통 동작이 있다면 인터페이스로 추상화. |
여러 데이터 저장소에 대해 Repository 인터페이스 도입. |
의존성 주입 사용 |
객체 간 강한 결합을 줄이고, 외부에서 의존성을 주입받도록 수정. |
val repo = Repository() → constructor(private val repo: Repository). |
불필요한 코드 제거 |
사용하지 않는 코드, 주석, 디버깅 코드 등을 제거. |
오래된 주석 제거, 사용하지 않는 메서드 삭제. |
5. 피해야 할 리팩토링 방식
- 과도한 추상화:
- 너무 많은 클래스를 만들어 코드를 복잡하게 만드는 것.
- 예: 간단한 작업을 위해 지나치게 많은 계층을 도입.
- 기능 변경:
- 리팩토링은 기능을 바꾸지 않는 것이 원칙.
- 예: 리팩토링 중에 기능을 추가하거나 수정하여 기존 동작이 변경됨.
- 불완전한 테스트 없이 진행:
- 리팩토링 전에 충분한 테스트 코드가 없다면, 변경 후 기존 기능이 깨질 위험이 있음.
- 한 번에 너무 많은 변경:
- 여러 파일과 클래스를 동시에 수정하면 오류 발생 가능성 증가.
- 작은 단위로, 변경 후 테스트하며 진행해야 함.
- 성능에 지나친 초점:
- 지나친 성능 최적화를 하다 보면 코드가 복잡해지고, 유지보수가 어려워짐.
6. 기술 부채 관리
- 기술 부채(Technical Debt):
- 빠른 개발을 위해 일시적으로 작성된 낮은 품질의 코드나 비효율적 설계.
- 이후 더 많은 시간과 비용을 들여 수정해야 함.
종류 |
설명 |
디자인 부채 |
비효율적 설계로 유지보수 어려움. |
코드 부채 |
품질이 낮거나 중복된 코드. |
빌드/배포 부채 |
자동화되지 않은 빌드 및 배포 프로세스. |
테스트 부채 |
충분한 테스트가 없는 상태로 기능 추가. |
7. 리팩토링의 장점과 단점
장점 |
단점 |
코드 가독성 및 유지보수성 향상 |
시간과 노력이 많이 소요. |
기술 부채 관리 가능 |
리팩토링 중 기능이 깨질 위험성 존재. |
코드 중복 제거 및 성능 최적화 |
충분한 테스트 없이 진행하면 버그 발생 가능. |
새로운 기능 추가 전 코드 안정화 가능 |
과도한 리팩토링 시 복잡성 증가. |
8. 리팩토링의 단계
- 코드 분석:
- 리팩토링이 필요한 영역 식별.
- "나쁜 냄새"가 나는 코드를 찾음.
- 작은 단위로 변경:
- 한 번에 너무 많은 코드를 수정하지 않고, 작은 단위로 작업.
- 테스트 코드 작성:
- 리팩토링 전, 기존 코드의 동작을 보장할 테스트 작성.
- 변경 후 테스트:
- 리팩토링 후, 기존 기능이 정상 동작하는지 테스트.
- 코드 리뷰 및 검토:
결론
리팩토링은 코드의 가독성과 유지보수성을 높이기 위한 중요한 과정입니다. 하지만 무분별한 리팩토링은 오히려 복잡성을 증가시키고, 프로젝트에 부정적인 영향을 미칠 수 있습니다. 적절한 시점과 범위를 설정하여, 작은 단위로 진행하는 것이 중요합니다.