[Clean Code] 01장. Clean Code란 무엇인가

나쁜 코드가 낳는 기술 부채와 개발 속도 저하를 짚고, Stroustrup·Booch 등 소프트웨어 거장들의 정의로 Clean Code의 본질과 프로그래머의 전문가 윤리를 설명한다. 23개 챕터로 구성된 시리즈 전체 커리큘럼도 함께 소개한다.

이 시리즈에 대하여

Clean Code는 동작하는 코드와 좋은 코드를 가르는 기준을 다루는 소프트웨어 공학의 핵심 주제다. 이 시리즈는 Robert C. Martin의 『Clean Code: A Handbook of Agile Software Craftsmanship』(Prentice Hall, 2008)이 제시한 원칙을 뼈대로 삼되, 책이 출간된 지 15년 이상 지난 현재 시점의 언어 기능(Optional, 스트림, 타입 힌트, async/await)과 자동화 도구(린터, 포매터, 정적 분석기)를 함께 다룬다.

코드는 한 번 작성되고 나면 원래 저자보다 훨씬 많은 사람이 훨씬 오랜 기간 읽는다. 나쁜 이름, 뒤엉킨 함수, 숨겨진 부수 효과는 처음에는 사소해 보이지만, 프로젝트가 커질수록 새 기능 하나를 추가하는 데 필요한 시간을 기하급수적으로 늘린다. 이 시리즈를 완주하면 독자는 코드를 “동작하게 만드는 능력"에서 “다음 사람이 이해하고 안전하게 바꿀 수 있게 만드는 능력"으로 넘어갈 수 있다. 이는 신입 개발자에게는 코드 리뷰 통과율을, 경력 개발자에게는 리팩토링과 아키텍처 의사결정 역량을 직접적으로 끌어올리는 실무 기술이다.

23개 챕터는 다섯 단계로 묶여 있으며, 각 단계는 이전 단계에서 배운 원칙을 전제로 한다. 이름 하나 짓는 방법(3장)을 모르면 함수 분해(5장)의 결과물도 다시 모호해지고, 함수 분해를 모르면 클래스 설계(18장)에서 책임을 나누는 감각을 세우기 어렵다. 즉 이 시리즈는 목차 순서 자체가 학습 경로다.

단계챕터이 단계가 필요한 이유
기초01~02Clean Code의 정의와 비용 감각이 없으면 이후 규칙들이 “그냥 스타일 취향"으로 오해된다
어휘와 문장03~10이름·함수·주석·형식은 코드의 “문장 단위” 품질이다. 여기서 무너지면 상위 구조도 읽기 어렵다
자료와 오류11~15객체/자료구조 구분, 예외 처리, 외부 경계는 코드가 실패하는 방식을 결정한다
검증과 구조16~19테스트 없이는 리팩토링이 불가능하고, SOLID 없이는 검증 가능한 구조를 만들기 어렵다
시스템과 성숙20~23개별 클래스를 넘어 시스템 조립, 동시성, 레거시 개선까지 다뤄야 실무 규모의 코드에 대응할 수 있다

전체 목차는 다음과 같다.

챕터제목핵심 내용
01Clean Code란 무엇인가정의, 나쁜 코드의 비용, 전문가 윤리
02나쁜 코드 진단과 개선 실습Before/After 코드 분석
03의미있는 이름 짓기의도를 드러내는 네이밍 원칙
04네이밍 리팩토링 실습그릇된 이름 교정
05함수는 작게, 한 가지만SRP, 추상화 수준, 인수 개수
06함수 리팩토링 실습긴 함수 분해
07주석은 실패를 의미한다좋은 주석과 나쁜 주석
08주석 걷어내기 실습주석을 코드로 대체
09형식 맞추기와 코드 스타일세로/가로 형식, 팀 컨벤션
10포맷팅·린팅 자동화 실습포매터·린터 도입
11객체와 자료구조의 비대칭디미터 법칙, DTO
12디미터 법칙 리팩토링 실습기차 충돌 코드 개선
13오류 코드 대신 예외를 써라예외 설계, Null Object
14오류 처리 리팩토링 실습예외 계층 재설계
15경계 — 외부 라이브러리 사용법학습 테스트, 어댑터
16TDD 법칙과 F.I.R.S.T 원칙테스트 우선 개발
17단위 테스트 리팩토링 실습테스트 코드 정리
18클래스는 작아야 한다SRP, 응집도, SOLID
19SOLID 원칙 리팩토링 실습God Class 분해
20시스템과 의존성 주입제작과 사용의 분리
21창발적 설계 네 가지 규칙켄트 벡의 단순한 설계
22동시성 결함과 방어 원칙동시성 버그, 방어 전략
23리팩토링과 레거시 코드 개선점진적 개선 전략

