본문으로 건너뛰기
클린·헥사고날·DDD — 추상화를 풀스택 구조로

생성 수정

IBM * 레드햇 AX 과정 · 10강

클린·헥사고날·DDD — 추상화를 풀스택 구조로

2026년 5월 25일(월) ~ 5월 29일(금)

이 강의 문제풀이 — 배운 내용을 주관식 문답으로 확인해보세요.풀어보기 →

8. ⭐ CLEAN HEXAGONAL DDD — 세 아키텍처가 한 폴더 트리에

8.1 폴더 구조

지금까지 배운 클린 아키텍처와 헥사고날, 3주차에서 이름만 나왔던 DDD가 하나의 프로젝트 트리에서 만납니다.

PROJECT_ROOT/
├── adapter/          ← 헥사고날 (Hexagonal)
│   ├── __pycache__/
│   ├── inbound/      →|
│   └── outbound/     |←
├── app/              ← 클린 (Clean)
│   ├── __pycache__/
│   ├── dtos/
│   ├── ports/        🔒↔🔒 Port Gate
│   │   ├── input/    →|
│   │   └── output/   |←
│   └── use_cases/
│       └── __init__.py
└── domain/           ← DDD (Domain-Driven Design)

세 최상위 폴더가 각각 하나의 아키텍처 사조를 담당합니다.

폴더사조담당축 2에서의 위치
adapter/헥사고날기술과 맞닿는 바깥 껍질가장 바깥
app/클린유스케이스와 포트 (계약과 흐름)중간
domain/DDD순수한 비즈니스 개념가장 안쪽

하위 폴더의 역할

경로정체추상/구상
adapter/inbound/Driving Adapter — 라우터, CLI구상
adapter/outbound/Driven Adapter — DB 리포지토리 구현체구상
app/dtos/계층 간 데이터 전달 그릇(데이터)
app/ports/input/Input Port — 진입 계약추상
app/ports/output/Output Port — 출구 계약추상
app/use_cases/비즈니스 흐름 구현구상(비즈니스적)
domain/엔티티·값 객체·도메인 규칙(핵심)

💡 3주차 9장에서 배운 __init__.py 가 여기서 다시 나옵니다. app/use_cases/__init__.py 는 빈 파일이어도 "이 폴더는 파이썬 패키지다" 라는 선언 자체가 역할입니다. 트리의 __pycache__/ 는 파이썬이 자동으로 만드는 컴파일 캐시이므로 .gitignore 대상입니다.

8.2 CORE PRINCIPLES — 핵심 원칙

  • 🔊 Adapter Pattern 사용 (Adapter Pattern Used) — 1.4절에서 배운 GoF 어댑터 패턴이 아키텍처 규모로 확대된 것입니다.
  • 문을 관리함 (Manages Gates) — Ports와 Adapters가 게이트웨이 역할을 합니다.

8.3 ⭐ Port Gate — "바로 가는 곳에 문을 세운다"

이 구조의 규칙은 한 줄입니다.

❌ 금지:   bound (external)  ──✕──►  use_case (core)
⭕ 대신:   bound (external) → adapter → input_port (gate) → use_case (core)

⭐ "바로 가는 곳에 Port라는 문을 세워 통과"

외부에서 코어로 직접 가려는 경로를 막고, 그 자리에 Port라는 문을 세워 반드시 통과하게 합니다. 이 행위 자체가 의존성 역전을 심는 것(Placing dependency inversion) 입니다.

문을 세워 얻는 것설명
교체 가능성문의 규격만 맞으면 문 바깥은 뭐든 갈아끼울 수 있습니다 (OCP)
테스트 가능성문 바깥에 가짜를 세워도 코어는 눈치채지 못합니다 (LSP)
오염 차단문 안쪽에 FastAPI·SQLAlchemy 같은 단어가 들어오지 못합니다 (의존성 규칙)

8.4 🗂 SOLID 중 ISP 해당

이 구조에서 특별히 강조되는 SOLID 원칙은 ISP(Interface Segregation Principle, 인터페이스 분리 원칙) 입니다.

왜 하필 ISP인가?

  • ports/ 아래를 input/ 과 output/ 으로 갈라 놓았기 때문입니다.
  • 만약 하나의 거대한 Port 인터페이스에 진입 메서드와 저장 메서드를 다 넣었다면, 라우터는 쓰지도 않는 save() 에 의존하게 되고, DB 리포지토리는 쓰지도 않는 handle_request() 에 의존하게 됩니다.
  • 각 어댑터가 자신에게 꼭 필요한 최소한의 진입점에만 결합 — 그것이 ISP이고, 그래서 문이 하나가 아니라 input/ 과 output/ 두 개입니다.

⚠️ 흔한 실수: ports/ 폴더를 만들어놓고 그 안에 파일을 하나만 두는 것. 문을 세우긴 했는데 문이 하나면 ISP가 아닙니다. 들어오는 문과 나가는 문은 반드시 나눕니다.

8.5 실제 파일로 보는 한 바퀴

이번 주 실습에서 만든 흐름을 이 구조에 대입하면 이렇습니다.

adapter/inbound/api/v1/james_router.py     [구상]  ← HTTP 요청 수신, DTO로 변환
              ↓ 호출
app/ports/input/james_use_case.py          [추상]  ← "이 시스템은 무엇을 할 수 있는가"
              ↑ 구현
app/use_cases/james_command.py             [구상]  ← 비즈니스 흐름 실행
              ↓ 호출
app/ports/output/james_repository.py       [추상]  ← "이 시스템은 무엇을 저장해야 하는가"
              ↑ 구현
adapter/outbound/.../james_pg_repository.py [구상] ← 실제 PostgreSQL 접근

화살표 방향을 다시 보세요. ↓ 호출 과 ↑ 구현 이 번갈아 나옵니다.

  • ↓ 는 제어의 흐름입니다.
  • ↑ 는 의존성의 방향입니다.

이것이 7.4절에서 배운 "흐름과 의존성이 어긋난다" 를 파일 경로로 본 모습입니다.


9. ⭐ 추상화하는 근본적인 이유

9.1 한 문장

지금까지 Port, 인터페이스, 추상 클래스를 계속 만들었습니다. 그런데 왜 만드는가? 답은 하나입니다.

⭐ "변화의 중심에서 내 코드를 보호하기 위해서."

9.2 구상(How)과 추상(What)

구상 (Concretion)추상 (Abstraction)
질문How — 어떻게 하는가What — 무엇을 하는가
예"MySQL에 연결해서 유저 테이블에 인서트한다""유저 정보를 저장한다"
성격언제든지 바뀔 수 있는 기술적 세부사항비즈니스의 본질, 변하지 않는 규칙
수명짧음 (기술 트렌드에 따라)김 (사업이 존재하는 한)

소프트웨어는 반드시 변합니다. 만약 내 핵심 로직이 상대방의 구상 클래스를 직접 바라보고 있다면(의존하고 있다면), 상대방 코드가 바뀔 때 내 코드도 도미노처럼 깨집니다.

중간에 추상화라는 방파제를 세워두면, 방파제 뒤의 구체적인 기술이 어떻게 바뀌든 내 핵심 로직은 안전하게 보호됩니다.

   [ 파도 = 기술 변화 ]  MySQL → PostgreSQL → MongoDB
   ███████ 방파제 = 추상(Port) ███████
   [ 항구 = 내 비즈니스 로직 ]  "유저 정보를 저장한다" ← 파도가 쳐도 그대로

💡 방파제는 파도를 없애는 게 아니라 막는 것입니다. 기술 변화를 막을 수는 없지만, 그 변화가 어디까지 들어오게 할지는 우리가 정합니다. 그 선을 긋는 행위가 추상화입니다.

