부록. 원칙들의 통합
프로그래밍을 배우다 보면 수많은 원칙을 마주해요. 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