이 장을 읽기 전에

이 장은 시리즈의 출발점이므로 특별한 선행 지식을 요구하지 않는다. 다만 최소 하나의 프로그래밍 언어로 함수와 클래스를 작성해 본 경험이 있어야 이후 예제(Java 중심, 필요 시 Python)를 무리 없이 읽을 수 있다. 이 장의 깊이는 초급~중급이며, 구체적인 리팩토링 기법이 아니라 “왜 이 시리즈 전체가 필요한가"에 대한 개념적 토대를 다진다. 코드 스타일 자동화 도구나 특정 디자인 패턴의 세부 구현은 다루지 않으며, 이는 각각 09장과 후속 챕터에서 다룬다.

수준읽을 부분핵심 목표
입문자전체, 특히 “전문가들의 Clean Code 정의"와 예제Clean Code가 막연한 스타일 취향이 아니라 구체적 기준이 있는 개념임을 이해한다
실무자“나쁜 코드의 비용”, “흔한 오개념”, “비판적 시각”팀 차원에서 Clean Code 원칙을 언제 엄격히, 언제 유연하게 적용할지 판단 기준을 세운다

Clean Code란 무엇인가

**Clean Code(클린 코드)**는 컴파일러가 아니라 사람이 읽기 위한 코드를 의미한다. 단순히 요구사항대로 동작하는 코드와 Clean Code의 차이는 “다음에 이 코드를 읽는 사람이 얼마나 적은 노력으로 의도를 파악할 수 있는가"에 있다. 이 정의는 추상적으로 들리지만, 실제로는 이름, 함수 크기, 추상화 수준, 오류 처리 방식처럼 구체적으로 관찰하고 측정할 수 있는 특성들로 구성된다.

Martin은 『Clean Code』 1장에서 여러 소프트웨어 거장에게 “Clean Code란 무엇인가"를 직접 물어 수집한 답변을 소개한다. C++ 창시자 Bjarne Stroustrup은 다음과 같이 정의했다.

“I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations. Clean code does one thing well.” — Bjarne Stroustrup, 『Clean Code』(Robert C. Martin, 2008) 1장 인용

(번역) “나는 코드가 우아하고 효율적이길 바란다. 논리는 버그가 숨을 곳이 없도록 곧아야 하고, 의존성은 유지보수를 쉽게 하도록 최소여야 하며, 오류 처리는 명확한 전략에 따라 완결돼야 하고, 성능은 최적에 가까워야 사람들이 원칙 없는 최적화로 코드를 어지럽히려는 유혹에 빠지지 않는다. 클린 코드는 한 가지 일을 잘 해낸다.”

객체지향 설계 방법론의 선구자인 Grady Booch는 더 짧고 문학적인 비유를 사용했다.

“Clean code is simple and direct. Clean code reads like well-written prose.” — Grady Booch, 『Clean Code』(Robert C. Martin, 2008) 1장 인용

(번역) “클린 코드는 단순하고 직접적이다. 클린 코드는 잘 쓰인 산문처럼 읽힌다.”

Wiki의 창시자 Ward Cunningham은 코드를 읽는 사람의 심리 상태로 정의를 설명했다.

“You know you are working with clean code when each routine you read turns out to be pretty much what you expected.” — Ward Cunningham, 『Clean Code』(Robert C. Martin, 2008) 1장 인용

(번역) “코드를 읽을 때 각 루틴이 예상했던 그대로일 때, 당신은 클린 코드를 다루고 있는 것이다.”

이 세 정의는 표현은 다르지만 공통점이 있다. 코드의 품질을 “얼마나 정교한 기법을 썼는가"가 아니라 “읽는 사람이 얼마나 적게 놀라는가"로 측정한다는 점이다. 아래 예제는 같은 로직을 두 가지 방식으로 구현한 것이다. 첫 번째는 문법적으로 완전히 정상이지만 읽는 사람에게 아무 맥락도 주지 않고, 두 번째는 변수와 함수 이름만으로 의도를 전달한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 나쁜 코드: 무엇을 하는지 이름만으로는 알 수 없다
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList)
        if (x[0] == 4)
            list1.add(x);
    return list1;
}

