ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [서평] 클린 코드(Clean Code) - 애자일 소프트웨어 장인 정신
    도서리뷰/IT도서 2025. 12. 29. 03:18
    반응형

    "나쁜 코드도 돌아는 간다. 하지만 코드가 깨끗하지 못하면 개발 조직은 기어간다."


    📖 도서 정보

    항목 내용
    원제 Clean Code: A Handbook of Agile Software Craftsmanship
    저자 로버트 C. 마틴 (Robert C. Martin, "Uncle Bob")
    역자 박재호, 이해영
    출판사 인사이트(insight)
    출간일 2013년 12월 24일 (원서 2008년)
    페이지 584쪽
    ISBN 9788966262724

    🎯 이 책을 읽게 된 계기

    개발자라면 한 번쯤 들어봤을 "클린 코드". 보안 소프트웨어 개발을 하면서 minifilter 드라이버나 서비스 코드를 작성할 때마다 느끼는 것이 있다. 동작하는 코드를 작성하는 것과 유지보수 가능한 코드를 작성하는 것은 전혀 다른 영역이라는 것이다.

    특히 커널 드라이버처럼 디버깅이 어려운 영역에서는 코드의 가독성과 명확성이 곧 생산성과 직결된다. 이 책은 단순히 "예쁜 코드"를 넘어서 소프트웨어 장인으로서의 자세를 이야기한다.


    📋 목차 및 구성

    이 책은 크게 세 부분으로 나뉜다:

    Part 1: 원칙과 패턴 (1장~13장, 약 p.1~p.300)

    제목 주요 내용
    1장 깨끗한 코드 클린 코드의 정의와 필요성 (~p.30)
    2장 의미 있는 이름 변수, 함수, 클래스 명명법 (~p.55)
    3장 함수 작고 한 가지만 하는 함수 작성법 (~p.85)
    4장 주석 좋은 주석과 나쁜 주석 (~p.110)
    5장 형식 맞추기 코드 포맷팅과 팀 규칙 (~p.130)
    6장 객체와 자료 구조 추상화와 캡슐화 (~p.150)
    7장 오류 처리 예외 처리 전략 (~p.175)
    8장 경계 외부 코드와의 인터페이스 (~p.190)
    9장 단위 테스트 TDD와 테스트 원칙 (~p.220)
    10장 클래스 SRP와 클래스 설계 (~p.250)
    11장 시스템 시스템 수준 설계 (~p.280)
    12장 창발성 단순한 설계 규칙 (~p.300)
    13장 동시성 동시성 프로그래밍 (~p.330)

    Part 2: 사례 연구 (14장~16장, 약 p.330~p.480)

    제목 주요 내용
    14장 점진적인 개선 Args 클래스 리팩터링 사례
    15장 JUnit 들여다보기 JUnit 프레임워크 분석
    16장 SerialDate 리팩터링 실제 오픈소스 코드 개선

    Part 3: 휴리스틱 (17장, 약 p.480~p.584)

    제목 주요 내용
    17장 냄새와 휴리스틱 코드 스멜 총정리 (66가지 규칙)

    💡 핵심 내용 정리

    1장. 깨끗한 코드란? (p.1~30)

    저자는 여러 거장 프로그래머들의 클린 코드 정의를 인용한다:

    비야네 스트롭스트룹 (C++ 창시자)

    "나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다."

    그래디 부치 (UML 창시자)

    "클린 코드는 잘 쓴 문장처럼 읽힌다."

    론 제프리스

    "중복을 피하라. 한 기능만 수행하라. 제대로 표현하라. 작게 추상화하라."

    핵심 메시지: "나중은 결코 오지 않는다" (르블랑의 법칙)


    2장. 의미 있는 이름 (p.31~55)

    // ❌ 나쁜 예
    int d; // 경과 시간(일)
    int s; // 상태
    
    // ✅ 좋은 예  
    int elapsedTimeInDays;
    int fileStatus;

    명명 규칙 핵심:

    • 의도를 분명히 밝혀라: 변수명만 보고 용도를 알 수 있어야 한다
    • 그릇된 정보를 피하라: accountList가 실제로 List가 아니면 혼란을 준다
    • 의미 있게 구분하라: a1, a2 대신 source, destination
    • 검색하기 쉬운 이름을 사용하라: 상수는 명명된 상수로
    • 인코딩을 피하라: 헝가리안 표기법은 현대 IDE에서 불필요

    3장. 함수 (p.56~85)

    이 장은 클린 코드의 핵심 중의 핵심이다.

    함수 작성의 황금률:

    1. 작게 만들어라 - 함수는 20줄 이내가 이상적
    2. 한 가지만 해라 - Single Responsibility
    3. 함수 당 추상화 수준은 하나로
    4. 서술적인 이름을 사용하라
    5. 인수는 적을수록 좋다 - 이상적으로 0개, 최대 3개
    // ❌ 나쁜 예 - 여러 가지를 한다
    void processFile(const wchar_t* path, bool shouldBackup, int mode) {
        // 파일 읽기, 백업, 처리, 저장까지 모두...
    }
    
    // ✅ 좋은 예 - 한 가지만 한다
    void readFile(const wchar_t* path);
    void backupFile(const wchar_t* path);
    void processContent(Content& content);
    void saveFile(const wchar_t* path, const Content& content);

    4장. 주석 (p.86~110)

    "주석은 나쁜 코드를 보완하지 못한다."

    좋은 주석:

    • 법적인 주석 (저작권, 라이선스)
    • 정보를 제공하는 주석
    • 의도를 설명하는 주석
    • TODO 주석

    나쁜 주석:

    • 같은 이야기를 중복하는 주석
    • 오해할 여지가 있는 주석
    • 주석으로 처리한 코드
    • 이력을 기록하는 주석 (Git 사용)
    // ❌ 나쁜 예
    // i를 1 증가시킨다
    i++;
    
    // ✅ 좋은 예
    // 스레드가 아직 gFltContext가 준비되지 않은 상태에서
    // 언로드 요청이 오면 경합 조건이 발생할 수 있다.
    // 이를 방지하기 위해 스핀락으로 보호한다.
    KeAcquireSpinLock(&gContextLock, &oldIrql);

    5장. 형식 맞추기 (p.111~130)

    수직 형식:

    • 신문 기사처럼 위에서 아래로 읽히게
    • 개념은 빈 행으로 분리
    • 세로 밀집도: 연관된 코드는 가까이

    수평 형식:

    • 한 행은 80~120자 이내
    • 들여쓰기로 범위(scope) 표현
    • 팀 규칙을 따르라

    6장. 객체와 자료 구조 (p.131~150)

    핵심 개념: 디미터 법칙 (Law of Demeter)

    // ❌ 기차 충돌 (Train Wreck) - 나쁜 예
    output = context.getOptions().getScratchDir().getAbsolutePath();
    
    // ✅ 좋은 예
    Options opts = context.getOptions();
    File scratchDir = opts.getScratchDir();
    string output = scratchDir.getAbsolutePath();

    7장. 오류 처리 (p.151~175)

    핵심 원칙:

    • 오류 코드보다 예외를 사용하라
    • Try-Catch-Finally 문부터 작성하라
    • 예외에 의미를 제공하라
    • null을 반환하지 마라
    • null을 전달하지 마라
    // ❌ 나쁜 예 - null 반환
    Employee* getEmployee(int id) {
        if (!found) return nullptr;
        // ...
    }
    
    // ✅ 좋은 예 - 예외 또는 특수 사례 객체
    Employee getEmployee(int id) {
        if (!found) throw EmployeeNotFoundException(id);
        // ...
    }

    9장. 단위 테스트 (p.185~220)

    TDD의 세 가지 법칙:

    1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다
    2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다
    3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다

    FIRST 원칙:

    • Fast: 테스트는 빨라야 한다
    • Independent: 테스트는 독립적이어야 한다
    • Repeatable: 어떤 환경에서도 반복 가능해야 한다
    • Self-Validating: 부울 값으로 결과를 내야 한다
    • Timely: 적시에 작성해야 한다

    10장. 클래스 (p.221~250)

    단일 책임 원칙 (SRP)

    클래스는 변경할 이유가 하나여야 한다.

    // ❌ 나쁜 예 - 여러 책임
    class MinifilterDriver {
        void RegisterCallbacks();
        void HandleIrp();
        void LogToFile();        // 로깅 책임
        void SendToServer();     // 통신 책임
        void EncryptData();      // 암호화 책임
    };
    
    // ✅ 좋은 예 - 책임 분리
    class MinifilterDriver { /* 드라이버 핵심 로직만 */ };
    class Logger { /* 로깅 책임 */ };
    class NetworkClient { /* 통신 책임 */ };
    class Encryptor { /* 암호화 책임 */ };

    13장. 동시성 (p.295~330)

    동시성 방어 원칙:

    • 단일 책임 원칙(SRP): 동시성 코드는 분리하라
    • 자료 범위를 제한하라: 공유 자료를 최소화
    • 자료 사본을 사용하라
    • 스레드는 가능한 독립적으로 구현하라

    커널 개발자를 위한 적용:

    // 스핀락 사용 시 항상 IRQL 고려
    KIRQL oldIrql;
    KeAcquireSpinLock(&gLock, &oldIrql);
    __try {
        // 임계 영역
    } __finally {
        KeReleaseSpinLock(&gLock, oldIrql);
    }

    17장. 냄새와 휴리스틱 (p.480~584)

    마지막 장은 책 전체의 레퍼런스 가이드 역할을 한다. 총 66가지의 코드 스멜과 휴리스틱이 정리되어 있다.

    주요 휴리스틱 (일부):

    코드 규칙 설명
    G1 한 소스 파일에 여러 언어 금지 HTML, CSS, JS 혼재 금지
    G2 당연한 동작 구현 최소 놀람의 원칙
    G5 중복 DRY 원칙
    G23 다형성 > if/else 조건문보다 다형성 선호
    G25 매직 넘버 금지 상수 사용
    G30 함수는 한 가지만 SRP

    🔥 Windows 드라이버 개발자 관점에서의 적용

    이 책은 Java 기반 예제가 많지만, C/C++ 드라이버 개발에도 충분히 적용 가능하다:

    명명 규칙 적용

    // Before
    NTSTATUS cb(PFLT_CALLBACK_DATA d, PCFLT_RELATED_OBJECTS o, PVOID* c) {
        // ...
    }
    
    // After (Clean Code 적용)
    NTSTATUS PreOperationCallback(
        _Inout_ PFLT_CALLBACK_DATA CallbackData,
        _In_ PCFLT_RELATED_OBJECTS FltObjects,
        _Outptr_ PVOID* CompletionContext
    ) {
        // ...
    }

    함수 분리 적용

    // Before - 300줄짜리 거대 함수
    FLT_PREOP_CALLBACK_STATUS HandleAllOperations(...) {
        // 모든 IRP 처리 로직이 한 함수에...
    }
    
    // After - 역할별 분리
    FLT_PREOP_CALLBACK_STATUS DispatchPreOperation(...);
    NTSTATUS HandleCreate(...);
    NTSTATUS HandleWrite(...);
    NTSTATUS HandleSetInfo(...);

    ⚖️ 비판적 시각

    이 책이 출간된 지 17년이 지났다. 몇 가지 고려할 점:

    1. 시대적 맥락: 2008년 Java 기반 예제가 현대 개발 환경과 맞지 않을 수 있다
    2. 절대적 규칙은 없다: 저자도 인정하듯, 이것은 "오브젝트 멘토 학파"의 관점
    3. 현실과의 괴리: 모든 원칙을 완벽히 따르기는 현실적으로 어렵다
    4. 2판 집필 중: 저자가 2024년 8월 X(트위터)에서 2판 작업 중이라고 발표

    📝 총평

    별점: ★★★★☆ (4.5/5)

    항목 평가
    실용성 ★★★★★
    가독성 ★★★★☆
    현대성 ★★★☆☆
    추천도 ★★★★★

    추천 독자:

    • 주니어 개발자: 코드 품질에 대한 기준을 세우고 싶다면 필독
    • 시니어 개발자: 팀 코드 리뷰 기준 정립에 활용
    • 드라이버 개발자: 유지보수성 높은 커널 코드 작성을 원한다면

    읽는 팁:

    • 1~14장까지 정독 권장 (후반부는 Java 종속적)
    • 팀 스터디로 함께 읽으면 효과 극대화
    • 실제 코드에 바로 적용하면서 읽기

    🎬 마무리

    "캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라." - 보이스카우트 규칙

    이 책의 핵심은 단순하다. 코드를 작성할 때 읽는 사람을 배려하라는 것이다. 결국 그 "읽는 사람"은 미래의 나 자신일 가능성이 높다.

    minifilter 드라이버든, NextJS 웹앱이든, 코드는 결국 사람이 읽고 유지보수한다. 클린 코드는 선택이 아닌 프로페셔널의 기본 덕목이다.

    반응형

    댓글

Designed by Tistory.