Chapter 7

코딩하는 동안

  • 7.1 파충류의 뇌에 귀 기울이기
  • 7.2 우연에 맡기는 프로그래밍
  • 7.3 알고리즘의 속도
  • 7.4 리팩터링
  • 7.5 테스트로 코딩하기
  • 7.6 속성 기반 테스트
  • 7.7 바깥에서는 안전에 주의하라
  • 7.8 이름 짓기

가장 실무적인 장이야. 키보드 위에 손을 올려놓고 코딩하는 바로 그 순간에 적용되는 조언들이거든. 8개 절로 구성되어 있어서 이 책에서 분량이 가장 많은 장이기도 하지.

20주년 기념판에서 새로 추가된 절이야. 코딩하다가 뭔가 찜찜한 느낌을 받을 때가 있잖아. 구체적으로 뭐가 잘못됐는지 말할 수 없는데 불안한 거.

저자들은 이걸 무시하지 말라고 해. 그건 경험이 축적된 무의식, 즉 "파충류의 뇌"가 보내는 신호야.

찜찜할 때 해볼 것들:

  • 멈춰라 — 코드에서 잠시 손을 떼고 산책해. 샤워해. 다른 걸 해
  • 외부화하라 — 화이트보드에 그림을 그리거나, 동료한테 설명해
  • 프로토타입을 만들어라 — 불안한 부분만 떼어내서 작게 실험해

"뭔가 잘못된 것 같은데 코드를 계속 짜는" 행위를 저자들은 강력히 경고하거든. 그 찜찜함을 무시하고 전진하면 나중에 더 큰 비용으로 돌아오지.

반대로, 빈 에디터 앞에서 막막할 때도 같은 원리야. 무의식이 "아직 충분히 이해 못했다"고 말하는 거거든. 억지로 코드부터 짜지 말고, 문제를 더 이해하는 데 시간을 써.

왜 되는지 모르는데 일단 돌아가는 코드 — 이게 가장 위험해.

우연에 맡기는 프로그래밍의 형태들:

  • 코드를 이리저리 고치다 보니 우연히 테스트가 통과
  • 특정 순서로 호출해야만 되는데 그 이유를 모름
  • 환경 의존적인 동작을 코드의 기능이라고 착각
  • 문서화되지 않은 API 동작에 의존

**"되니까 넘어가자"**가 기술 부채의 씨앗이야.

해결책: 의도적으로 프로그래밍해.

  • 자기가 작성한 코드가 왜 동작하는지 설명할 수 있어야 해
  • 가정을 문서화해
  • 가정을 테스트로 검증해
  • 우연히 통과한 테스트보다 실패해야 할 곳에서 실패하는 테스트가 더 가치 있지

Big-O 표기법과 알고리즘 복잡도에 대한 실용적 가이드야.

실무에서 알아야 할 것:

  • O(1) — 상수 시간. 해시맵 조회
  • O(log n) — 이진 탐색. 정렬된 데이터에서 찾기
  • O(n) — 선형. 리스트 전체 순회
  • O(n log n) — 좋은 정렬 알고리즘
  • O(n^2) — 이중 루프. 데이터가 크면 느려지지
  • O(2^n) — 지수. 실용적으로 사용 불가

저자들이 강조하는 건 **"이론보다 측정"**이야. Big-O는 추세를 알려주지만 실제 성능은 측정해야 알거든. O(n^2)이라도 n이 작으면 O(n log n)보다 빠를 수 있어.

그리고 **"섣부른 최적화는 모든 악의 근원"**이라는 크누스의 말을 인용하지. 프로파일러로 실제 병목을 찾은 다음에 최적화해. 감으로 하지 마.

리팩터링은 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 거야. 마틴 파울러의 정의를 그대로 쓰지.

언제 리팩터링해야 하는가:

  • DRY 위반을 발견했을 때 — 중복이 보이면
  • 직교적이지 않을 때 — 결합도가 높으면
  • 지식이 오래됐을 때 — 요구사항이 바뀌었는데 코드가 옛날 모델을 반영하면
  • 성능 문제 — 구조를 바꿔야 최적화할 수 있을 때
  • 테스트가 통과하는데 코드가 마음에 안 들 때

리팩터링의 핵심 규칙:

  • 기능 추가와 리팩터링을 동시에 하지 마. 모자를 하나만 써
  • 작은 단계로 해. 한 번에 하나씩 바꾸고, 바꿀 때마다 테스트
  • 테스트가 있어야 해. 테스트 없이 리팩터링하면 그건 리팩터링이 아니라 도박이야

