Search

부록_원칙들의_통합

부록. 원칙들의 통합

프로그래밍을 배우다 보면 수많은 원칙을 마주해요. SRP, OCP, DIP, IoC, DRY, KISS… 각각 따로 외우다 보면 머리가 복잡해져요.
이 부록에서는 하나의 관점을 제시해요:
대부분의 프로그래밍 원칙은 What/How 분리의 변형이에요.

A.1 하나의 원리, 여러 표현

What/How 분리의 핵심

1장에서 다룬 정의를 다시 볼게요:
What = 무엇을 하는가 (의도, 역할, 인터페이스) How = 어떻게 하는가 (구현, 세부사항, 메커니즘)
Plain Text
복사
이 분리가 좋은 설계의 핵심이에요. 그리고 역사적으로 발명된 수많은 원칙들은 이 분리를 다른 각도에서 표현한 것이에요.
What/How 분리 │ ┌────────┬───────┬───┴───┬───────┬────────┐ │ │ │ │ │ │ SRP OCP DIP IoC 응집도 결합도
Plain Text
복사

A.2 SRP: 단일 책임 원칙

정의

“하나의 모듈은 하나의 이유로만 변경되어야 한다” — Robert C. Martin

What/How로 해석

SRP = What을 하나로 유지하라
// SRP 위반: What이 두 개 class UserManager { saveToDatabase(user) { /* DB 저장 */ } formatForDisplay(user) { /* 화면 표시 */ } } // 이 클래스는 "DB 저장"과 "화면 표시"라는 두 가지 What을 가져요 // SRP 준수: What이 하나씩 class UserRepository { save(user) { /* DB 저장 */ } } class UserPresenter { format(user) { /* 화면 표시 */ } }
JavaScript
복사
“하나의 책임”이란 “하나의 What”이에요. 클래스가 여러 What을 가지면, 각 What이 변경될 때마다 클래스가 바뀌어요.

체크 질문

이 클래스/함수의 What은 몇 개인가요?
What을 한 문장으로 설명할 수 있나요?
“~하고 ~한다”라면 What이 두 개예요

A.3 OCP: 개방-폐쇄 원칙

정의

“확장에는 열려 있고, 수정에는 닫혀 있어야 한다” — Bertrand Meyer

What/How로 해석

OCP = What은 확장 가능, How는 수정 불가
// OCP 위반: 새 타입 추가 시 How를 수정해야 함 function calculateArea(shape) { if (shape.type === 'circle') { return Math.PI * shape.radius ** 2; } else if (shape.type === 'rectangle') { return shape.width * shape.height; } // 삼각형 추가하려면 여기를 수정해야 해요 ❌ } // OCP 준수: What(인터페이스)만 확장 interface Shape { area(): number; // What: 넓이를 구한다 } class Circle implements Shape { area() { return Math.PI * this.radius ** 2; } // How } class Rectangle implements Shape { area() { return this.width * this.height; } // How } // 삼각형 추가: 기존 코드 수정 없이 확장 class Triangle implements Shape { area() { return 0.5 * this.base * this.height; } // 새 How }
JavaScript
복사
“확장에 열림”: 새로운 How를 추가할 수 있어요 “수정에 닫힘”: 기존 How를 건드리지 않아요
What(인터페이스)이 안정적이면, How(구현)는 자유롭게 추가돼요.

체크 질문

새 기능 추가 시 기존 코드를 수정해야 하나요?
What(인터페이스)과 How(구현)가 분리되어 있나요?

A.4 DIP: 의존성 역전 원칙

정의

“고수준 모듈이 저수준 모듈에 의존하면 안 된다. 둘 다 추상화에 의존해야 한다.” — Robert C. Martin

What/How로 해석

DIP = How가 아닌 What에 의존하라
// DIP 위반: What이 How에 의존 class OrderService { constructor() { this.db = new MySQLDatabase(); // How(MySQL)에 직접 의존 } } // DIP 준수: What이 What에 의존 interface Database { // What: 데이터를 저장한다 save(data): void; } class OrderService { constructor(db: Database) { // What에 의존 this.db = db; } } // How는 별도로 구현 class MySQLDatabase implements Database { ... } class PostgresDatabase implements Database { ... }
JavaScript
복사
“추상화에 의존”이란 “What에 의존”이에요.
구체적인 How(MySQL, Postgres)가 아니라, 추상적인 What(Database 인터페이스)에 의존하면 교체가 쉬워져요.

체크 질문

이 모듈은 구체 클래스(How)에 의존하나요?
인터페이스(What)에 의존하도록 바꿀 수 있나요?

A.5 IoC: 제어의 역전

정의

“프레임워크가 내 코드를 호출한다” (Hollywood Principle) “Don’t call us, we’ll call you”

What/How로 해석

IoC = What을 먼저 정하고, How를 나중에 채운다
// IoC 없음: 내가 직접 How를 호출 class App { run() { const db = new Database(); const service = new UserService(db); service.start(); } } // IoC 적용: 프레임워크가 How를 주입 // 나는 What(인터페이스)만 정의 @Injectable() class UserService { constructor(private db: Database) {} // What만 선언 } // 프레임워크가 How(실제 구현)를 주입
JavaScript
복사
4장의 Wishful Thinking과 같은 원리예요:
1.
“이런 게 있으면 좋겠다” (What 선언)
2.
나중에 채운다 (How 구현/주입)
IoC 컨테이너는 What과 How의 연결을 자동화해요.