9.3 파이썬으로 추상 만들기 — abc 모듈

파이썬에서는 abc(Abstract Base Class) 모듈로 추상 클래스를 만듭니다.

from abc import ABC, abstractmethod


class JamesRepository(ABC):
    """Output Port — 코어가 선언하는 저장 계약."""

    @abstractmethod
    async def save_all(self, records: list[dict]) -> int:
        """탑승자 레코드를 저장하고 저장된 건수를 반환한다."""
        ...

    @abstractmethod
    async def find_page(self, page: int, size: int) -> list[dict]:
        """페이지 단위로 탑승자 목록을 조회한다."""
        ...
요소하는 일
ABC이 클래스를 추상 기반 클래스로 만든다. 직접 인스턴스화할 수 없게 된다
@abstractmethod"이 메서드는 자식이 반드시 구현해야 한다"고 문법적으로 강제한다
... (또는 pass)본문 없음. 여기엔 How가 오면 안 된다

구현체는 이렇게 됩니다.

class JamesPgRepository(JamesRepository):   # Outbound Adapter [구상]
    def __init__(self, session):
        self.session = session

    async def save_all(self, records: list[dict]) -> int:
        # 여기서만 SQLAlchemy / PostgreSQL 이라는 단어가 등장한다
        ...

    async def find_page(self, page: int, size: int) -> list[dict]:
        ...

⚠️ 자식이 메서드를 안 만들면? 인스턴스를 만드는 순간 TypeError: Can't instantiate abstract class ... 가 납니다. 이 에러는 버그가 아니라 안전장치입니다.

9.4 ⭐ 추상화가 나타나는 5곳 — 전부 같은 목적

이번 주에 배운 것들이 서로 다른 이름을 달고 있지만 전부 추상화입니다.

① 객체지향의 추상화 (Abstraction)

  • 개념 — 복잡한 현실 세계에서 불필요한 세부 사항을 제거하고, 우리 시스템에 필요한 핵심 본질(행동)만 추출하여 표준 규격을 만드는 것입니다.
  • 파이썬 구현 — abc 모듈의 ABC와 @abstractmethod로 "이 메서드는 자식이 반드시 구현해야 한다"고 문법적으로 강제하는 추상 클래스를 만듭니다.

② SOLID의 의존성 역전 원칙 (DIP)

  • 개념 — 고수준 모듈(비즈니스 로직)과 저수준 모듈(DB, 파일, 외부 API)이 서로를 직접 바라보지 말고, 둘 다 추상화(인터페이스)에 의존해야 한다는 설계 규칙입니다.
  • 목적 — 하위 기술(저수준)의 변경이 상위 비즈니스(고수준)를 흔들지 못하게 소스코드의 의존 방향을 역전시킵니다.

③ 클린 아키텍처의 포트 (Port)

  • 개념 — 소프트웨어의 내부(비즈니스 영역)와 외부(인프라 영역)를 넘나들 때, 경계선에 세워두는 추상 클래스 통로입니다.
  • 목적 — 내부 영역이 외부의 구체적 기술(웹 프레임워크, 특정 DB 라이브러리)에 오염되는 것을 막기 위해, 내부 영역이 정의한 추상 클래스(Port)를 외부 영역(Adapter)이 상속받아 구현하도록 격리합니다.

④ GoF 디자인 패턴 (예: 전략 패턴)

  • 개념 — 실행 중에 상황에 따라 구체적인 알고리즘(구상)을 동적으로 갈아 끼울 수 있도록, 알고리즘들을 하나의 추상 클래스로 묶어두는 실전 설계 공식입니다.
  • 목적 — 새로운 기능(새로운 구상 클래스)이 추가되더라도 기존의 클라이언트 코드를 단 한 줄도 수정하지 않기 위함입니다 (OCP 달성).

⑤ 데이터베이스(RDB)의 상속 — 식별 관계

  • 개념 — 클래스의 상속 개념을 테이블 구조로 바꿀 때, 자식 테이블이 부모 테이블의 기본키(PK)를 그대로 물려받아 자신의 PK이자 외래키(FK)로 사용하는 구조입니다.
  • 목적 — 데이터의 중복을 막고, 데이터베이스 레벨에서 부모-자식 간의 물리적 연결성(무결성) 을 보장합니다.
#자리추상에 해당하는 것구상에 해당하는 것
①객체지향추상 클래스 (ABC)구현 클래스
②SOLID인터페이스고수준·저수준 모듈
③클린 아키텍처PortAdapter
④GoF 전략 패턴Strategy 인터페이스구체 전략들
⑤RDB 식별관계부모 테이블의 PK자식 테이블

⭐ 다섯이 전부 "변할 것과 안 변할 것을 갈라 놓고, 안 변할 쪽에 계약을 두는" 동일한 동작입니다. 그래서 하나를 이해하면 나머지 넷이 저절로 읽힙니다. 새 용어를 만나면 "여기서 안 변하는 쪽은 무엇인가" 를 물으세요.

9.5 한 줄 요약

추상화로 규격을 정의하고, DIP 원칙에 따라 의존성을 꺾고, 클린 아키텍처(Port)나 패턴으로 코드를 격리하는 것 — 모두 "구체적인 기술(구상)이 바뀌어도 내 핵심 로직을 안 터지게 만들겠다" 는 하나의 목적을 위한 수단들입니다.


10. 스코프 계층 구조 — 같은 원리를 함수 크기로 축소하기

10.1 왜 스코프가 아키텍처 장에 나오는가

지금까지 우리는 시스템 크기의 경계(Port, 계층, 어댑터)를 배웠습니다. 그런데 함수 하나 안에도 똑같은 경계가 있습니다. 이름만 다를 뿐입니다.

  • 시스템 경계에서는 그것을 Port라고 부릅니다.
  • 함수 경계에서는 그것을 매개변수(Parameter) 라고 부릅니다.

이 장의 판별 축 5(좁은 스코프 ↔ 넓은 스코프)가 여기서 쓰입니다.

10.2 동심원 — 최내곽에서 최외곽으로

        🌐 외부층: 컨텍스트 · 전역 변수 · 환경 변수
        ┌──────────────────────────────────────┐
        │   📚 중간층: 클래스 · 필드           │
        │   ┌──────────────────────────────┐   │
        │   │  중심: 메서드 { }            │   │
        │   │      └─ 로컬 변수            │   │
        │   └──────────────────────────────┘   │
        └──────────────────────────────────────┘
              위성: 매개변수 ( )  /  추상 클래스

❶ 최내곽: 메서드 & 로컬 변수

  • 로컬 변수 (Local Variable) — 함수·메서드 블록 { } 내부에서만 유효한 가장 좁은 스코프. 3주차에서 배운 대로 Stack 영역에 있고 함수가 끝나면 사라집니다.
  • 메서드 (Method) — 클래스 내부의 함수. 로컬 변수를 감싸는 경계선 역할을 합니다.

❷ 중간층: 클래스, 필드, 인스턴스 변수

  • 클래스 (Class) — 데이터(필드)와 행위(메서드)를 하나로 묶는 틀입니다.
  • 필드 (Field) = 인스턴스 변수 (Instance Variable) — 클래스 내부에 선언되어, 객체(인스턴스)가 살아있는 동안 유지되는 변수입니다.

⚠️ 보완해야 할 지점: '필드'와 '인스턴스 변수'를 별개처럼 그리는 경우가 있는데, 엄밀히 말하면 인스턴스 변수는 필드의 한 종류입니다. 필드에는 인스턴스 변수 말고도 클래스 변수(정적 변수, static) 가 포함됩니다.

