# 순환 복잡도: 지금 리팩토링할 코드를 찾는 법

URL: https://whatshouldibuildnext.com/ko/journal/sunhwan-bokjapdo-refactoring-guide
Type: blog
Locale: ko
Published: 2026-08-01
Updated: 2026-08-27

---

> 순환 복잡도가 높은 함수는 버그의 온상이다. 측정 도구 3가지와 리팩토링 우선순위 기준, 실제 코드 패턴을 다룬다.

## 순환 복잡도: 지금 리팩토링할 코드를 찾는 법

함수 하나가 200줄을 넘어가고, if-else가 중첩 5단계를 넘어설 때 무언가 잘못됐다는 걸 직감한다. 문제는 막연함이다. 어디서부터 손을 댈지 모른다. 순환 복잡도는 그 막연함을 숫자로 바꿔준다.

## 순환 복잡도란 무엇인가

Thomas McCabe가 1976년에 정의한 소프트웨어 지표다. 소스 코드의 제어 흐름 그래프에서 독립 경로의 수를 센다. 공식은 `M = E - N + 2P` (E = 엣지, N = 노드, P = 연결 요소). 실무에서는 함수 안의 분기점(if, for, while, case, &&, ||)을 세고 1을 더한 값이 근사치로 맞다.

점수 기준:

- 
1-10: 간단, 테스트하기 쉬움

- 
11-20: 복잡, 주의 필요

- 
21-50: 매우 복잡, 리팩토링 권장

- 
50 이상: 테스트 불가능에 가까움, 지금 분해 필요

## 왜 이 숫자가 버그 예측에 실제로 쓸모 있나

순환 복잡도가 높은 함수는 테스트 케이스가 많이 필요하다. 경로 하나가 빠지면 버그가 숨는다. McCabe의 원래 논문과 이후 연구들은 복잡도 10 이상의 모듈에서 버그 밀도가 급격히 올라가는 것을 보여준다.

더 실용적인 관점: 복잡도가 높은 코드는 코드 리뷰 시간이 길어진다. 이 함수가 뭘 하는지 파악하는 데 시간이 걸리면, 리뷰어는 포기하거나 LGTM을 누른다. 두 결과 모두 좋지 않다.

![여러 제어 흐름 경로를 보여주는 코드 그래프 다이어그램](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/whatshouldibuildnext/2026-08/aec123-inline1.webp)

## 측정 도구 세 가지: 직접 써본 결과

**SonarQube**는 엔터프라이즈 팀에서 가장 많이 쓴다. 30개 이상 언어를 지원하고, CI 파이프라인에 붙이면 PR마다 복잡도 변화를 추적할 수 있다. 설치가 무겁지만 한 번 세팅하면 팀 전체가 쓸 수 있다.

**CodeScene**은 다르게 접근한다. 순환 복잡도뿐 아니라 Git 히스토리를 분석해 자주 바뀌는데 복잡한 파일을 우선순위로 올린다. 복잡하지만 6개월 동안 한 번도 안 바뀐 파일은 지금 당장 손댈 필요가 없다. 리팩토링 우선순위 결정에 특히 유용하다.

**Code Climate Quality**는 GitHub 연동이 깔끔하다. 오픈소스 프로젝트는 무료다. 복잡도 외에도 중복 코드, 메서드 길이, 인수 개수 같은 지표를 함께 보여준다.

## 리팩토링을 시작할 함수를 고르는 기준

도구가 복잡도 리포트를 뱉으면 목록이 나온다. 어디서 시작할지가 진짜 질문이다. 두 가지 기준을 교차한다:

- 
**순환 복잡도 15 이상** + **최근 3개월 내 변경된 파일**: 지금 당장 건드려야 할 곳

- 
**순환 복잡도 20 이상** + **테스트 커버리지 40% 미만**: 시한폭탄

CodeScene의 hotspot 기능이 이 교차점을 자동으로 시각화한다. 없으면 `git log --since=3.months --name-only | sort | uniq -c | sort -rn`으로 자주 바뀌는 파일 목록을 뽑아서 복잡도 리포트와 대조하면 된다.

![복잡도와 변경 빈도를 교차 분석하는 핫스팟 시각화 화면](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/whatshouldibuildnext/2026-08/cf7628-inline2.webp)

## 실제 리팩토링 패턴: Extract Method부터 시작하라

복잡도를 낮추는 가장 직접적인 방법은 함수 분해다. 복잡도 20짜리 함수를 복잡도 5짜리 4개로 나누면 각각을 독립적으로 테스트할 수 있다.

