목차12개 항목
출처와 참고
박영록의 위키 글 「Simple Design」(2024.06)을 요약·재구성한 노트다. DRY·KISS·YAGNI 비교는 「fru1tworld/cs-basic 모의 면접 질문 목록」을 점검해 보강했다.
왜 객관적 기준이 필요한가
전문가는 직관으로 일해도 좋은 코드를 쓰고, 그러면서도 자기 설계를 계속 의심한다. 초보자는 직관이 부족한데도 "더 깔끔해 보여서", "가독성이 좋아서" 같은 느낌으로 설계한다. 느낌은 사람마다 달라서 팀 안에서 부딪힌다.
| 표현 | 문제 |
|---|---|
| 가독성, 읽기 쉽다/어렵다 | 자기 눈에 안 들어오면 "읽기 어렵다"가 된다. 주관적으로 평가하고 주관적으로 쓰게 만든다 |
| 깔끔하다, 지저분하다 | 기준이 없어 개선 방향을 주지 못한다 |
| 클린 코드 | 책이 기준을 주지만 모두 동의할 만한 기준은 아니다 |
이런 표현은 본질적 복잡성(essential complexity)을 우발적 복잡성(accidental complexity)으로 착각하게 만든다는 문제도 있다. 우발적 복잡성은 도구·언어·코드 품질로 줄일 수 있지만, 본질적 복잡성은 풀려는 문제 자체에 있어서 코드로는 줄일 수 없다.
좋은 기준은 다음 조건을 갖춘다.
- 냄새가 나는지는 모호함 없이 판단할 수 있다. 초보자에게 지침이 된다.
- 언제, 어떻게 고칠지는 열어 둔다. 전문가를 묶지 않는다.
- 개수가 적다. 복잡성을 다루는 원칙이 복잡하면 그것도 문제다.
Simple Design의 네 규칙
켄트 벡의 원래 문구와 마틴 파울러의 재해석을 합치면 다음과 같다.
| 순서 | 규칙 | 실무에서 |
|---|---|---|
| 1 | 모든 테스트를 통과한다 | 당연해서 따로 말할 필요가 없다 |
| 2 | 중복이 없다 (평행 상속 구조 같은 숨은 중복 포함) | 핵심 |
| 3 | 의도를 드러낸다 | 가장 주관적이라 초보자에겐 부작용이 있다. 빼도 손해가 적다 |
| 4 | 문법 요소가 최소다 | 핵심 |
1과 3을 빼면 한 줄 정의가 남는다.
중복이 없으면서 최소한의 문법 요소로 구성된 코드
최소한의 문법 요소
켄트 벡은 "클래스와 메서드", 파울러는 "요소"라고 했다. 이를 코드 수준에서 셀 수 있게 좁히면 문법 요소가 된다.
- 클래스, 메서드, 상속 계층, 콜 스택 깊이
- 멤버 변수, 로컬 변수, 데코레이터
if분기, 루프. 순환 복잡도(Cyclomatic Complexity)가 느는 것도 문법 요소가 느는 것이다- 여기에 전체 코드 분량(TLoC)까지 줄이면 더 좋다
중복을 없애면 문법 요소는 어느 정도 늘 수밖에 없다. 기준은 같은 중복을 더 적은 요소로 없애는 쪽이다. 메서드 하나 추출로 끝나는 방법이 클래스나 상속 계층을 추가하는 방법보다 낫다.
DRY, KISS, YAGNI
코드 품질 원칙으로 자주 묶어 부르는 세 약어는 Simple Design의 규칙과 겹친다.
| 원칙 | 뜻 | Simple Design과의 관계 | 지나치면 |
|---|---|---|---|
| DRY(Don't Repeat Yourself) | 시스템 안의 모든 지식은 단 하나의 명확하고 권위 있는 표현을 가져야 한다. 『실용주의 프로그래머』에서 나왔다. 대상은 같은 글자의 코드가 아니라 같은 지식(규칙, 계산식, 상수, 스키마)이다 | 「중복이 없다」 | 우연히 모양만 같은 코드를 억지로 합쳐 서로 다른 이유로 바뀌는 코드를 묶는다 |
| KISS(Keep It Simple, Stupid) | 같은 문제를 푸는 방법 중 가장 단순한 것을 고른다. 복잡도는 필요가 증명될 때만 더한다 | 「문법 요소가 최소다」 | 단순함을 핑계로 필요한 추상화나 오류 처리를 빼서, 중복이 쌓인다 |
| YAGNI(You Aren't Gonna Need It) | 지금 필요하지 않은 기능·확장 지점·설정은 만들지 않는다. 익스트림 프로그래밍(XP)에서 나왔다 | 현재 요구에 충실한 설계 | 이미 알고 있는 요구까지 미뤄, 곧 다시 뜯어고친다 |
셋은 서로를 견제한다. DRY만 밀면 추상화가 늘어 KISS와 부딪치고, 그 추상화가 미래 대비라면 YAGNI에 어긋난다. Simple Design은 이 충돌을 「같은 중복을 더 적은 요소로 없앤다」는 한 기준으로 정리한다. 시스템 설계 차원의 KISS는 시스템 설계의 품질 속성과 트레이드오프의 약어 표에도 있다.
중복은 언제 없애나
"미래에 갈라질 수 있으니 남겨 둔다"는 YAGNI 위반이다
「Don't DRY Your Code Prematurely」 류의 주장은 "지금은 같아도 나중에 로직이 달라질 수 있다"를 근거로 든다. Simple Design 관점에서는 이것이 오히려 YAGNI에 어긋난다. 아직 오지 않은 변화를 근거로 설계하는 것이기 때문이다. 좋은 코드는 현재에 충실한 Simple Design이지 미래 변화에 대비한 유연함이 아니다.
def assert_future(deadline):
if deadline <= datetime.now():
raise ValueError('blah')
return deadline
set_task_deadline(assert_future(datetime(2024, 3, 12)))중복을 없애더라도 함수 하나로 끝내는 식으로 문법 요소를 최소화한다.
남겨 둘 이유는 비용뿐이다: 밥 먹기 전에 설거지하기
다시 만질 일이 없거나 영향 범위가 좁은 코드는 고쳐도 이득이 없다. 그래서 중복이 실제로 해를 끼치기 시작할 때 없앤다. 설거지의 이득은 다음 끼니에야 생기니, 다음 끼니 직전에 하는 것과 같다.
이 판단은 작은 단위로 한다.
flowchart LR A["A"] --> B["B"] --> C["C"] --> D["D<br/>(최종 결과, 다른 곳에서 안 씀)"] B --> D
- A, B, C: 다른 모듈이 기대므로 다 만들면 바로 리팩터링
- D: 의존하는 곳이 없으니 일단 둔다 → 연결이 생길 때 리팩터링
초보자라면 가끔은 극단적으로
비용 저울질은 개선 비용을 가늠할 수 있어야 가능하다. 그래서 초보자에게는 중복을 하나도 남기지 않는 극단적인 품질 추구를 권한다.
- 유지보수 단계의 회사가 스타트업보다 많다. 스타트업도 몇 달 만에 "갈아엎자"는 말이 나오거나, 핵심 기능 하나 넣다가 버그가 쏟아지는 일이 흔하다.
- 극단까지 밀어 보면 여러 방법을 탐색하게 되고 그 과정에서 배운다.
- 비용과 타협하는 리팩터링은 스스로 코드 품질 전문가가 됐다고 느낄 때 시작해도 늦지 않다.
코드 품질은 속도를 위한 것이다
- 품질을 높이는 이유는 더 빨리 개발하기 위해서다. 품질 때문에 일정이 늘어난다면 품질을 잘못 이해한 것이다.
- 품질을 희생해 빨라지는 건 맥락이 머릿속에 다 들어 있을 때뿐이다. 보통 30분, 길어야 몇 시간 분량이다. 그보다 큰 일에서는 자기가 쓴 나쁜 코드를 다시 읽으며 손해를 보기 시작한다.
- 어떤 냄새는 하루 이틀짜리 작업도 늦춘다. 어떤 중복이 해롭고 어떤 중복이 무해한지 가리는 것이 곧 실력이다.
- 방법론은 성과를 보장하지 않고 확률을 높인다. 과학적 방법론이 과학적 성과를 보장하지 않아도 과학자는 그것을 쓴다. 품질이 좋다고 성공하진 않지만, 품질이 나빠 기능 추가에 실패하고 정체되는 회사는 많다.
더 구체적인 기준이 필요할 때: 나쁜 냄새
Simple Design만으로 부족하면 『리팩터링』의 나쁜 냄새(code smell) 목록을 찾아본다. 머릿속에 다 담고 다니기엔 많지만, 냄새를 맡았을 때 "이건 무슨 냄새고 어떻게 고치나"를 찾아보고 설명하는 데는 아주 좋다.
본질적 복잡성에도 도전한다
본질적 복잡성 때문에 코드가 복잡하다면 코드 대신 비즈니스 규칙을 건드린다. 개발자가 이해하기 어려운 규칙이면 운영 실무자도 이미 힘들어하고 있다.
| 단계 | 환불 규칙 사례 |
|---|---|
| 첫 미팅 | 8개 정도로 정리됨 |
| 개발 중 | 담당자도 몰랐던 규칙이 계속 나와 30개까지 늘어남 |
| 뜯어보니 | 월 수백~수천 원 손해면 되거나 아무도 손해 보지 않는 규칙이 많음 |
| 협의 후 | 10여 개로 줄임 |
요구사항은 담당자마다 다른 생각에서 나와 끝없이 불어난다. 규칙 전체를 보는 개발자가 함께 다듬으면 비즈니스 비용도 준다. 냄새의 원인이 본질적이든 우발적이든, 맡았으면 고쳐 본다.
정리
- "가독성"·"깔끔함" 같은 느낌 대신 판단 가능한 기준으로 이야기한다.
- 기준은 한 줄: 중복이 없고, 문법 요소가 최소인 코드. 숨은 중복도 찾는다.
- DRY는 지식의 중복을, KISS는 불필요한 복잡도를, YAGNI는 오지 않은 요구를 경계한다. 셋이 부딪치면 더 적은 요소로 중복을 없애는 쪽을 고른다.
- 미래 변화를 근거로 중복을 남기지 않는다. 남긴다면 이유는 비용이고, 해를 끼치기 시작할 때 없앤다.
- 품질은 속도를 위한 수단이다. 부족하면 나쁜 냄새 목록을, 코드로 안 되면 비즈니스 규칙을 본다.
관련 노트: 리팩터링 원칙, 코드 리뷰 원칙과 체크리스트, 프론트엔드에서의 TDD, 삭제하기 쉬운 코드, 시스템 설계의 품질 속성과 트레이드오프