종류소속개수3주차 메모리 3영역
인스턴스 변수각 인스턴스인스턴스마다 하나씩Heap
클래스 변수클래스 자체전체에 하나Method Area

❸ 외부층: 컨텍스트, 전역 변수, 환경 변수

  • 컨텍스트 (Context) — 애플리케이션이 실행되는 '문맥'이나 '상태'를 의미합니다. 프레임워크 레벨에서 관리되는 경우가 많아 클래스 바깥에 위치합니다.
  • 전역 변수 (Global Variable) — 애플리케이션 프로세스 전체에서 접근 가능한 변수입니다.
  • 환경 변수 (Environment Variable) — 운영체제(OS)나 컨테이너 레벨에서 주입되는 변수입니다. 소스 코드(전역 변수)보다 더 바깥쪽인 시스템 레벨에 존재합니다. DB 접속 정보 등이 여기 해당합니다.

⭐ 환경 변수가 전역 변수보다 바깥인 이유 — 전역 변수는 코드 안에 있어 코드를 고쳐야 값이 바뀝니다. 환경 변수는 코드 밖에 있어 코드를 안 고치고도 바뀝니다. 그래서 개발 환경과 운영 환경에 다른 값을 넣을 수 있습니다. 4.2절에서 core/ 가 Docker와 연결되는 이유가 이것입니다.

10.3 프레임워크별 실제 매핑 (Next.js & FastAPI)

이 계층 구조가 실제 웹 기술 스택에서 어떻게 구현되는지 매핑한 표입니다. 이번 주에서 가장 실용적인 표입니다.

개념 (스코프 층)프레임워크 A (Next.js / Frontend)프레임워크 B (FastAPI / Backend)
환경 변수 / DB.env.local (API 키, 백엔드 주소).env (PostgreSQL 접속 URI, JWT 비밀키)
컨텍스트React Context API, Next.js MiddlewareFastAPI의 Request 객체, Dependency Injection Context
전역 변수Zustand/Redux 스토어, 전역 window 객체Python 모듈 파일 레벨에 선언된 변수 (App 인스턴스 등)
클래스 / 필드React 클래스 컴포넌트 또는 Custom Hook / ClassPydantic 데이터 모델 (Schema), SQLAlchemy ORM 클래스
메서드 / 로컬 변수컴포넌트 내부 함수, useState 지역 변수라우터 함수(@app.get) 내부의 비즈니스 로직 및 변수

💡 이 표는 "이 값을 어디에 둬야 하지?" 에 답할 때 씁니다. 이 요청에서만 쓰는 값 → 로컬 변수 / 이 객체가 사는 동안 유지 → 필드 / 요청 전체를 관통 → 컨텍스트 / 환경마다 달라야 함 → 환경 변수. 필요한 것보다 넓은 스코프에 두는 것이 거의 모든 버그의 시작입니다.

10.4 외곽 키워드 배치 — 매개변수와 추상 클래스

동심원의 바깥에 따로 붙는 두 개념이 있습니다. 위치가 중요합니다.

매개변수 (Parameter)

  • 위치 — 메서드(함수)의 관문 역할을 하므로, '메서드'의 경계선(괄호 안) 에 위치합니다.
  • 외부에서 값을 받아 내부 로컬 변수처럼 활용되는 징검다리입니다.

추상 클래스 (Abstract Class)

  • 위치 — 일반 '클래스'의 바로 위 상위 개념으로 배치됩니다.
  • 구체적인 클래스들을 만들기 위한 '설계도(규격)' 역할을 하며, FastAPI 등에서는 서비스 레이어의 인터페이스를 정의할 때 주로 씁니다.

⭐ 여기서 두 개념의 대칭이 드러납니다.

무엇의 관문인가무엇을 통제하는가
매개변수메서드의 관문어떤 값이 들어올 수 있는가
추상 클래스클래스의 관문어떤 구현이 들어올 수 있는가

하나는 값의 게이트, 하나는 타입의 게이트입니다. 둘 다 게이트입니다.

10.5 백엔드와 프론트엔드는 완전히 같은 구조다

이 계층을 백엔드와 프론트엔드에 각각 적용해보면 놀랄 만큼 대칭입니다.

TOP LAYER — SYSTEM & CONTEXT

  • CONTEXT (컨텍스트) / GLOBAL VARIABLE (전역 변수) — 공유 데이터(system_time, user_session), 어디서나 접근 가능
층BACKEND (서버 사이드)FRONTEND (클라이언트 사이드)
정의하는 것구조 & 행위 (structure & behavior)구조 & 상호작용 (structure & interactions)
1. 설계도CLASS (클래스) — blueprint/schemaCLASS/COMPONENT — UI element blueprint
2. 실체INSTANCE (인스턴스) — object (in RAM/DB)INSTANCE/COMPONENT — rendered object (in DOM)
2-a. 속성INSTANCE VARIABLE — object propertiesINSTANCE VARIABLE (props/state) — component data
2-b. 행동METHOD (메서드) — actions/functionsMETHOD (event handler) — actions (click, hover)
2-c. 임시값LOCAL VARIABLE — temporary data, Stack → 짧은 수명LOCAL VARIABLE (local state) — temporary UI data → 짧은 수명

중앙 — DATA FLOW: Database ↔ DATA FLOW ↔ JSON Data

⭐ 핵심: 백엔드와 프론트엔드가 완전히 같은 구조로 대칭을 이룹니다. 다른 것은 실체가 어디에 그려지느냐뿐입니다(백엔드는 RAM/DB, 프론트엔드는 DOM). 3주차의 "클래스는 디스크, 인스턴스는 메모리"가 프론트엔드에도 그대로 적용됩니다. DOM이 프론트엔드의 메모리입니다.


11. ⭐ 매개변수 = 관문(Gate)

11.1 모래시계 도식

두 개의 격리된 스코프(방)가 하나의 제한된 통로를 통해 소통하는 구조입니다. 모래시계를 떠올리면 정확합니다.

   ┌──────────────────────────────────┐
   │  ❶ 상단 스코프 (함수 A)          │
   │     로컬 변수 x = 10             │
   │     내부 연산  x + 2  →  12      │
   └──────────────┬───────────────────┘
                  │
   ┅┅┅┅┅┅┅┅┅┅┅┅┅┅│┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅   ← ❷ 중앙 격리선 (메모리 불혼합)
                 (⊗) ← 매개변수 = 유일한 통로
                  │
   ┌──────────────┴───────────────────┐
   │  ❸ 하단 스코프 (함수 B)          │
   │     새 로컬 변수 x ← 12          │
   │     내부 연산  x + 5  →  17      │
   │     → 외부로 반환 (Return)       │
   └──────────────────────────────────┘

❶ 상단 스코프 (함수 A의 로컬 스코프)

  • 로컬 변수 (Local Variable) x — 함수 내부에서 독립적으로 존재하는 변수입니다. 외부 영역에서는 이 변수에 직접 접근할 수 없습니다.
  • 내부 연산 (+1 또는 +2) — 해당 스코프 내에서 비즈니스 로직을 거쳐 데이터가 가공됩니다.

❷ 중앙 경계선과 매개변수 (Parameter)

  • 중앙 격리선 (점선) — 두 스코프가 완전히 분리되어 있어 메모리가 섞이지 않음을 뜻합니다.
  • 매개변수 (Parameter) — 하단 스코프가 외부 데이터를 안전하게 받아들이기 위해 열어둔 유일한 통로(관문) 입니다. 상단 스코프의 데이터는 이 관문을 통과해야만 경계선을 넘을 수 있습니다.