// Clean Code: 지뢰찾기 게임에서 깃발이 꽂힌 셀을 찾는다는 의도가 드러난다
public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
        if (cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCells;
}

두 코드는 로직이 동일하고 실행 결과도 같다. 차이는 오직 이름과 타입 선택에 있다. 그런데도 두 번째 코드를 읽는 데 걸리는 시간은 첫 번째보다 훨씬 짧다. 이것이 Clean Code 논의의 출발점이다 — 가독성은 부가 기능이 아니라 코드의 정확성·유지보수성과 직결되는 속성이다.

나쁜 코드의 비용

나쁜 코드가 문제가 되는 이유는 미학이 아니라 경제학이다. 팀이 새 기능을 추가할 때마다 기존 코드를 읽고, 그 코드가 어떻게 동작하는지 추론하고, 변경이 다른 부분에 미칠 영향을 예측해야 한다. 코드가 나쁠수록 이 세 단계에 드는 시간이 늘어나며, 이 증가는 선형이 아니라 코드베이스가 커질수록 가속되는 경향을 보인다. Martin Fowler는 이를 “기술 부채(Technical Debt)“라는 은유로 설명했는데, 금융 부채처럼 원금(당장 처리하지 못한 설계 개선)에 이자(그 설계를 우회하기 위해 매번 드는 추가 작업 시간)가 계속 붙는다는 뜻이다.

구분건강한 코드베이스기술 부채가 쌓인 코드베이스
기능 추가 시간기능 규모에 비례코드베이스 규모에 비례해 증가
버그 수정원인 국소화 가능부수 효과로 다른 버그 유발
온보딩코드가 곧 문서 역할구두 설명·경험에 의존
팀 심리리팩토링을 두려워하지 않음“건드리면 터진다"는 공포 문화 형성

이 악순환은 흔히 다음 패턴으로 나타난다. 일정에 쫓겨 급하게 코드를 작성하면, 그 나쁜 코드가 다음 기능 개발 속도를 늦춘다. 속도가 늦어지면 일정 압박이 더 커지고, 압박은 더 나쁜 코드를 낳는다. 이 순환을 끊지 않으면 결국 “전체 재작성"이라는 가장 비싼 해법에 도달한다. Martin은 이를 프로그래머들 사이에서 흔히 쓰이는 변명과 함께 소개하는데, “나중에 정리하겠다"는 이 흐름에서 거의 실현되지 않는다는 뜻으로 르블랑의 법칙(LeBlanc’s Law), 즉 “나중은 결코 오지 않는다(Later equals never)“가 자주 인용된다.

우리는 저자다

프로그래머가 코드를 작성하는 시간과 기존 코드를 읽는 시간의 비율은 압도적으로 읽기 쪽에 치우쳐 있다. 새 기능 하나를 구현하려면 관련 모듈을 읽고, 호출 관계를 추적하고, 변경이 안전한지 확인하는 과정이 실제 타이핑보다 훨씬 오래 걸린다. Martin은 이 비율을 대략 10:1로 제시하며, 정확한 수치는 프로젝트와 개인마다 다르지만 “쓰기보다 읽기가 압도적으로 많다"는 방향성 자체는 코드 리뷰 경험이 있는 개발자라면 대부분 동의하는 관찰이다.

이 사실은 코드를 쓰는 사람의 책임을 재정의한다. 코드를 작성하는 순간의 나는 “저자"이고, 이후 그 코드를 읽는 모든 사람—동료, 후임자, 그리고 6개월 뒤의 나 자신—은 “독자"다. 좋은 소설가가 독자를 배려해 문장을 다듬듯, 좋은 프로그래머는 미래의 독자를 배려해 코드를 다듬는다. 새 코드를 짜는 데 들이는 시간을 줄이려고 기존 코드를 대충 읽고 넘어가면, 그 대가는 다음 사람이 같은 실수를 반복하거나 더 큰 시간을 들여 코드를 이해하는 형태로 돌아온다.

흔한 오개념

