topic★★★★★난이도 · 약 20분
레이어드 · 헥사고날 · 클린 아키텍처
"도메인을 프레임워크·DB로부터 격리" — 변화에 강한 코드 구조 3단 진화.
#레이어드#헥사고날#클린아키텍처#관심사분리
왜 배우는가
AI가 만든 코드가 "이 파일이 API고, 이 파일이 DB고, 이 파일이 비즈니스 규칙" 섞여있으면 리팩토링 지옥. 계층 분리 감각이 있으면 "이 부분은 도메인 레이어로 빼줘" 같은 지시가 가능.
모든 아키텍처의 공통 목표는 '관심사 분리(Separation of Concerns)'. 3단계로 진화한다.
| 스타일 | 핵심 아이디어 | 계층 |
|---|---|---|
| 레이어드 (N-Tier) | 위→아래 단방향 | UI → Application → Domain → Infrastructure |
| 헥사고날 (Ports & Adapters) | 도메인을 중심, 외부는 '어댑터' | 외부(DB·API·UI) ↔ Ports ↔ 도메인 |
| 클린 (Uncle Bob) | 의존성은 안쪽으로만 | Entities ← Use Cases ← Adapters ← Frameworks |
공통 원리 — 의존성 규칙. "외부(프레임워크·DB·UI)가 내부(비즈니스 규칙)를 의존하지, 반대로는 절대 아님." 이 한 줄이 클린 아키텍처의 핵심.
typescript
// ━━━ 나쁜 예: 모든 계층이 섞임 ━━━
// app/api/users/route.ts
export async function POST(req: Request) {
const data = await req.json();
if (!data.email.includes("@")) return new Response("bad", { status: 400 });
const { rows } = await pg.query("INSERT INTO users ...", [data.email]);
await sendWelcomeEmail(data.email); // SMTP 호출
return Response.json(rows[0]);
}
// → HTTP·검증·DB·이메일이 한 함수. 테스트 불가능, 재사용 불가.
// ━━━ 좋은 예: 계층 분리 ━━━
// domain/user.ts (순수 도메인 — 어떤 DB·HTTP도 모름)
export class User {
constructor(public email: string, public name: string) {
if (!email.includes("@")) throw new Error("invalid email");
}
}
// application/use-cases/register-user.ts
export async function registerUser(
email: string, name: string,
repo: UserRepository,
notifier: Notifier,
): Promise<User> {
const user = new User(email, name);
await repo.save(user);
await notifier.welcome(user);
return user;
}
// infrastructure/db/postgres-user-repo.ts
export class PgUserRepository implements UserRepository {
async save(u: User) { await pg.query("INSERT ...", [u.email, u.name]); }
}
// app/api/users/route.ts (얇은 HTTP 어댑터)
export async function POST(req: Request) {
const body = await req.json();
const user = await registerUser(body.email, body.name, pgRepo, smtpNotifier);
return Response.json(user);
}도메인 계층은 테스트가 쉬움(DB·네트워크 목킹 안 해도 됨). 나중에 Postgres → MongoDB로 바꿔도 use-case는 안 바뀐다.
현실 타협점. 바이브코더 대부분은 Next.js + Supabase. 풀 클린 아키텍처는 과함. 최소한 "도메인 로직은 route.ts 바깥의 순수 함수로" 규칙만 지켜도 70% 효과. AI에게 "이 비즈니스 로직을 lib/domain에 순수 함수로 분리" 지시로 자동화.
실기 드릴 1문항
check_circle실기 드릴 · OX
클린 아키텍처에서 도메인 계층이 데이터베이스 드라이버를 직접 import해도 된다.