❸ 하단 스코프 (함수 B의 로컬 스코프)

  • 새로운 로컬 변수 — 관문을 통해 들어온 데이터를 이 방만의 새로운 로컬 변수 x에 할당합니다. ⭐ 이름은 상단과 같을지라도 완전히 독립된 별개의 메모리 공간입니다.
  • 후속 연산 (+5) — 전달받은 값으로 로직을 수행한 뒤 외부로 반환(Return) 합니다.

주황색 점선이 프로그램의 실행 흐름(Control Flow) 이자 데이터의 이동 경로입니다.

⚠️ 가장 많이 하는 착각: "이름이 같으니까 같은 변수겠지." ❌ 이름은 그 방 안에서만 통하는 호칭입니다. 방이 다르면 완전히 다른 메모리입니다. 이걸 놓치면 "함수 안에서 값을 바꿨는데 밖에서는 안 바뀌어요"가 끝없이 반복됩니다.

11.2 🐍 FastAPI (Backend) 코드 매핑

상단 함수에서 연산된 값을 매개변수를 통해 하단 비즈니스 로직 함수로 전달하는 흐름입니다.

# 하단 스코프 (함수 B): 관문(매개변수 x)을 열어두고 대기
def calculate_next_value(x: int):  # 초록색 관문 (매개변수)
    result = x + 5                 # 하단 내부 연산 (+5)
    return result                  # 주황색 화살표 아웃풋

# 상단 스코프 (함수 A / 라우터)
@app.get("/process")
def process_data():
    x = 10
    temporary_value = x + 2         # 상단 내부 연산 (+2) -> 12

    # 관문(매개변수)을 통해 하단 함수로 데이터 주입
    final_output = calculate_next_value(temporary_value)
    return {"result": final_output}

calculate_next_value 안의 x와 process_data 안의 x는 이름만 같고 아무 관계가 없습니다. 관계를 맺어주는 것은 오직 호출 시점에 괄호 안으로 넘긴 값 하나뿐입니다.

11.3 ⚛️ Next.js / React (Frontend) 코드 매핑

부모 컴포넌트가 자식 컴포넌트에게 Props(매개변수) 를 통해 데이터를 단방향으로 하향 전달하는 구조와 정확히 일치합니다.

// 하단 스코프: 자식 컴포넌트 (Child Component)
// 부모가 데이터를 던져줄 관문(Props)인 x를 정의함
function ChildComponent({ x }) {  // 초록색 관문 (매개변수/Props)
  const localCalc = x + 5;        // 하단 내부 연산 (+5)
  return <div>{localCalc}</div>;
}

// 상단 스코프: 부모 컴포넌트 (Parent Component)
export default function ParentComponent() {
  const x = 10;
  const passedValue = x + 2;      // 상단 내부 연산 (+2)

  // 자식이 열어둔 관문(x)에 가공된 데이터를 흘려보냄
  return <ChildComponent x={passedValue} />;
}

⭐ React의 props가 왜 읽기 전용인지가 여기서 설명됩니다. props는 관문을 통과해 들어온 값이라 자식이 고쳐도 부모의 메모리는 바뀌지 않습니다(방이 다르니까요). 그래서 React는 "props를 수정하지 말라"고 못 박습니다. 수정해봐야 소용이 없기 때문입니다.

11.4 아키텍처적 의의 — 결합도(Coupling) 완화

이 도식이 보여주는 가장 중요한 프로그래밍 원칙은 느슨한 결합(Loose Coupling) 입니다.

하단 스코프는 상단 스코프의 내부 로직이나 변수 명을 알 필요가 전혀 없으며, 오직 관문(매개변수)으로 들어오는 데이터의 규격만 맞으면 독립적으로 작동합니다.

이는 코드의 재사용성을 높이고 모듈별 단위 테스트를 쉽게 만드는 핵심 기반이 됩니다.

11.5 ⭐ 매개변수와 Port는 같은 것이다

이제 이번 주의 마지막 연결이 완성됩니다.

매개변수 (Parameter)포트 (Port)
크기함수 하나시스템 경계
무엇을 가르는가두 개의 스코프코어와 인프라
통로의 규격타입 (x: int)인터페이스 시그니처
통과 안 하면접근 불가 (스코프 에러)접근 금지 (아키텍처 규칙 위반)
얻는 것느슨한 결합느슨한 결합
다른 쪽을 알아야 하는가❌ 규격만 알면 됨❌ 규격만 알면 됨

8.3절의 "바로 가는 곳에 Port라는 문을 세워 통과" 와 11.1절의 "매개변수만이 유일한 통로" 는 완전히 같은 문장입니다. 배율만 다릅니다.

⭐ 그래서 이번 주의 결론은 이렇습니다. 프로그래밍이란, 서로 다른 두 세계 사이에 문을 세우고 그 문의 규격을 정하는 일이다. 문이 함수 크기면 매개변수, 클래스 크기면 추상 클래스, 시스템 크기면 Port입니다.


12. 풀스택 3계층 — Next.js → FastAPI → PostgreSQL

12.1 전체 흐름

이번 주 내내 다룬 계층 구조가 실제 기술 스택으로 내려오면 이렇게 됩니다.

Next.js  --🔒 HTTP(S) API REQUESTS-->  FastAPI  --DATABASE QUERIES (ORM)-->  PostgreSQL
                                          ◄──────── DATA RESPONSE ─────────

12.2 각 층의 책임

계층기술책임
1. FRONTEND / UI LAYERNext.jsUser Interface & UX / SSR(서버 사이드 렌더링) & Static Generation / API Routes (선택적)
2. BACKEND / LOGIC LAYERFastAPIHigh-Performance Asynchronous Python(3주차의 비동기) / Business Logic & Core Functionality / AI Model & Data Processing(예: Pandas) / Authentication & Authorization / 자동 API 문서(Swagger)
3. DATABASE LAYERPostgreSQLRDBMS / ACID Compliance(3주차 7.2절) / SQL / Scalable Data Storage

12.3 이 조합의 이득 (Key Benefits)

  1. Python Ecosystem for AI & Data — 백엔드 언어가 곧 AI 언어입니다. 모델을 다른 서버로 보낼 필요가 없습니다.
  2. Fast API Development — 타입 힌트만 쓰면 검증·문서가 따라옵니다.
  3. Robust Data Storage — 트랜잭션과 무결성을 DB가 책임집니다.

12.4 이 그림을 이번 주 개념으로 다시 읽기

층GoF 분류 (1장)CQRS (2장)클린 아키텍처 (6장)스코프 (10장)
Next.js행위 (Behavioral)Command·Query 발신Frameworks & Drivers.env.local / Context / props
FastAPI생성 (Creational)Command·Query 분기Interface Adapters + Use Cases.env / Request / 라우터 로컬 변수
PostgreSQL구조 (Structural)Write DBFrameworks & Drivers(외부 시스템)

⭐ 같은 시스템을 네 개의 서로 다른 자를 대고 재본 것입니다. 이 표를 그릴 수 있다면 이번 주 내용은 다 이해한 것입니다.


13. 실습 — 이번 주에 손으로 한 것

이번 주 실습의 목표는 하나입니다. "타이타닉 CSV 파일 하나를, 헥사고날 구조를 통과시켜 DB까지 보내라." 파일 하나 올리는 데 왜 이렇게 폴더가 많이 필요한지 — 그걸 몸으로 겪는 것이 실습의 본질입니다.

13.1 1단계 — 인바운드 어댑터부터 (05-27)

가장 바깥부터 시작합니다. 아직 Port도 Use Case도 없이 라우터 하나만 만듭니다.

AI에게 준 지시