흔한 패턴:

`# 리팩토링 전 (복잡도 약 12)
def process_order(order):
    if order.status == 'pending':
        if order.payment_method == 'card':
            if order.amount > 1000:
                apply_large_order_discount(order)
            charge_card(order)
        elif order.payment_method == 'bank':
            initiate_bank_transfer(order)
    elif order.status == 'failed':
        notify_failure(order)
        if order.retry_count < 3:
            reschedule(order)

# 리팩토링 후 (각 함수 복잡도 3-4)
def process_order(order):
    if order.status == 'pending':
        process_pending_order(order)
    elif order.status == 'failed':
        handle_failed_order(order)

def process_pending_order(order):
    apply_discounts_if_applicable(order)
    charge_by_payment_method(order)

def handle_failed_order(order):
    notify_failure(order)
    if order.retry_count < 3:
        reschedule(order)`가독성이 올라가고, 테스트 케이스 작성이 쉬워지고, 코드 리뷰 시간이 줄어든다.

## 팀에 도입할 때 실패하는 이유

숫자를 목표로 삼는 순간 게임이 시작된다. 복잡도를 낮추기 위해 함수를 의미 없이 쪼개거나, 조건을 lookup 테이블로 숨기면 복잡도는 낮아지지만 코드는 더 이해하기 어려워진다.

실용적인 접근: 신규 코드에 임계치를 적용하고, 레거시는 점진적으로 개선한다. CI에서 복잡도 10 초과 신규 함수를 경고로 표시하되, 기존 코드에는 quality gate 대신 issues 탭을 주간 리뷰 용도로만 쓴다.

팀 컨벤션으로 정착시키는 가장 빠른 방법은 코드 리뷰 체크리스트에 항목 하나를 넣는 것이다. 이 함수의 순환 복잡도가 15를 넘으면 분리 여부를 검토한다.

## 사이드 프로젝트에서의 현실적인 적용

1인 빌더라면 도구 설치에 시간 쓰기 전에 명령어 한 줄이 더 빠르다. Python이면 `radon cc -s -a .`, JavaScript면 `npx complexity-report --format json src/`. 숫자를 보고 복잡도 15 이상짜리 함수를 리스트로 뽑아서 우선순위 3개만 정하면 된다.

6개월 후의 자신이 이 코드를 읽는다고 생각하면 판단이 쉬워진다. 복잡도 높은 함수는 나중에 기능 추가할 때 가장 먼저 막히는 곳이다.

## FAQ

### 순환 복잡도 임계치로 권장하는 숫자는?

McCabe 본인은 10을 제안했다. 실무에서는 팀마다 다르지만 신규 함수 기준 10-15 사이가 일반적이다. 15를 넘으면 리팩토링 논의를 시작할 신호다.

### 복잡도가 낮아도 나쁜 코드가 있나?

있다. lookup 테이블로 if-else를 숨기거나 callback 지옥을 추상화 뒤에 넣으면 순환 복잡도는 낮지만 이해하기 어렵다. 복잡도는 하나의 지표지 전부가 아니다.

### JavaScript와 TypeScript에서 어떻게 측정하나?

ESLint의 complexity 규칙을 활성화하면 린트 단계에서 잡힌다. {"complexity": ["error", 10]}으로 설정하면 CI에서 블로킹된다.

### SonarQube 무료로 쓸 수 있나?

Community Edition은 무료 오픈소스다. 로컬 또는 자체 서버에 설치해야 한다. SonarCloud(클라우드 버전)는 오픈소스 프로젝트 무료, 프라이빗은 유료다.

### 레거시 코드에 처음 적용할 때 어디서 시작해야 하나?

CodeScene의 hotspot 분석이나 git log와 복잡도 교차 분석으로 자주 바뀌는 복잡한 파일 목록을 만들어라. 거기서 상위 3개부터 시작하면 된다.

### 순환 복잡도와 인지 복잡도의 차이는?

순환 복잡도는 제어 흐름 경로 수를 센다. 인지 복잡도(SonarSource가 제안)는 중첩 깊이에 가중치를 둬서 사람이 읽기 어려운 정도를 더 잘 반영한다. 둘 다 함께 보는 게 좋다.

### 복잡도를 낮추면 성능이 떨어지나?

함수 분해로 인한 성능 차이는 현대 컴파일러와 런타임에서 무시할 수 있는 수준이다. 측정 없이 마이크로 최적화 이유로 복잡한 코드를 유지하는 건 잘못된 절충이다.