**“Clean Code는 코드를 짧게 만드는 것이다”**라는 오해가 흔하다. 실제로는 짧은 코드가 항상 깨끗한 코드는 아니다. 한 줄짜리 삼항 연산자 체인이나 불필요하게 압축된 표현식은 오히려 읽는 사람의 인지 부하를 늘린다. Clean Code의 목표는 짧음이 아니라 의도의 투명성이며, 때로는 짧은 코드보다 조금 더 긴 코드가 훨씬 명확할 수 있다(3장에서 다루는 SetupTeardownIncluder.render처럼 긴 이름이 짧은 이름보다 나은 경우가 그 예다).

**“Clean Code 규칙은 예외 없이 지켜야 한다”**는 오해도 있다. Martin 자신도 책 전반에서 규칙을 적용하는 맥락(레거시 코드, 성능이 critical한 구간, 프로토타입)에 따라 판단이 달라질 수 있다고 인정한다. 규칙은 사고를 대신해주는 것이 아니라, 사고할 때 참고할 기준선이다. 이 시리즈의 각 장에서 “판단 기준"을 별도로 다루는 이유가 여기에 있다.

**“가독성을 높이면 항상 성능이 떨어진다”**는 오해도 흔하지만, 실제로는 함수를 잘게 나누고 이름을 명확히 하는 대부분의 리팩토링은 현대 컴파일러·인터프리터의 인라이닝, JIT 최적화 앞에서 실질적인 성능 손실이 거의 없다. Stroustrup의 정의에서 언급한 “원칙 없는 최적화(unprincipled optimizations)“라는 표현 자체가, 성능은 프로파일링으로 확인된 병목에서만 다뤄야 할 문제이지 가독성을 미리 희생할 이유가 아니라는 뜻을 담고 있다.

프로그래머의 책임과 비판적 시각

Clean Code 운동은 **소프트웨어 장인정신(Software Craftsmanship)**이라는 더 큰 흐름의 일부다. 이 관점은 프로그래머를 요구사항을 코드로 옮기는 타이피스트가 아니라, 품질에 책임을 지는 전문가로 규정한다. 품질에 대한 책임, 지속적 학습, 동료에 대한 멘토링, 정직한 자기평가가 이 정체성의 핵심 요소로 꼽힌다.

다만 이 시리즈가 다루는 원칙들이 논쟁의 여지가 없는 것은 아니다. 게임 엔진 개발자 Casey Muratori를 비롯한 일부 실무자들은 극단적으로 잘게 쪼갠 함수와 과도한 다형성이 오히려 데이터 흐름을 추적하기 어렵게 만들고, 캐시 지역성을 해쳐 성능에 실질적 악영향을 준다고 비판해 왔다. 이 비판은 특히 성능이 critical한 도메인(게임, 임베디드, 고빈도 거래 시스템)에서 설득력을 얻는다. 즉 Clean Code 원칙은 “코드를 읽고 바꾸는 사람이 많고, 요구사항이 자주 바뀌는” 애플리케이션 소프트웨어에서 가장 강한 효과를 내며, 도메인의 성격에 따라 트레이드오프를 다시 따져야 한다는 점을 이 시리즈 전반에서 함께 짚는다.

다음 장에서는

02장: 나쁜 코드 진단과 개선 실습에서는 이 장에서 다룬 정의와 기준을 실제 코드에 적용해, 나쁜 코드를 진단하고 단계적으로 개선하는 실습을 진행한다.

평가 기준

이 장을 읽은 후 다음을 할 수 있어야 한다.

  • Clean Code를 “짧은 코드"가 아니라 “읽는 사람의 인지 부하를 최소화하는 코드"로 설명할 수 있다.
  • 기술 부채가 쌓이는 악순환 구조(일정 압박 → 나쁜 코드 → 속도 저하 → 더 큰 압박)를 구체적 사례로 설명할 수 있다.
  • “Clean Code 규칙은 절대적이다"라는 흔한 오개념을 반박하고, 규칙을 언제 유연하게 적용해야 하는지 판단할 수 있다.
  • Clean Code 원칙에 대한 비판(과도한 함수 분해가 성능·추적성에 미치는 영향)을 설명하고, 어떤 도메인에서 그 비판이 더 설득력을 갖는지 구분할 수 있다.

참고 및 출처