@file:titanic_command_router.py 를 헥사고날 구조로 연결해줘.
스키마의 타입은 전부 str 로 하고, 컬럼명 sex 를 gender 로 변형해줘.

두 가지 결정의 이유

결정이유
타입을 전부 str 로CSV에서 올라온 값은 전부 문자열입니다. 어댑터 단계에서 섣불리 타입을 바꾸면, 파싱 에러가 어댑터에서 터져 원인 파악이 어려워집니다. 타입 변환은 안쪽(유스케이스·도메인)에서 합니다.
sex → gender외부 데이터의 컬럼명을 우리 도메인의 언어로 번역하는 것입니다. 이것이 Interface Adapter 계층의 '통역' 역할입니다 (6.3절 ③).

⭐ 외부 세계의 단어를 그대로 코어에 들이지 않는다 — 이것이 어댑터가 존재하는 이유입니다. DDD 용어로는 보편적 언어(Ubiquitous Language) 를 지키는 일입니다.

13.2 2단계 — 파일 업로드 엔드포인트 붙이기 (05-28)

@sym:james_router 에 프리픽스 "/titanic/james" 로 fileupload 를 추가해서
POST 로 컬럼 데이터를 받고 sex → gender 로 변환해줘.

그런데 프론트에서 호출했더니 fetch 메소드가 누락되어 붙지 않았습니다. 엔드포인트 /titanic/james/upload 를 라우터 메소드에 직접 연결합니다.

@james_router.post("/upload")
async def upload_titanic_file(file: UploadFile = File(...)):
    """Titanic CSV 파일을 업로드하고 Sex 컬럼을 gender로 변환합니다."""
    records = _parse_csv_file(file)
    return {"count": len(records), "records": records}

이 코드에서 읽어야 할 것

요소의미
@james_router.postPOST — 상태를 바꾸는 요청. CQRS의 Command 쪽 (2.3절)
UploadFile = File(...)FastAPI가 제공하는 파일 수신 방식. ...(Ellipsis)는 필수 값이라는 뜻
async def3주차에서 배운 비동기. 파일 I/O는 대기 시간이 있으므로 비동기가 효과적입니다
_parse_csv_file앞의 _ 는 파이썬 관례로 "이 모듈 내부에서만 쓰는 함수" 라는 표시
반환값 {"count": ..., "records": ...}⚠️ 이 시점의 코드는 아직 CQRS 원칙을 어기고 있습니다. Command인데 데이터를 통째로 반환합니다.

⚠️ 마지막 항목이 중요합니다. 처음부터 완벽하게 짜지 않습니다. 먼저 동작하게 만들고, 그 다음 구조로 옮깁니다. 이번 주 실습이 정확히 이 순서입니다.

13.3 3단계 — 인바운드 → 인풋 포트 → 유스케이스 (05-28)

이제 라우터에 뭉쳐 있던 로직을 단계별로 안쪽으로 밀어 넣습니다.

인바운드 → 인풋 포트 → 유스케이스로 데이터를 단계별로 이동시켜줘.
james_use_case.py → james_command.py
파일자리추상/구상
adapter/inbound/api/v1/james_router.pyInbound Adapter구상
app/ports/input/james_use_case.pyInput Port추상
app/use_cases/james_command.pyUse Case구상(비즈니스적)

이 순서로 옮기는 이유 — 라우터에 로직이 있으면 웹 없이는 테스트할 수 없습니다. 유스케이스로 옮기면 FastAPI를 띄우지 않고도 함수 호출 한 번으로 검증됩니다.

13.4 4단계 — 추상 클래스를 실제로 쓰기 (05-29)

3단계까지는 파일만 나눠 놓고 정작 추상 클래스는 쓰지 않은 상태였습니다. 그걸 고칩니다.

추상클래스 미사용 → 사용으로 전환해줘.
use_cases/james_command.py 에 ports/input/james_use_case.py 를 두고,
adapter/inbound/api/v1/james_router.py 에서 전달한 데이터를
ports/output/james_repository.py 로 이동시켜줘.

완성된 한 바퀴 (8.5절의 그림이 여기서 실현됩니다)

james_router.py            [구상] Inbound Adapter
       ↓ 호출
james_use_case.py          [추상] Input Port      ← ABC + @abstractmethod
       ↑ 구현
james_command.py           [구상] Use Case
       ↓ 호출
james_repository.py        [추상] Output Port     ← ABC + @abstractmethod
       ↑ 구현
james_pg_repository.py     [구상] Outbound Adapter

⭐ "파일만 나눠 놓는 것"과 "추상 클래스를 쓰는 것"은 다릅니다. 파일만 나누면 라우터가 여전히 유스케이스 구현체를 직접 import 하므로 의존성이 역전되지 않습니다. 추상 클래스를 넣어야 비로소 라우터가 Port만 보고 호출하게 되고, 그때 7.4절의 화살표가 꺾입니다.

13.5 프론트엔드 — 목록 화면과 페이지네이션 (05-28)

"1. 데이터 수집" 아래에 "2. 탑승자 목록" 을 추가하고,
페이지당 50명씩 페이지네이션 해줘.

왜 필요한가 — 타이타닉 데이터는 891행이고 실무 데이터는 89만 행일 수 있습니다. 페이지네이션은 처음부터 넣는 게 맞습니다.

💡 CQRS 관점에서 페이지네이션은 Query 쪽의 전형적 요구입니다. "화면에 맞춰 잘라서 준다"는 비즈니스 규칙이 아니라 표현 요구이므로 Command 모델에 넣으면 안 됩니다.

13.6 프론트엔드 — 네비바와 사이드바 (05-27)

  • 레슨 네비바 클릭 시 서브 메뉴 + 왼쪽 사이드바가 나오는 구조로 만듭니다.
  • F12로 모바일 환경을 켰을 때 사이드바가 깨지면, 햄버거 형태로 변형해달라고 요청합니다.

💡 F12 디바이스 모드는 배포 전 필수 검증입니다. 데스크톱에서만 확인하고 배포하면 사용자의 절반 이상이 깨진 화면을 봅니다.

13.7 백엔드에서 CSV 파일 제거하기 (05-27)

2.9절에서 개념으로 배운 것을 실제로 적용합니다.

하는 일

  1. 백엔드 폴더 안에 있던 .csv 파일을 삭제합니다.
  2. 데이터를 파일이 아니라 업로드 → DB 저장 경로로 바꿉니다.
  3. 요청마다 필요한 DB 세션은 Depends 로 받습니다.
from fastapi import Depends

@james_router.get("/passengers")
async def list_passengers(page: int = 1, size: int = 50, session=Depends(get_session)):
    ...

Depends(get_session) 는 요청이 들어올 때 세션을 만들고 응답이 나갈 때 정리합니다. 요청과 요청 사이에 아무것도 남기지 않는다 — 이것이 무상태(Stateless)입니다.

13.8 배포 — Vercel 도메인 연동 (05-26)

⚠️ 도메인을 붙일 때 자주 놓치는 지점입니다.

디폴트 도메인과 www 도메인이 함께 연결되어 있어야 합니다.

  • example.cloud 만 연결하고 www.example.cloud 를 빼먹으면, 사용자가 습관적으로 www. 를 붙여 들어왔을 때 아무것도 안 뜹니다.
  • Apex 도메인(example.cloud)과 WWW 도메인(www.example.cloud)을 둘 다 등록하고, 하나를 대표(primary)로 지정한 뒤 나머지를 리다이렉트 시키는 것이 표준 구성입니다.

13.9 실습 체크리스트

