LangTailor

Lesson

소프트웨어 아키텍처 설계

GoF 디자인 패턴부터 헥사고날·클린 아키텍처와 SOLID 원칙까지, 구조를 설계하는 관점을 단계별로 쌓습니다.

14개 주제

01

아키텍처(구조)와 아키텍트(역할)

아키텍처가 모듈화·상호작용·확장성을 갖춘 구조 그 자체라면, 아키텍트는 그 구조를 만들고 유지하는 사람이다. 비즈니스 목표를 기술적 판단과 트레이드오프로 옮기는 것이 역할의 핵심이다.

아키텍처(구조)와 아키텍트(역할)
  • 아키텍처: 모듈화, 상호작용, 확장성, 데이터 흐름을 규정한 구조
  • 아키텍트: 비즈니스 목표를 읽고 트레이드오프를 결정하며 복잡성을 해결
  • 소통과 가이드 제공도 아키텍트의 핵심 업무다
02

설계 전략의 3계층

설계는 규모에 따라 세 층위로 나뉜다. 각 층은 독립적이지 않고, 하위 전략은 항상 상위 전략 안에서 작동한다.

설계 전략의 3계층
  • 소전략 — GoF 디자인 패턴: 객체 수준의 코드 재사용과 확장 (Singleton, Strategy, Observer, Factory)
  • 중전략 — 아키텍처 패턴: 서브시스템 상호작용과 관심사 분리 (MVC, MVP, MVVM, Clean)
  • 대전략 — 시스템 아키텍처: 거시적 구조와 가용성/확장성 (Monolithic, MSA, Serverless)
03

현대 소프트웨어 엔지니어링 4대 구조

무상태 프론트엔드, 하네스 엔지니어링, 모듈러 모놀리식, 객체지향 백엔드가 현대 웹 시스템을 떠받치는 네 기둥이다. 각각 '틀(상수)'과 '알맹이(변수)'로 나눠 보면 구조가 선명해진다.

현대 소프트웨어 엔지니어링 4대 구조
  • 무상태 프론트엔드: 화면 렌더링 규칙이 틀, 외부 데이터가 알맹이
  • 하네스 엔지니어링: 규격 인터페이스가 틀, 전송되는 데이터가 알맹이 — 복잡한 API 호출을 규격화
  • 모듈러 모놀리식: 시스템 전체 구조가 틀, 개별 독립 모듈이 알맹이 — 배포는 하나, 내부는 독립
  • 객체지향 백엔드: 객체 정의(부품 설계도)가 틀, 객체 내부 데이터가 알맹이
04

웹 아키텍처와 GoF 디자인 패턴 지도

GoF 패턴 3대 분류가 웹 3계층에 자연스럽게 대응된다. 프론트엔드는 행위(Behavioral), 백엔드는 생성(Creational), DB 접근은 구조(Structural) 패턴이 주로 쓰인다.

웹 아키텍처와 GoF 디자인 패턴 지도
  • 프론트엔드 / 행위(Behavioral): Observer(데이터 변화 알림), Strategy(동적 행동 전환)
  • 백엔드 / 생성(Creational): Singleton(유일한 인스턴스 보장), Factory Method(객체 생성 위임)
  • DB / 구조(Structural): Proxy(접근 제어·캐싱), Adapter(비호환 인터페이스 연결), Facade(복잡한 시스템 단순화)
05

CQRS — 명령과 조회의 책임 분리

쓰기와 읽기는 요구사항이 다르다. 쓰기는 정합성과 비즈니스 로직이 중요하고, 읽기는 속도와 유연한 조회 모델이 중요하다. 둘을 분리하면 각각을 독립적으로 최적화할 수 있다.

CQRS — 명령과 조회의 책임 분리
  • 커맨드(Command): INSERT/UPDATE/DELETE — 결과를 반환하지 않고 비즈니스 로직 중심
  • 쿼리(Query): SELECT — 데이터를 DTO로 반환하며 사용자 정의 조회 모델 사용
  • 두 데이터베이스는 이벤트 소싱으로 동기화되며 최종 일관성을 갖는다
  • 장점: 확장성, 독립적 배포, 성능 최적화, 조회 모델 유연성
06

헥사고날 아키텍처 — 포트와 어댑터

핵심 비즈니스 로직을 가운데 두고, 외부와 닿는 모든 지점을 포트(인터페이스)와 어댑터(구현)로 감싼다. 덕분에 DB를 PostgreSQL에서 다른 것으로 바꿔도 코어는 그대로다.

헥사고날 아키텍처 — 포트와 어댑터
  • 포트(Ports) = 인터페이스, 추상적인 계약
  • 어댑터(Adapters) = 기술 구현체, 구체적인 기술
  • 인바운드 어댑터(Driving): HTTP/REST API, GraphQL, CLI
  • 아웃바운드 어댑터(Driven): PostgreSQL, Redis 캐시, 파일 시스템, 메일 서비스
07

헥사고날 아키텍처 흐름도

요청은 인바운드 라우터에서 시작해 입력 포트(use_case 인터페이스)를 지나 비즈니스 로직을 실행하고, 출력 포트(repository 인터페이스)를 거쳐 실제 DB 구현체로 나간다. 추상과 구상이 번갈아 배치된다.

헥사고날 아키텍처 흐름도
  • INBOUND(Router) — 구상: 실제 HTTP 라우터
  • INPUT(use_case) — 추상: 입력 포트 정의
  • USE_CASE — 비즈니스 로직 실행 (DIP 의존성 역전 원칙 적용)
  • OUTPUT(Repository) — 추상: 데이터 접근 인터페이스
  • OUTBOUND(pg_repository) — 구상: PostgreSQL 실제 구현
