Chapter 5

구부러지거나 부러지거나

  • 5.1 결합도 줄이기
  • 5.2 실세계를 갖고 저글링하기
  • 5.3 변환 프로그래밍
  • 5.4 상속세
  • 5.5 설정

유연한 코드는 변화에 적응하고, 경직된 코드는 변화에 부러져. 이 장은 유연한 코드를 만드는 구체적인 기법들을 다루거든.

묻지 말고 말하라(Tell, Don't Ask). 그리고 디미터 법칙(Law of Demeter).

결합도가 높으면 한 모듈을 바꿀 때 연쇄적으로 다른 모듈도 바꿔야 하잖아. 코드가 서로 엮여서 움직이지 못하는 상태지.

디미터 법칙을 쉽게 말하면: "친구하고만 대화하라." 메서드 안에서 접근할 수 있는 건 자기 자신, 파라미터, 직접 생성한 객체, 자기 필드뿐이야. a.getB().getC().doSomething() 같은 체이닝은 디미터 법칙 위반이지. a가 B의 존재를 알고, B가 C를 갖고 있다는 것까지 아는 셈이거든.

결합을 줄이는 패턴들:

  • 이벤트 — 직접 호출 대신 이벤트를 발행하고 구독
  • 위임 — 체이닝 대신 중간 객체에 요청을 위임
  • 인터페이스 — 구체 클래스 대신 추상화에 의존

전역 변수도 결합의 원흉이야. 전역 변수를 쓰면 코드 어디서든 접근할 수 있으니 모든 코드가 암묵적으로 결합되지.

20주년 기념판에서 크게 보강된 절이야. 이벤트 기반 아키텍처를 다루거든.

실세계는 이벤트의 연속이잖아. 사용자가 클릭하고, 데이터가 도착하고, 타이머가 울리고. 이런 이벤트를 어떻게 처리할지 네 가지 전략을 소개해:

  • 유한 상태 기계(FSM) — 상태와 전이를 명시적으로 정의. 작은 시스템에 적합하지
  • 감시자 패턴(Observer) — 이벤트 발생 시 등록된 관찰자들에게 통지. 가장 고전적인 패턴이야
  • 게시-구독(Pub/Sub) — 감시자 패턴의 확장. 발행자와 구독자가 서로를 몰라. 중간에 채널이 있지
  • 반응형 프로그래밍(Reactive) — 값의 변화가 자동으로 전파. 스프레드시트처럼 — A1을 바꾸면 A1을 참조하는 B1도 자동으로 바뀌는 거야

어떤 걸 쓸지는 상황에 따라 다르지만, 공통 원칙은 하나야: "이벤트에 반응하는 코드는 이벤트를 발생시키는 코드와 분리되어야 해."

20주년 기념판의 완전히 새로운 절이야. 프로그래밍은 코드에 관한 것이 아니라, 데이터 변환에 관한 것이지.

유닉스 파이프라인을 생각하면 돼:

cat access.log | grep error | sort | uniq -c | sort -rn | head -10

각 단계가 입력을 받아서 변환해서 출력해. 상태를 관리하는 게 아니라 데이터를 흘려보내는 거야.

이 사고방식을 코드에 적용하면:

  • 함수를 "입력 → 변환 → 출력"으로 생각
  • 객체의 내부 상태를 변경하는 대신 새로운 데이터를 만들어 반환
  • 에러 처리도 파이프라인의 일부로 — 에러가 발생하면 에러를 담은 데이터를 다음 단계로 전달

함수형 프로그래밍의 핵심 아이디어와 통하지. OOP에서도 이 방식을 활용할 수 있다는 게 저자들의 입장이야. 꼭 Haskell을 배우라는 게 아니라, **"데이터 흐름 관점에서 설계하라"**는 거거든.

상속은 거의 쓰지 마. 20주년 기념판에서 가장 과감하게 바뀐 조언 중 하나야.

원래 판에서는 상속을 적절히 쓰라고 했는데, 이번에는 한 발 더 나갔지. 상속의 문제:

  • 결합도 증가 — 자식 클래스가 부모 클래스의 내부 구현에 의존해
  • 취약한 기반 클래스 문제 — 부모를 바꾸면 모든 자식이 영향받지
  • 다이아몬드 상속 — 다중 상속의 복잡성

대안 세 가지:

  • 인터페이스와 프로토콜 — "무엇을 할 수 있는가"를 정의. 구현은 각자
  • 위임(Delegation) — "has-a" 관계로 기능을 조합. 상속 대신 포함
  • 믹스인과 트레이트 — 기능의 조각을 필요한 곳에 끼워 넣는 방식

저자들이 하는 말: "상속이 답인 경우는 is-a 관계가 정말 명확할 때뿐이야." 그마저도 인터페이스 + 위임으로 대체 가능한 경우가 대부분이지.

애플리케이션의 값을 코드 바깥에서 설정해.

하드코딩된 값은 코드를 경직되게 만들잖아. 포트 번호, DB 연결 문자열, 기능 플래그, 외부 서비스 URL — 이런 건 전부 설정으로 빼야 해.

이전 판에서는 설정 파일(XML, YAML 등)을 강조했는데, 20주년 기념판에서는 범위가 넓어졌어:

  • 설정 파일 — 가장 기본
  • 환경 변수 — 12 Factor App 방식. 배포 환경마다 다르게 설정
  • 설정 서비스 — 런타임에 중앙 서비스에서 설정을 가져옴. 재시작 없이 설정 변경 가능

핵심: 설정을 외부화하면 코드를 건드리지 않고 동작을 바꿀 수 있어. 이게 바로 ETC(Easier to Change)야.

단, 설정이 너무 많아지면 그것도 문제거든. "이 설정이 진짜 필요한가?"를 항상 질문해. 아무도 안 바꿀 값을 설정으로 빼는 건 복잡성만 늘리는 거지.


정리

5장 읽고 기억할 거 세 가지:

  1. 결합도가 낮을수록 유연해. 디미터 법칙, 이벤트 기반, 인터페이스 — 전부 결합을 줄이는 도구야
  2. 데이터 변환 관점으로 설계해. 상태를 변이시키지 말고 데이터를 흘려보내는 거지
  3. 상속보다 조합을 써. 인터페이스 + 위임이 대부분의 경우 더 나은 선택이야