#확인 항목통과 기준
1app/ 안의 파일에 fastapi, sqlalchemy import가 있는가없어야 통과 (6.2절)
2ports/input/ 과 ports/output/ 이 나뉘어 있는가나뉘어야 통과 (8.4절 ISP)
3Port 클래스가 ABC를 상속하고 @abstractmethod가 붙었는가붙어야 통과 (9.3절)
4라우터가 Use Case 구현체를 직접 import 하는가하면 실패 (13.4절)
5POST 엔드포인트가 큰 데이터를 통째로 반환하는가하면 재검토 (2.1절)
6백엔드 폴더에 .csv, .json 같은 상태 파일이 남아 있는가남으면 실패 (2.9절)
7모바일 뷰(F12)에서 레이아웃이 깨지는가깨지면 실패 (13.6절)
8www 도메인이 연결되어 있는가안 되어 있으면 실패 (13.8절)

14. 이 주의 읽을거리와 그 맥락

14.1 아키텍처

자료왜 이 주에 나왔는가
CQRS 패턴 🔗 https://junuuu.tistory.com/8912장의 근간. "읽기 빈도가 쓰기보다 훨씬 높을 때, 쓰기용 모델을 조회에 그대로 쓰면 정규화 테이블 조인 연산으로 성능이 나빠진다"는 문제의식이 그대로 이 글에서 나옵니다.
Port & Adapter 패턴을 왜 사용하는가 🔗 https://codingjw.tistory.com/2167장. "왜(Why)"에 집중한 글입니다. 구조를 먼저 배우면 "이걸 왜 이렇게까지"라는 반발이 생기는데, 이 글이 그 지점을 답합니다.
DIP는 Dependency Injection이 아니라 Dependency Inversion 🔗 https://guhonga.tistory.com/1285.7절의 정정. 수업 중 실제로 혼동이 있었고, 바로잡은 자료입니다. DIP(원칙) ≠ DI(기법) 입니다.
SOLID 원칙 — 카카오뱅크 기술 블로그 🔗 https://tech.kakaobank.com/posts/2411-solid-truth-or-myths-for-developers/5장. 제목이 "개발자를 위한 SOLID, 진실 혹은 신화"입니다. 원칙을 교조적으로 적용했을 때 생기는 문제까지 다루므로, 원칙을 배운 직후에 읽어야 균형이 맞습니다.
퍼사드(Facade) 패턴 🔗 https://refactoring.guru/ko/design-patterns/facade1.4절. 불어로 '정면'이라는 어원부터 짚어야 오래 남습니다. gateway/ 폴더의 정체가 이것입니다 (4.4절).

14.2 DB · Python

자료왜 이 주에 나왔는가
ORM(Object-Relational-Mapping)이란 🔗 https://velog.io/@mpfo0106/ORMOject-Relational-Mapping-Framework-%EB%9E%807.3절 5단계(Outbound Adapter)에서 실제로 쓰는 기술입니다. 3주차에서 배운 "객체(메모리)와 개체(DB)"를 잇는 다리가 ORM입니다.
데이터 모델링 기본 — 2NF와 3NF3.6절. CQRS의 Write DB가 "정규화되어 있다"는 말을 이해하려면 정규화 단계를 알아야 합니다. 2NF는 부분적 종속 제거, 3NF는 이행적 종속 제거입니다.

14.3 AI 개발 환경의 변화

하네스 영상 — "부탁과 구조, 이 차이가 핵심" 🔗 https://youtu.be/6MYZ7fMhKPY

이 영상이 이번 주에 나온 것은 우연이 아닙니다.

  • 부탁 — "이 기능 좀 만들어줘". 결과가 매번 다릅니다. AI가 어디에 코드를 놓을지 알 수 없습니다.
  • 구조 — "app/ports/input/ 에 Input Port를 만들고, app/use_cases/ 에 구현체를 만들어줘". 결과가 항상 같습니다.

3주차의 하네스(Harness) 개념이 여기서 아키텍처와 만납니다. 아키텍처는 AI에게 주는 하네스입니다. 폴더 구조가 명확하면 AI가 헤매지 않습니다. .cursorrules 파일이 프로젝트 루트에 있는 이유가 이것입니다 (4.1절 트리).

삼성전자·SK하이닉스 클로드 개발 투자 — 개발 툴을 클로드 코드로 전환

대기업이 개발 툴 전체를 AI 코딩 도구로 전환한다는 소식입니다. 이 뉴스의 의미는 "AI가 코드를 짠다"가 아니라 "AI가 코드를 짜는 것을 전제로 조직의 개발 프로세스를 재설계한다" 는 뜻입니다. 그 전제 아래서 개발자에게 남는 일은 구조를 정하고 경계를 긋는 일 — 정확히 이번 주에 배운 것입니다.

14.4 커리어 — 해커톤 · 공모전

이번 주에 정리된 마감 일정입니다. 아키텍처를 배운 직후에 나오는 이유는 포트폴리오에 넣을 결과물을 만들 시점이기 때문입니다.

대회일정링크
한컴 코드게이트~7/8🔗 https://eduhancom.com/codegate
2026 공공기관 AI 혁신 챌린지~8/28🔗 https://aichallenge4all.or.kr/competitions/public-ai
JB금융그룹 해커톤~6/12
삼성전자 SCPC (AI 파트)6/11 ~ 7/10🔗 https://research.samsung.com/scpc
과학기술정보통신부6/1 ~ 8/28
문화체육관광부4/27 ~ 6/26🔗 https://www.culture.go.kr/digicon/
방산 AI~6/14
디지털 헬스케어~6/29

14.5 커리어 — KDT 수료자 채용 기업 지원금

청년일자리도약장려금 — KDT(K-Digital Training) 수료자를 채용하는 기업이 받는 지원금입니다. 구직할 때 "저를 채용하면 회사가 지원금을 받습니다" 라고 말할 근거이므로 정확히 알아두세요.

항목내용
지원 규모1인당 1년 최대 720만원 (월 60만원 × 6개월 이상 정규직)
신청 주체상시근로자 5인 이상 우선지원대상기업
신청처관할 고용센터
청년 요건 ①만 15~34세
청년 요건 ②미취업 상태
청년 요건 ③아래 취업애로청년 요건 중 1개 충족

취업애로청년 요건 (하나만 충족하면 됨)

  • 4개월 이상 연속 실업
  • 고졸 이하
  • 최종학교 졸업 후 고용보험 가입 12개월 미만
  • (그 외 고시된 요건들)

부록 A. 용어 사전

