# 코드 리팩토링 기법: 실제로 측정 가능한 결과를 만드는 방법

URL: https://codebasechat.com/ko/journal/ko-code-refactoring-gipbeop
Type: blog
Locale: ko
Published: 2026-06-29
Updated: 2026-06-30

---

> 코드 리팩토링 기법 9가지를 실측 기준으로 정리했다. 어디서 시작할지, 어떤 순서로 접근할지를 데이터 기반으로 다룬다.

팀원 세 명이 독립적으로 그 모듈을 '공포의 구역'이라고 부른다. 버그는 거기 몰려 있고, 그 파일을 스치기만 해도 PR 리뷰가 두 배로 길어진다. 지난번에 리팩토링을 시도한 사람은 스프린트 두 개를 통째로 쓰고, 피처 플래그를 망가뜨린 채 나왔다.

코드 리팩토링 기법은 '변수 이름 바꾸기'부터 '서비스 경계 전체를 재구성하기'까지 스펙트럼이 넓다. 이 글에서는 실제로 측정 가능한 지표를 바꾸는 기법들만 다룬다. 새 엔지니어의 첫 커밋까지 걸리는 시간, 모듈별 버그 발생률, PR 리뷰 소요 시간이 기준이다.

## Extract Method: 오늘 당장 할 수 있는 리팩토링

코드베이스에 기법을 하나만 적용해야 한다면 Extract Method다. 긴 함수 안에서 단일한 역할을 하는 코드 블록을 찾아 별도 함수로 분리하는 것이다. 즉각적인 효과가 두 가지다: 원래 함수가 읽기 쉬워지고, 분리된 함수는 독립적으로 테스트할 수 있다.

기준은 단순하다. 코드 블록에 주석을 달아야 무슨 역할인지 이해가 된다면, 그 블록은 함수여야 한다. 함수 이름이 곧 주석이 된다. 주석과 달리, 함수 이름은 낡아지면 빌드를 깨뜨린다.

![코드 리뷰 세션에서 협업하는 두 명의 엔지니어](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/codebasechat/2026-06/6cb492-inline1.webp)

실무에서 흔히 보이는 패턴이 있다. 200줄짜리 함수를 열면 데이터 검증, 비즈니스 로직 계산, DB 저장, 이메일 발송이 전부 한 곳에 들어 있다. 각 단계를 `validate_order_input()`, `calculate_discount()`, `persist_order()`, `notify_customer()`로 분리하면 테스트 커버리지 작성 시간이 절반으로 줄고, 다음 기능 추가 시 어느 부분을 수정해야 하는지 바로 보인다.

## 조건문을 다형성으로 교체하기

타입 필드를 체크하는 긴 if/elif/else 체인이나 switch 문은 코드 확장을 가장 어렵게 만드는 패턴 중 하나다. 수정 방법은 조건 분기를 클래스 계층구조나 전략 패턴으로 교체하는 것이다. 각 케이스가 동일한 인터페이스를 가진 독립 클래스가 된다.

단, 이 기법은 조건 분기가 둘 이하이고 앞으로 늘어날 가능성이 낮다면 건너뛰는 것이 낫다. 다형성은 오버헤드가 있다. 함수 하나가 있던 자리에 파일 여러 개가 생긴다. 규모가 커질수록 ROI가 나오는 기법이다.

한국 현업에서 자주 보이는 사례: 결제 방식에 따라 분기되는 로직. 카드, 계좌이체, 간편결제, 포인트가 하나의 함수에 if 블록으로 들어가 있다. 각 결제 수단을 `PaymentMethod` 인터페이스를 구현하는 클래스로 분리하면, 새로운 결제 수단 추가가 기존 코드 수정 없이 클래스 하나 추가로 끝난다.

## 핫스팟 분석: 어디를 먼저 리팩토링해야 하는가

순환 복잡도(cyclomatic complexity)와 변경 빈도를 함께 본다. 모듈이 아무리 복잡해도 3년째 아무도 건드리지 않는다면, 그것을 리팩토링하는 건 고고학이지 엔지니어링이 아니다. 비용을 발생시키는 모듈은 복잡하면서도 팀이 매주 수정하는 파일들이다.

![어두운 IDE 화면에 문법 강조 코드가 보이는 개발자 손 클로즈업](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/codebasechat/2026-06/003fd2-inline2.webp)