08

클린 · 헥사고날 · DDD를 한 폴더 트리에서

세 가지 설계 사조는 배타적이지 않다. `adapter/`는 헥사고날, `app/`은 클린, `domain/`은 DDD에 해당하며 하나의 프로젝트 구조 안에서 함께 작동한다.

클린 · 헥사고날 · DDD를 한 폴더 트리에서
  • `adapter/inbound`, `adapter/outbound` — 헥사고날(Hexagonal): 문을 관리하는 어댑터 패턴
  • `app/ports`, `app/use_cases`, `app/dtos` — 클린(Clean): 포트 게이트를 통과
  • `domain/` — DDD(Domain-Driven Design): 순수 도메인 로직
  • 의존성 규칙: 외부는 코어를 직접 호출하지 않고 어댑터 → 입력 포트 → 유스케이스 순으로 통과
  • SOLID 중 ISP(인터페이스 분리 원칙)에 해당한다
09

클린 아키텍처의 데이터와 의존성 흐름

router → use_case → interactor → repository → pg_repository → DB로 이어지는 다섯 계층이다. 이 중 use_case와 repository는 인터페이스만 존재하는 '추상 공간'으로, 구현체를 갈아 끼울 수 있는 지점이다.

클린 아키텍처의 데이터와 의존성 흐름
  • ① 인바운드 어댑터 계층: router — schema로 입력 검증
  • ② 애플리케이션/도메인 코어: use_case(입력 포트) → interactor(구현체)
  • ③ 영속성 인터페이스 계층: repository(출력 포트) — dto로 전달
  • ④ 아웃바운드 어댑터 계층: pg_repository — ORM 매핑
  • ⑤ 데이터 계층: 실제 DB
10

요청 처리 파이프라인

하나의 요청이 지나가는 다섯 정거장이다. 각 정거장은 하나의 책임만 갖고, 정거장 사이는 Schema와 DTO 같은 데이터 계약으로 연결된다.

요청 처리 파이프라인
  • Router: 요청 핸들러 (컨트롤러 계층)
  • Schema: 입력 데이터 검증
  • Interactor: 비즈니스 로직 처리
  • Repository: 데이터 접근 계층 — DTO로 주고받는다
  • ORM을 통해 DB에 영속 저장
11

컨 · 서 · 모 · 레 구조와 도메인

컨트롤러 · 서비스 · 모델 · 레포지터리의 네 글자로 백엔드 계층을 외우면 편하다. 여기에 DDD를 얹으면 가운데 도메인 계층에 Entity와 VO(Value Object)가 자리한다.

컨 · 서 · 모 · 레 구조와 도메인
  • 컨(Controller): 인바운드 어댑터 — Pydantic 스키마로 요청 검증
  • 서(Service): 애플리케이션 서비스 — 유스케이스와 흐름 제어
  • 모(Model): 도메인 — Entity(식별자 ID)와 VO(불변 값 객체)
  • 레(Repository): 아웃바운드 어댑터 — SQLAlchemy ORM으로 SQL 실행
  • 데이터 개념(Entity, VO)은 도메인에, 기술 구현(Schema, ORM)은 어댑터에 둔다
12

백엔드에 적용되는 패턴들

하나의 백엔드 요청 흐름에 여러 패턴이 겹쳐서 작동한다. 게이트웨이는 Facade, 라우트는 Adapter, 레포지터리는 Abstract Method, 전처리 단계는 책임 연쇄(CoR) 패턴이다.

백엔드에 적용되는 패턴들
  • gateway — Facade 패턴: 단일 진입점, 내부 서브시스템 단순화
  • Route(inbound) — Adapter 패턴
  • PG_Repository(outbound) — Abstract Method 패턴: 포트/인터페이스 정의
  • CoR(책임 연쇄) 적용 예: 요청 전처리(인증/인가) → 비즈니스 정책 검사 → 다중 DB 접근/캐싱
13

SOLID — LSP와 OCP

부모 객체를 자식 객체로 안전하게 바꿔 끼울 수 있어야 하고(LSP), 기존 코드를 수정하지 않고 새 기능을 추가할 수 있어야 한다(OCP). 두 원칙이 아키텍처의 확장성을 만든다.

SOLID — LSP와 OCP
  • LSP(리스코프 치환 원칙): 동일한 인터페이스와 계약을 준수하면 Schema·DTO·ORM·VO·Entity를 안전하게 교체 가능
  • OCP(개방-폐쇄 원칙): 확장에는 열려 있고(Open) 수정에는 닫혀 있다(Closed)
  • 공통 인터페이스를 두면 Router·Use Case·Interactor·Repository를 코드 추가만으로 확장할 수 있다
14

FastAPI 의존성 주입 흐름

DB 세션이 레포지터리에 주입되고, 레포지터리가 다시 유스케이스에 주입된다. 각 계층은 구체 클래스가 아니라 인터페이스에 의존하므로 느슨한 결합이 유지된다.

FastAPI 의존성 주입 흐름
  • 인프라 계층: `Depends(get_db)`로 AsyncSession 제공
  • 데이터 접근 계층: 세션을 주입받은 Repository 반환
  • 도메인 계층: Repository를 주입받은 Interactor(Use Case) 반환
  • 결과: 느슨한 결합(Loose Coupling), 관심사 분리, 제어의 역전(IoC)