용어영문상위 범주정의
CQRSCommand Query Responsibility Segregation아키텍처 패턴명령(쓰기) 모델과 조회(읽기) 모델을 분리하는 패턴
명령CommandCQRS 구성요소상태를 변경하는 작업. 결과 데이터를 반환하지 않음
질의QueryCQRS 구성요소상태를 변경하지 않는 조회. 부작용 없음
최종 일관성Eventual Consistency일관성 모델지금은 다를 수 있지만 결국 같아지는 상태
DQLData Query LanguageSQL 분류(목적별)SELECT. 조회 전용
DMLData Manipulation LanguageSQL 분류(목적별)INSERT·UPDATE·DELETE. 데이터 상태 변경
DDLData Definition LanguageSQL 분류(대상별)CREATE·ALTER·DROP·TRUNCATE. 구조 정의
DCLData Control LanguageSQL 분류(대상별)GRANT·REVOKE. 권한 관리
TCLTransaction Control LanguageSQL 분류(대상별)COMMIT·ROLLBACK. 트랜잭션 제어
무상태Stateless서버 성질요청 사이에 서버가 아무 상태도 보관하지 않음
Depends—FastAPI 기능요청마다 의존 객체를 만들고 정리하는 의존성 주입 장치
SOLID—설계 원칙 모음객체지향 설계 5원칙의 머리글자
SRPSingle Responsibility PrincipleSOLID모듈은 하나의 변경 이유(액터)만 가져야 함
OCPOpen-Closed PrincipleSOLID확장에는 열려 있고 수정에는 닫혀 있어야 함
LSPLiskov Substitution PrincipleSOLID하위 타입은 상위 타입을 안전하게 대체해야 함
ISPInterface Segregation PrincipleSOLID안 쓰는 메서드에 의존하지 않도록 인터페이스를 쪼갬
DIPDependency Inversion PrincipleSOLID고수준·저수준 둘 다 추상에 의존해야 함
클린 아키텍처Clean Architecture아키텍처동심원 4계층, 의존성은 안쪽으로만
의존성 규칙The Dependency Rule클린 아키텍처 규칙모든 의존성은 안쪽 원을 향한다
엔티티(클린)Entities클린 계층가장 안쪽. 프레임워크 무관한 핵심 업무 규칙
유스케이스Use Cases클린 계층애플리케이션 특화 업무 규칙. 시나리오 흐름
인터페이스 어댑터Interface Adapters클린 계층통역 계층. Controller·Presenter·Gateway
프레임워크와 드라이버Frameworks & Drivers클린 계층가장 바깥. 교체 가능한 세부 기술
헥사고날 아키텍처Hexagonal Architecture아키텍처포트와 어댑터로 코어를 외부에서 격리
포트Port추상코어가 정의한 계약(인터페이스)
어댑터Adapter구상포트를 특정 기술로 구현한 것
Input PortInbound Port포트외부가 코어를 호출할 때의 진입 계약
Output PortOutbound Port포트코어가 외부에 요청할 때의 출구 계약
Inbound AdapterDriving Adapter어댑터외부 요청을 받아 Input Port를 호출 (라우터·CLI)
Outbound AdapterDriven Adapter어댑터Output Port를 구현 (DB 리포지토리·파일 로더)
기술적 구상—구상의 종류어댑터. 어떤 기술로 할지를 기술
비즈니스적 구상—구상의 종류유스케이스. 어떤 순서·규칙으로 할지를 기술
제어의 흐름Flow of Control런타임 개념실행 시 누가 누구를 호출하는가
의존성의 방향Dependency Direction컴파일타임 개념코드가 누구를 import 하는가
Port Gate—설계 관행직행 경로를 막고 포트라는 문을 세워 통과시키는 것
DDDDomain-Driven Design설계 방법론비즈니스 도메인을 코드 구조의 중심에 두는 방법론
abcAbstract Base Class파이썬 모듈추상 클래스를 만드는 표준 모듈
ABC—파이썬 클래스상속하면 추상 기반 클래스가 됨
@abstractmethod—파이썬 데코레이터자식이 반드시 구현하도록 강제
추상Abstraction설계 개념What. 무엇을 하는가. 변하지 않는 계약
구상Concretion설계 개념How. 어떻게 하는가. 변하는 기술 세부사항
스코프Scope언어 개념이름이 유효한 범위
로컬 변수Local Variable스코프함수·메서드 안에서만 유효
필드Field스코프클래스에 선언된 변수 (인스턴스 변수 + 클래스 변수)
인스턴스 변수Instance Variable필드의 한 종류인스턴스마다 하나씩
컨텍스트Context스코프애플리케이션 실행 문맥·상태
전역 변수Global Variable스코프프로세스 전체에서 접근 가능
환경 변수Environment Variable스코프OS·컨테이너 레벨에서 주입. 코드 바깥
매개변수Parameter관문스코프 사이의 유일한 통로
느슨한 결합Loose Coupling설계 성질서로의 내부를 모른 채 규격만으로 연결됨
전략 패턴Strategy행위 패턴알고리즘을 캡슐화해 교체 가능하게 함
싱글톤 패턴Singleton생성 패턴인스턴스가 하나만 존재하도록 보장
어댑터 패턴Adapter구조 패턴비호환 인터페이스를 연결
퍼사드 패턴Facade구조 패턴복잡한 서브시스템에 단순한 정면 하나를 제공

부록 B. 스스로 확인하기

먼저 답해보고, 막히면 옆의 절을 펼치세요.

#질문답이 있는 곳
1프론트=행위 / 백엔드=생성 / DB=구조 매핑이 성립하는 이유는?1.1
2CQRS의 핵심 한 줄을 그대로 쓸 수 있는가?2.1
3기존 CRUD의 두 가지 한계는?2.2
4CQRS 1단계와 2단계의 차이는?2.4~2.5
5최종 일관성을 써도 되는 곳과 안 되는 곳의 기준은?2.6
6SELECT만 DQL이고 나머지가 DML인 이유는?3.2
7alembic/이 DDL이고 apps/가 DML·DQL인 이유는?4.2
8SRP의 '액터'란 무엇인가?5.3
9DIP와 DI는 무엇이 다른가?5.7
10클린 아키텍처의 의존성 규칙을 한 문장으로?6.2
11클린 4계층을 안쪽부터 나열할 수 있는가?6.3
12Use Case가 코어 안에 있으면서도 '구상'인 이유는?7.2
13제어의 흐름과 의존성의 방향이 어긋나는 지점은 정확히 어디인가?7.4
14전략 패턴·DIP·IoC·DI·포트&어댑터의 공통 구조는?7.5
15ports/를 input/과 output/으로 나누는 근거가 되는 SOLID 원칙은?8.4
16추상화하는 근본 이유를 한 문장으로?9.1
17추상화가 나타나는 5곳을 열거할 수 있는가?9.4
18환경 변수가 전역 변수보다 바깥인 이유는?10.2
19함수 안팎의 x가 이름이 같아도 다른 이유는?11.1
20매개변수와 Port가 같은 것인 이유는?11.5

부록 C. 자주 하는 실수

실수바로잡기
"DIP = Dependency Injection"❌ Dependency Inversion(의존 역전) 입니다. DI는 별개의 기법
"DI를 쓰면 DIP가 지켜진다"⚠️ 구상 클래스를 주입하면 DI는 맞지만 DIP는 위반
"Use Case는 코어 안에 있으니 추상이다"❌ 비즈니스적 구상입니다. 추상은 Port
"Port는 바깥이 만들어서 코어에 준다"❌ 코어가 선언합니다. 이 방향이 곧 DIP
"제어가 흐르는 쪽으로 import 하면 된다"❌ 흐름과 의존성은 어긋납니다. Outbound 구간에서 화살표가 꺾입니다
"폴더만 나누면 헥사고날이다"⚠️ 추상 클래스를 안 쓰면 라우터가 구현체를 직접 import 합니다. 역전이 안 일어납니다
"ports/ 폴더에 파일 하나 두면 됨"⚠️ input/과 output/을 나눠야 ISP입니다
"명령(Command)도 데이터를 리턴하는 게 편하다"⚠️ 성공/실패 또는 ID만 반환하는 게 원칙
"CQRS는 무조건 DB 두 개"❌ 1단계는 클래스만 분리합니다. 대부분은 여기서 끝내도 됩니다
"최종 일관성이 최신 기술이니 다 적용"❌ 잔액·재고엔 쓰면 안 됩니다. 틀려도 되는 시간이 있느냐가 기준
"TRUNCATE 했다가 ROLLBACK 하면 되지"❌ TRUNCATE는 DDL이라 대체로 롤백 불가. 되돌리려면 DELETE
"정규화는 무조건 많이 할수록 좋다"⚠️ 조인이 늘어납니다. Read 쪽은 일부러 역정규화 합니다
"그 계층엔 그 GoF 분류만 쓴다"❌ 금지 규칙이 아니라 무게중심입니다
"SRP = 클래스는 한 가지 일만"⚠️ 정확히는 변경 이유(액터)가 하나여야 한다는 뜻
"LSP는 상속만 잘하면 지켜진다"⚠️ 반환값의 의미와 예외까지 같아야 지켜집니다
"백엔드에 CSV 두면 편한데 왜 지우나"⚠️ 서버를 늘리는 순간 어느 서버에 있는지 알 수 없게 됩니다
"함수 밖 x를 함수 안에서 고치면 밖도 바뀐다"❌ 별개의 메모리입니다. 이름만 같습니다
"값은 넓은 스코프에 두면 편하다"⚠️ 필요한 것보다 넓은 스코프가 거의 모든 버그의 시작입니다
"환경 변수와 전역 변수는 같은 것"❌ 전역 변수는 코드 안, 환경 변수는 코드 밖입니다
"usecases, services, orchestrators 섞어 써도 됨"⚠️ 하나로 통일하세요. 안 그러면 답 없는 논쟁이 매주 반복됩니다
"Vercel에 도메인 하나만 붙이면 끝"⚠️ 디폴트 도메인과 www 를 함께 연결해야 합니다
"AI에게 '이 기능 만들어줘'라고 하면 됨"⚠️ 부탁이 아니라 구조를 주세요. 폴더 경로까지 지정하면 결과가 안정됩니다