실제 측정 방법은 두 단계다. 첫째, 정적 분석 도구로 함수/파일별 순환 복잡도를 뽑는다. Python은 `radon`, JS/TS는 `complexity-report`, Java는 `checkstyle`을 쓴다. 둘째, git 로그에서 지난 90일 기준 파일별 커밋 수를 뽑는다: `git log --since="90 days ago" --name-only --format="" | sort | uniq -c | sort -rn | head -20`.

두 점수를 곱하거나 행렬로 시각화하면 우선순위가 바로 나온다. 복잡도 상위 25%이면서 변경 빈도 상위 25%인 파일이 진짜 핫스팟이다. BCG 2024 리포트에 따르면 이런 체계적 접근법은 임시방편 대비 ROI가 3배다.

## Branch-by-Abstraction: 운영 중인 시스템을 리팩토링하는 방법

서비스가 실제로 트래픽을 받고 있는 상태에서 핵심 모듈을 교체해야 할 때, 직접 수술하는 방식은 위험하다. Branch-by-Abstraction은 이 문제를 단계적으로 해결한다.

순서는 이렇다. 현재 구현을 감싸는 추상화 레이어를 먼저 만든다. 호출자들이 추상화를 통해 동작하도록 전환한다. 그 뒤에서 새 구현을 작성한다. 준비가 되면 스위치를 전환한다. 이것이 서비스 수준에서 Strangler Fig 패턴이 동작하는 방식이다.

이 접근법의 장점은 각 단계가 독립적으로 배포 가능하다는 점이다. 리팩토링이 스프린트 중간에 끼어도 언제든 멈출 수 있다. 팀이 동시에 다른 기능 작업을 계속할 수 있다.

## 매직 넘버를 명명된 상수로 교체하기

명명된 상수는 스타일 선호의 문제가 아니다. 단일 진실 공급원(single source of truth) 메커니즘이다. 상수가 바뀌면 전체 코드베이스에서 자동으로 반영된다.

![왼쪽은 케이블이 뒤엉킨 서버랙, 오른쪽은 깔끔하게 정리된 서버랙](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/codebasechat/2026-06/afe5e9-inline3.webp)

`timeout(30)`과 `timeout(REQUEST_TIMEOUT_SECONDS)`는 동작은 같다. 하지만 6개월 뒤 타임아웃을 45초로 바꿔야 할 때, 전자는 코드베이스에서 `30`을 전부 찾아서 어떤 맥락의 30인지 판단해야 한다. 후자는 상수 선언 한 줄을 바꾸면 끝난다.

더 중요한 것은 맥락이다. `PAYMENT_RETRY_LIMIT = 3`은 비즈니스 규칙을 코드에 명시하는 행위다. 나중에 이 숫자를 5로 바꿔야 한다면, 그게 비즈니스 결정임을 코드 히스토리가 보여준다.

## 파라미터 객체 도입하기

인수가 여섯 개인 함수는 잘못 호출될 함수다. 그 파라미터들이 항상 함께 움직인다면, 그것들은 객체 안에 있어야 한다.

`# 이전
def create_order(user_id, product_id, quantity, discount_code, shipping_address, payment_method):
    ...

# 이후
@dataclass
class OrderRequest:
    user_id: int
    product_id: int
    quantity: int
    discount_code: str | None
    shipping_address: Address
    payment_method: PaymentMethod

def create_order(request: OrderRequest):
    ...`파라미터 순서를 틀릴 위험이 사라진다. 타입 힌트가 IDE에서 바로 보인다. 나중에 파라미터를 추가해도 기존 호출부를 전부 수정할 필요가 없다. Stack Overflow 설문에 따르면 개발자의 62%가 기술 부채에 좌절감을 느낀다고 응답했는데, 이런 파라미터 지옥이 그 원인 중 하나다.

## 준비적 리팩토링 마인드셋

기능을 추가하기 직전에 리팩토링한다. 별도 프로젝트로 분리하지 않는다. Kent Beck의 표현이 정확하다: '변화를 쉽게 만들어라, 그런 다음 쉬운 변화를 만들어라.'

실무에서 이것이 중요한 이유가 있다. 리팩토링을 독립 스프린트로 제안하면 비즈니스 승인을 받기 어렵다. 하지만 '이 기능 추가에 2일 필요한데, 코드 정리를 먼저 하면 1일로 줄일 수 있다'는 대화는 완전히 다른 맥락이다. 기능 작업에 리팩토링이 내장되어 있다면, 관리자의 별도 승인이 필요 없다.

