리팩토링 (Refactoring)

📌 기존 코드를 변경하여 코드의 구조를 개선하고, 가독성, 유지보수성을 높이는 프로세스.

  • 기능의 결과(동작)는 변경하지 않고, 내부 구조를 더 효율적으로 바꾸는 것이 목적.

2. 리팩토링의 필요성

  1. 코드 가독성 향상:
    • 다른 개발자와의 협업을 원활하게 하고, 새로운 팀원이 코드를 이해하기 쉽게 함.
  2. 유지보수성 개선:
    • 수정과 추가가 쉬워져서 변화에 빠르게 대응 가능.
  3. 버그 감소:
    • 복잡하고 중복된 로직을 단순화하여 버그 발생 가능성을 줄임.
  4. 기술 부채 관리:
    • 과거의 빠른 개발로 인해 생긴 기술 부채를 해소.

3. 리팩토링을 진행하는 시점

  1. 기능 개발 완료 후:
    • 동작이 정상적으로 구현된 후 코드 품질을 개선.
  2. 코드 리뷰 중:
    • 팀원들과 코드를 리뷰하며 리팩토링 방향 논의.
  3. 새로운 기능 추가 전:
    • 기존 코드가 복잡하다면 새로운 기능 추가 전에 정리.
  4. 코드에서 '나쁜 냄새'가 날 때:
    • 중복 코드, 지나치게 긴 메서드, 과도한 의존성 등이 있을 때.

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. 피해야 할 리팩토링 방식

  1. 과도한 추상화:
    • 너무 많은 클래스를 만들어 코드를 복잡하게 만드는 것.
    • 예: 간단한 작업을 위해 지나치게 많은 계층을 도입.
  2. 기능 변경:
    • 리팩토링은 기능을 바꾸지 않는 것이 원칙.
    • 예: 리팩토링 중에 기능을 추가하거나 수정하여 기존 동작이 변경됨.
  3. 불완전한 테스트 없이 진행:
    • 리팩토링 전에 충분한 테스트 코드가 없다면, 변경 후 기존 기능이 깨질 위험이 있음.
  4. 한 번에 너무 많은 변경:
    • 여러 파일과 클래스를 동시에 수정하면 오류 발생 가능성 증가.
    • 작은 단위로, 변경 후 테스트하며 진행해야 함.
  5. 성능에 지나친 초점:
    • 지나친 성능 최적화를 하다 보면 코드가 복잡해지고, 유지보수가 어려워짐.

6. 기술 부채 관리

  • 기술 부채(Technical Debt):
    • 빠른 개발을 위해 일시적으로 작성된 낮은 품질의 코드나 비효율적 설계.
    • 이후 더 많은 시간과 비용을 들여 수정해야 함.
종류 설명
디자인 부채 비효율적 설계로 유지보수 어려움.
코드 부채 품질이 낮거나 중복된 코드.
빌드/배포 부채 자동화되지 않은 빌드 및 배포 프로세스.
테스트 부채 충분한 테스트가 없는 상태로 기능 추가.

7. 리팩토링의 장점과 단점

장점 단점
코드 가독성 및 유지보수성 향상 시간과 노력이 많이 소요.
기술 부채 관리 가능 리팩토링 중 기능이 깨질 위험성 존재.
코드 중복 제거 및 성능 최적화 충분한 테스트 없이 진행하면 버그 발생 가능.
새로운 기능 추가 전 코드 안정화 가능 과도한 리팩토링 시 복잡성 증가.

8. 리팩토링의 단계

  1. 코드 분석:
    • 리팩토링이 필요한 영역 식별.
    • "나쁜 냄새"가 나는 코드를 찾음.
  2. 작은 단위로 변경:
    • 한 번에 너무 많은 코드를 수정하지 않고, 작은 단위로 작업.
  3. 테스트 코드 작성:
    • 리팩토링 전, 기존 코드의 동작을 보장할 테스트 작성.
  4. 변경 후 테스트:
    • 리팩토링 후, 기존 기능이 정상 동작하는지 테스트.
  5. 코드 리뷰 및 검토:
    • 팀원들과 코드 품질과 변경 사항 검토.

결론

리팩토링은 코드의 가독성과 유지보수성을 높이기 위한 중요한 과정입니다. 하지만 무분별한 리팩토링은 오히려 복잡성을 증가시키고, 프로젝트에 부정적인 영향을 미칠 수 있습니다. 적절한 시점과 범위를 설정하여, 작은 단위로 진행하는 것이 중요합니다. 

'기본기' 카테고리의 다른 글

클린 아키텍처 (Clean Architecture)  (1) 2025.02.23
테스트와 개발  (0) 2025.02.21
백엔드 로드맵  (0) 2025.01.06
Clean Cord  (4) 2024.12.05
이름 짓기 규칙  (0) 2024.12.05

+ Recent posts