부록 D. 날짜별 색인

이 장의 내용이 실제 수업에서 어느 날 다뤄졌는지입니다.

날짜다룬 내용이 장의 절
5/25 (월)(휴강)—
5/26 (화)웹 3계층 ↔ GoF 3분류 매핑(행위·생성·구조), 옵저버·전략·싱글톤·팩토리·프록시·어댑터·퍼사드, CQRS 패턴 전반(CRUD 한계, Command/Query, 1·2단계 분리, 이벤트 동기화, 최종 일관성), 정규화 2NF/3NF, Vercel 도메인 연동, 해커톤 일정, 청년일자리도약장려금1, 2.12.8, 3.6, 13.8, 14.414.5
5/27 (수)SQL 명령어 체계(목적별 DQL·DML / 대상별 DDL·DCL·TCL), CRUD 매핑, "상태변화가 중심", 백엔드 CSV 제거 = 무상태(Sticky Session·장애극복·Depends·JWT·Redis), 퍼사드 어원, 헥사고날 이름 충돌 정리(usecases/application_services/orchestrators, gateways/middlewares/auth), ORM, 타이타닉 인바운드 어댑터 스키마(str·sex→gender), 네비바·사이드바·모바일 햄버거, 하네스 영상3, 2.9, 1.4, 4.5, 13.1, 13.6, 13.7, 14.3
5/28 (목)프로젝트 디렉터리 ↔ 개념 매핑(alembic=DDL / apps=DML·DQL / core=Docker), 서비스 코드네임과 gateway, SOLID 5원칙과 클린 아키텍처 연계, 클린 아키텍처 의존성 규칙·4계층, DIP 실전 결합(UserRepository), 헥사고날 추상/구상(Ports·Adapters·Use Cases), Inbound/Outbound(Driving/Driven), 제어의 흐름 ≠ 의존성의 방향, 5단계 흐름도, Next.js→FastAPI→PostgreSQL 풀스택, Port&Adapter 읽을거리, 타이타닉 업로드 엔드포인트·유스케이스 이동·페이지네이션4, 5, 6, 7.17.4, 12, 13.213.3, 13.5
5/29 (금)추상화하는 근본 이유(방파제, How/What), abc·ABC·@abstractmethod, 추상화가 나타나는 5곳, DIP 정정(Inversion≠Injection), 카카오뱅크 SOLID 글, CLEAN HEXAGONAL DDD 폴더 구조(adapter/app/domain, Port Gate, ISP), 스코프 계층 구조와 Next.js/FastAPI 매핑, 매개변수=관문(모래시계), 백엔드-프론트엔드 대칭, 추상클래스 전환 실습, 해커톤 일정, 대기업 AI 개발툴 전환9, 5.7, 8, 10, 11, 13.4, 14.1, 14.3~14.4

부록 E. Gemini / NotebookLM 프롬프트

복습 문제 받기

이 5주차 교재만 근거로 8문제를 내줘. 비전공자 기준으로.
"CQRS 핵심 한 줄"과 "제어의 흐름 ≠ 의존성의 방향"은 반드시 포함해줘.
답은 바로 주지 말고, 내가 답한 다음에 채점하고
틀린 이유를 교재의 어느 절을 근거로 설명해줘. 교재에 없는 내용은 꺼내지 마.

인포그래픽 ① — 이 장의 중심

「7.4 제어의 흐름 ≠ 의존성의 방향」을 인포그래픽 1장으로 만들어줘.
- 가로로 5개 박스: Inbound Adapter / Input Port / Use Case / Output Port / Outbound Adapter
- 위쪽에 "제어의 흐름" 화살표를 왼→오른쪽 한 방향으로
- 아래쪽에 "의존성의 방향" 화살표를 그리되, Port 두 곳에서 화살표가 안쪽으로 꺾이게
- 각 박스에 [추상] / [구상] 태그를 색으로 구분
- 전문용어는 한글(영문) 병기

인포그래픽 ② — CQRS 전체

「2. CQRS」를 한 장으로 만들어줘.
- 왼쪽에 Client, 오른쪽으로 Command Path와 Query Path 두 갈래
- Command Path 끝에 Write DB(RDB·정규화), Query Path 끝에 Read DB(NoSQL·역정규화)
- 두 DB 사이에 "이벤트 동기화 (최종 일관성)" 점선 화살표
- 하단에 핵심 한 줄: "질의는 상태를 바꾸지 않고, 명령은 결과를 반환하지 않는다"
- 1단계(논리적 분리)와 2단계(물리적 분리)를 왼쪽에 세로 띠로 표시

인포그래픽 ③ — 추상화 5곳

「9.4 추상화가 나타나는 5곳」을 한 장으로 만들어줘.
- 가운데 큰 원에 "변화의 중심에서 내 코드를 보호하기 위해"
- 주위에 5개 위성: 객체지향 추상화 / SOLID DIP / 클린 아키텍처 Port / GoF 전략 패턴 / RDB 식별관계
- 각 위성에 "추상에 해당하는 것"과 "구상에 해당하는 것"을 두 줄로
- 다섯 위성이 전부 같은 목적이라는 점이 시각적으로 드러나게

막힐 때

5주차에서 "___" 가 이해가 안 돼.
이 교재의 판별 축 5개(추상/구상, 안쪽/바깥쪽, 읽기/쓰기, 구조정의/데이터조작, 좁은스코프/넓은스코프)만
써서 다시 설명해줘. 교재에 없는 새 비유는 만들지 마.

3주차와 이어붙이기

3주차 교재의 "전략 패턴 = DIP = IoC = DI = 포트&어댑터" 부분과
5주차 교재의 7.5절을 나란히 놓고,
다섯 개념이 각각 어느 '층위'에 속하는지 표로 정리해줘.
그리고 세 주차에 걸쳐 같은 개념이 어떤 이름으로 다시 나왔는지 추적해줘.

다음 장 연결 확인

이 교재에서 "앞으로 다시 만납니다"라고 표시된 부분을 모두 찾아서,
이번 장의 어떤 개념이 나중에 어떤 이름으로 다시 나오는지 표로 정리해줘.
특히 '어댑터 패턴', '퍼사드', 'Repository' 세 단어를 추적해줘.