저자들이 쓰는 비유: 리팩터링은 정원 가꾸기와 같아. 잡초를 뽑고, 가지를 치고, 배치를 바꾸는 일상적인 활동이지, 건물을 허무는 게 아니거든.

"테스트는 버그를 찾기 위한 게 아니야. 테스트는 설계 도구지."

이 절의 핵심 주장은 TDD(Test-Driven Development)보다 넓어. TDD의 red-green-refactor 사이클이 좋긴 하지만, 저자들이 말하는 건 **"테스트를 먼저 생각하는 것이 더 좋은 코드를 만든다"**는 점이야.

테스트를 먼저 생각하면:

  • 인터페이스를 먼저 설계하게 돼
  • 결합도가 낮아지지 (테스트하기 어려운 코드 = 결합도 높은 코드)
  • 요구사항을 명확히 이해하게 돼

테스트의 가치:

  • 회귀 방지 — 기존 기능이 안 깨지는지 확인
  • 문서 역할 — 테스트가 코드의 사용법을 보여주지
  • 설계 피드백 — 테스트하기 어려우면 설계가 잘못된 거야

"테스트가 없는 코드는 레거시 코드다"라는 마이클 페더스의 말을 인용하기도 하거든.

20주년 기념판에서 새로 추가된 절이야. 기존의 예제 기반 테스트(example-based test)와 다른 접근이지.

예제 기반: "입력이 [3, 1, 2]이면 출력은 [1, 2, 3]이다" 속성 기반: "어떤 리스트를 정렬하면, 결과의 길이는 원래와 같고, 모든 인접 원소가 오름차순이다"

속성 기반 테스트의 장점:

  • 개발자가 미처 생각 못한 엣지 케이스를 발견해
  • 테스트 프레임워크가 수백 개의 입력을 자동 생성하지
  • 코드의 불변식(invariant)을 검증

Haskell의 QuickCheck이 원조고, 지금은 거의 모든 언어에 포트가 있어. Python의 hypothesis, Java의 jqwik, JavaScript의 fast-check 등.

저자들은 속성 기반 테스트가 예제 기반 테스트를 대체하는 게 아니라 보완하는 거라고 해. 둘 다 쓰라는 거지.

20주년 기념판에서 새로 추가된 보안 관련 절이야. 보안을 전문 보안팀만의 일이라고 생각하지 말라는 메시지거든.

기본 보안 원칙들:

  • 공격 표면을 최소화하라 — 노출되는 인터페이스를 줄여. 안 쓰는 API는 닫아
  • 최소 권한 원칙 — 필요한 권한만 부여해
  • 기본값은 안전하게 — opt-in이 opt-out보다 안전하지
  • 입력을 절대 신뢰하지 마라 — 모든 외부 입력은 검증해. SQL 인젝션, XSS 등
  • 민감 데이터를 암호화하라 — 저장할 때도, 전송할 때도
  • 보안 업데이트를 적용하라 — 의존성 관리. 알려진 취약점 있는 라이브러리 쓰지 마

저자들은 "완벽한 보안은 없다"고 인정하면서도, 기본적인 위생 수칙은 모든 개발자가 따라야 한다고 강조해. **"나는 보안 전문가가 아니니까"**는 변명이 아니야.

이름이 중요해. 이것도 20주년 기념판에서 새로 추가됐지.

좋은 이름은:

  • 의도를 드러내 — d 말고 elapsedDays
  • 맥락을 제공해 — 어디서 쓰이는지 이름만 보고 알 수 있어
  • 거짓말하지 않아 — userList인데 실제로는 Map이면 안 되잖아

이름 짓기가 어려우면 두 가지 가능성이야:

  1. 문제를 충분히 이해하지 못했다
  2. 설계가 잘못됐다

이름이 자연스럽게 떠오르지 않는다는 건 무언가 잘못됐다는 신호지. 파충류의 뇌가 또 보내는 메시지야.

문화적 관습도 중요하거든. 팀에서 쓰는 용어, 도메인의 유비쿼터스 언어(DDD에서 말하는)를 일관되게 사용해. customer, client, user를 섞어 쓰면 혼란이 생기잖아. 하나로 통일해야 해.


정리

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

  1. 찜찜하면 멈춰. 무의식의 경고를 무시하지 마
  2. 우연에 기대지 마. 코드가 왜 동작하는지 설명할 수 있어야 하거든
  3. 테스트는 설계 도구야. 버그 찾기가 아니라 더 나은 코드를 만드는 방법이지