McKinsey 2024 데이터에 따르면 체계적 현대화를 적용한 팀은 작업 완료 속도가 40-50% 빨랐다. 그 '체계적'이라는 단어 안에 준비적 리팩토링이 포함되어 있다.

## AI 도구가 리팩토링에서 하는 것과 하지 못하는 것

Cursor, GitHub Copilot, Cody는 기계적 리팩토링의 마찰을 줄여준다. Extract Method, 변수 이름 변경, 파라미터 추출 같은 작업을 빠르게 처리한다. 이 부분은 실제로 도움이 된다.

하지만 이 도구들은 어디를 리팩토링해야 하는지 말해주지 못한다. 복잡도 트렌드를 추론하지 못하고, 팀이 어떤 파일을 가장 자주 수정하는지 모른다. 전략적 판단은 여전히 개발자의 몫이다.

핫스팟 분석은 AI가 대신해줄 수 없다. git 히스토리와 복잡도 데이터를 읽고, 팀의 도메인 지식을 결합해서 '이번 분기에 여기를 정리해야 한다'는 결정을 내리는 것은 사람이 해야 한다.

## 월요일 아침에 시작하는 방법

이번 주에 레포지토리에 핫스팟 분석을 돌려라. 점수가 가장 높은 파일을 하나 골라라. 그 파일에서 가장 긴 함수 세 개에 Extract Method를 적용하라. 테스트를 작성하라. 시작 전 복잡도 점수를 커밋 메시지에 기록하고 커밋하라.

다음 스프린트에 그 파일이 또 등장하면, 이전 커밋 메시지를 참조해서 얼마나 개선됐는지 확인할 수 있다. 측정 불가능한 리팩토링은 다음 스프린트 계획에서 정당화하기 어렵다.

## FAQ

### 코드 리팩토링 기법을 처음 적용할 때 어디서 시작해야 하나요?

핫스팟 분석부터 시작하세요. 순환 복잡도와 git 변경 빈도를 결합해서 복잡하면서 자주 수정되는 파일을 찾고, 그 파일에 Extract Method를 먼저 적용하면 가장 빠르게 효과를 볼 수 있습니다.

### 리팩토링과 기능 개발을 동시에 진행해도 되나요?

네, 오히려 권장됩니다. Kent Beck의 준비적 리팩토링 접근법처럼, 기능 추가 직전에 해당 코드를 정리하면 기능 작업이 빨라지고 별도 승인 없이 리팩토링을 진행할 수 있습니다.

### AI 도구가 코드 리팩토링에 실제로 도움이 되나요?

Extract Method나 이름 변경 같은 기계적 리팩토링에는 도움이 됩니다. 하지만 어떤 모듈을 우선 리팩토링해야 하는지, 복잡도 트렌드를 어떻게 해석해야 하는지 같은 전략적 판단은 여전히 개발자가 해야 합니다.

### 조건문을 다형성으로 교체하는 것이 항상 좋은 선택인가요?

아닙니다. 분기가 2개 이하이고 늘어날 가능성이 낮다면 오버엔지니어링입니다. 분기 케이스가 많고 앞으로 계속 추가될 가능성이 높을 때 ROI가 나옵니다.

### 리팩토링 효과를 어떻게 측정하나요?

시작 전 순환 복잡도, PR 리뷰 시간, 해당 모듈의 버그 발생률을 기록하세요. 리팩토링 후 같은 지표를 4-8주 뒤에 다시 측정하면 효과가 수치로 보입니다.

### Branch-by-Abstraction은 언제 사용해야 하나요?

운영 중인 시스템에서 핵심 구현체를 교체해야 할 때 사용합니다. 추상화 레이어를 먼저 만들고 새 구현을 뒤에서 준비한 뒤 스위치를 전환하므로, 서비스를 중단하지 않고 대규모 변경이 가능합니다.

### 파라미터 객체를 도입할 기준은 무엇인가요?

함수 인수가 4개 이상이고 그 파라미터들이 항상 함께 전달된다면 객체로 묶을 시점입니다. 파라미터 순서 오류가 런타임에서 발생한 적이 있다면 즉시 적용해야 합니다.