체크 질문

내가 How를 직접 생성하고 있나요?
What만 선언하고 How는 외부에서 주입받을 수 있나요?

A.6 응집도와 결합도

정의

응집도(Cohesion): 모듈 내부 요소들이 얼마나 관련 있는가
결합도(Coupling): 모듈 간 의존성이 얼마나 강한가
좋은 설계 = 높은 응집도 + 낮은 결합도

What/How로 해석

응집도 = What의 일관성
// 낮은 응집도: 여러 What이 섞임 class Utils { formatDate(date) { ... } // What: 날짜 포맷 getTax(amount) { ... } // What: 세금 validateEmail(email) { ... } // What: 이메일 검증 } // 높은 응집도: 하나의 What에 집중 class DateFormatter { format(date) { ... } parse(str) { ... } isValid(date) { ... } } // 모든 메서드가 "날짜 처리"라는 하나의 What에 기여해요
JavaScript
복사
결합도 = How에 대한 의존 정도
// 높은 결합도: How에 직접 의존 class OrderService { process(order) { const tax = order.items.reduce((sum, item) => sum + item.price * 0.1, 0); // 세금 계산 How가 여기에 } } // 낮은 결합도: What에만 의존 class OrderService { constructor(private taxCalculator: TaxCalculator) {} process(order) { const tax = this.taxCalculator.calculate(order); // What만 호출 } }
JavaScript
복사
How(세금 계산 로직)가 변경되어도, OrderService는 영향받지 않아요.

체크 질문

이 모듈의 모든 요소가 같은 What에 기여하나요? (응집도)
다른 모듈의 How가 변경되면 이 모듈도 변경해야 하나요? (결합도)

A.7 통합 정리

원칙
What/How 관점
SRP
What을 하나로 유지
OCP
What은 확장, How는 수정 금지
DIP
How가 아닌 What에 의존
IoC
What 선언, How는 나중에
응집도
What의 일관성
결합도
How 의존 최소화
공통점: 모두 What/How의 명확한 분리를 다른 방식으로 표현해요.

실전 적용

코드 리뷰나 설계 시 이 질문을 던져보세요:
1. What과 How가 분리되어 있는가? 2. What이 명확하고 안정적인가? 3. How만 바꿔도 동작하는가?
Plain Text
복사
이 세 질문에 Yes라면, 위의 모든 원칙을 어느 정도 만족하고 있을 확률이 높아요.

A.8 마무리: 원칙 100개 < 원리 1개

처음에는 SRP, OCP, DIP… 각각 외우려 해요. 하지만 본질을 이해하면 하나의 원리로 수렴해요:
What과 How를 분리하라. What은 안정적으로, How는 유연하게.
이 원리를 체화하면, 새로운 원칙이 나와도 당황하지 않아요. “이것도 결국 What/How 분리의 변형이구나”라고 보이기 때문이에요.
이것이 추상화의 힘이에요. 많은 것을 적은 것으로 이해하는 능력.

A.9 What 카탈로그

자주 등장하는 What의 이름들이에요. 이름을 알면 의도가 즉시 전달돼요.
What
의도
예시
Form
입력 → 검증 → 제출
useForm({ validate, onSubmit })
Query
외부 데이터 조회
useQuery(key, fetcher)
Mutation
외부 데이터 변경
useMutation(updateUser)
List
동질 항목 나열
<List items={users} renderItem={...} />
Guard
조건부 진입 차단
if (!auth) return <Login />
Validator
입력 유효성 판정
validate(email): boolean
Formatter
값 → 표시 형태 변환
formatCurrency(1000) → "₩1,000"
Calculator
계산 로직 캡슐화
calculateTax(order): number
Handler
이벤트 처리
handleSubmit(e) { ... }
Provider
하위에 값 공급
<AuthProvider>{children}</AuthProvider>
사용법: 코드를 볼 때 “이 함수/컴포넌트의 What이 위 목록 중 뭐지?”라고 물어보세요. 즉시 답할 수 없다면 What이 불명확한 거예요.

A.10 추상화 신호 사전

“언제 추상화해야 하지?”에 대한 답이에요. 이 신호가 보이면 추상화를 고려하세요.

흩어짐 신호 (응집도 문제)

신호
의미
대응
같은 조건 3곳+
결정이 흩어짐
Guard로 한 곳에 모으기
한 변경에 파일 3개+ 수정
Shotgun Surgery
관련 코드 한 모듈로
데이터만/로직만 따로
데이터-로직 분리
클래스나 훅으로 묶기
파일명 ≠ 실제 내용
What 불명확
이름 재정의

얽힘 신호 (결합도 문제)

신호
의미
대응
A 다음 B 필수
시간축 얽힘
순서를 캡슐화
라이브러리 코드가 곳곳에
프레임워크 침투
어댑터로 격리
주석 없이 이해 불가
암묵적 의존
명시적으로 드러내기
비즈니스 로직이 UI에
도메인 얽힘
도메인 레이어 분리
사용법: 코드 리뷰 시 “이 신호가 있나?”로 스캔하세요. 신호가 보이면 What/How 분리 기회예요.

참고 자료

Martin, R. C. (2003). Agile Software Development: Principles, Patterns, and Practices
Meyer, B. (1988). Object-Oriented Software Construction
Gamma et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software