소프트웨어 아키텍처

아키텍처 문서화

meellon 2026. 7. 7. 05:20

학습 목표

C4 ModelContext·Container·Component·Code 4레벨과 대상 독자를 설명할 수 있다.

Container 다이어그램으로 서비스·데이터스토어·외부 시스템 경계를 그릴 수 있다.

API 계약(OpenAPI 등)이 서비스 간 결합을 관리하는 방법임을 설명할 수 있다.

Living documentation — 코드·계약과 동기화 — 원칙을 적용할 수 있다.

문제 상황

  • 신입이 3주 만에 "주문이 결제를 직접 호출하나요?" — 다이어그램 없음, 구두 전승만
  • MSA 전환 후 draw.io에 박스 40개 — 레벨이 섞여 Context클래스가 한 장에
  • inventoryorder 내부 DTO직접 의존 — 계약 없이 공유 jar
  • OpenAPI 6개월 전 버전 — 모바일은 v2, 백엔드는 v3 배포
  • 장애 RCA에서 "누가 Kafka topic 소유?" — 문서·코드 불일치
  • C4 Component까지 전 서비스 그리다 유지 포기 — 범위 과다

데이터·이벤트까지 설계했다. 이제 팀이 같은 그림을 보도록 문서의도적으로 만드는 편이다.

1. 왜 아키텍처 문서인가

아키텍처 문서 — 구조·경계·결정을 공유변경 비용·오해를 줄인다.

없을 때 있을 때
구두·슬랙만 온보딩·리뷰 기준
"내 머릿속" 설계 이해관계자
계약 암묵 명시 API·이벤트 스키마
레거시 공포 경계·의존 추적
  • 코드 = 진실이지만 의도·트레이드오프는 코드만으로 안 보임 → ADR은 다음 편
  • 문서는 완벽 스냅샷이 아니라 living — 배포·계약과 같이 갱신

2. C4 Model 개요

C4(Context, Container, Component, Code) — 줌 레벨별로 다른 추상화.

레벨 보여 주는 것 주 독자
1 Context 시스템·사용자·외부 연동 전사·PM·비개발
2 Container 앱·서비스·DB·메시지 브로커 개발·운영·아키텍트
3 Component 한 컨테이너 안 모듈 해당 팀 개발자
4 Code 클래스·패키지 IDE — 보통 생략
  • 한 장에 전 레벨 금지 — 독자가 달라 혼란
  • Simon Brown C4 — notation 단순, UML 전체가 아님
  • Deployment·Dynamic 뷰는 보조(배포 노드·시퀀스)

3. Level 1 — System Context

Context — 우리 시스템 하나를 박스로, 사람·외부 시스템과 관계만.

[Customer] ──uses──> [Shop Platform] ──card payment──> [Payment PG]
                         │
                         └──sends notification──> [Email provider]
규칙 이유
내부 서비스 이름 넣지 않음 Container에서
목적 한 줄 per 박스 Scope 명확
화살표에 동사 라벨 uses / sends / queries
  • 품질 속성·SLA 요약을 캡션으로 붙일 수 있음
  • 이해관계자 concern과 맞춤 — 누가 무엇을 걱정하는지

4. Level 2 — Container

Container배포 가능 단위: Spring Boot 앱, SPA, DB, Kafka, Lambda 등.

요소
Person Customer
Container Web App, API Gateway, Order API, Catalog API
Datastore PostgreSQL, Redis, Kafka
External Payment PG
관계 라벨
프로토콜 HTTPS JSON, gRPC, JDBC
방향 단방향 기본, 순환 주의
동기/비동기 REST vs publishes events
  • 앞서 MSA 서비스 맵런타임 관점으로 구체화
  • DB per service — Container마다 자기 datastore 연결
  • 앞서 Kafka·Redis — Container로 명시

Container 작성 체크

질문  
배포 artifact 단위인가  
소유 팀이 있는가  
기술 스택이 다른가 SPA vs API vs DB

5. Level 3 — Component (선택적)

Component하나의 Container 내부: Controller, Domain Service, Repository, Outbox Relay.

Order API (container)
  ├── OrderController
  ├── PlaceOrderService
  ├── OrderRepository
  └── OutboxPublisher
할 때 안 할 때
복잡 도메인·레이어 논의 CRUD 단순 서비스
신규 팀 온보딩 전사 일괄 Component — 유지 불가
헥사고날 포트·어댑터 설명 코드가 진실이면 링크
  • Component는 해당 repo README·/docs로컬로 두는 경우 많음

6. 다이어그램 작성 원칙

원칙 설명
Scope 제목에 레벨·시스템·날짜
Legend 사람·시스템·외부·DB 통일
적은 박스 한 장 7±2 container
스파게티 금지 겹치면 분할·Dynamic
버전 container-v3-2026-07.png 또는 repo 내 Mermaid
  • Mermaid·Structurizr·PlantUML텍스트로 diff 가능
  • Confluence만 — rot 빠름, repodocs/ 권장

7. API 계약

API 계약 — consumer가 의존하는 표면: 경로·스키마·에러·버전.

산출물 역할
OpenAPI (REST) HTTP API 스키마·예제
Protobuf (gRPC) 강타입·버전 필드
AsyncAPI 이벤트 topic·payload
JSON Schema 이벤트 envelope
openapi: 3.1.0
info:
  title: Order API
  version: 2.1.0
  description: Provider-owned contract for order read model

paths:
  /orders/{orderId}:
    get:
      operationId: getOrder
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Order found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "404":
          description: Order not found

components:
  schemas:
    Order:
      type: object
      required: [orderId, status, totalAmount]
      properties:
        orderId:
          type: string
        status:
          type: string
          enum: [PENDING, PAID, SHIPPED, CANCELLED]
        totalAmount:
          type: integer
          description: minor units (KRW)
        items:
          type: array
          items:
            $ref: "#/components/schemas/OrderItem"
    OrderItem:
      type: object
      required: [sku, quantity]
      properties:
        sku:
          type: string
        quantity:
          type: integer
규칙 이유
Provider가 계약 소유 consumer pull
Breaking vs compatible 정의 semver·deprecation
CI contract test drift 조기 발견
Correlation ID 문서화 분산 추적
  • 공유 DTO jar계약 변경전체 재배포 → 안티패턴
  • 앞서 REST/gRPC 선택이 계약 형식을 결정

버전·호환

변경 호환
필드 추가 (optional) compatible
필드 삭제·타입 변경 breaking
enum 값 추가 보통 compatible
URL path 변경 breaking
  • Deprecation 헤더·sunset 날짜·마이그레이션 가이드

8. 이벤트·배포 문서

내용
Dynamic (시퀀스) Place order 한 흐름 — 앞서 동기·이벤트 시나리오
Deployment K8s namespace·AZ·replica
Event catalog topic·schema·owner·retention
OrderPlaced:
  topic: order.events
  schema: order-placed.v2.json
  owner: order-team
  consumers: inventory, notification, analytics
  • CDC topic도 catalog에 등록 — 앞서 CDC 파이프라인 연계

9. 문서 구조 실무

docs/
  c4/
    01-context.md
    02-containers.md
  adr/
  api/
    openapi-order-snippet.yaml
  events/
    order-placed.schema.json
습관 효과
PR에 다이어그램 diff 구조 변경 리뷰
온보딩 30분: Context→Container 빠른 지도
분기마다 Container 점검 drift 방지
계약 CI publish Portal·SDK 자동

10. 실무 체크리스트

질문 좋은 답
C4 어디까지? 전사 Context+Container, Component는
누가 갱신? 구조 PR 작성자
API 진실? OpenAPI in repo, 배포 동기
이벤트? AsyncAPI·schema registry
너무 상세? Code 레벨 생략, IDE로
안티패턴 대안
전사 UML 클래스 C4 Container + ADR
한 번 그리고 방치 living doc·PR 연동
계약 없는 REST OpenAPI 필수
내부 DB 직접 조회 API·이벤트

11. 정리

  • C4 — Context→Container→Component→Code , 독자별 분리
  • Container — MSA·데이터·메시징 경계핵심 산출물
  • API 계약 — OpenAPI·AsyncAPI로 결합 명시·버전
  • Living doc — repo·CI·PR과 같이 움직임
  • Part 6 업무 시작 — 다음은 ADR

다음에 다룰 것

  • ADR과 의사결정 기록
  • Architecture Decision Record 작성법

해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford) 및 Software Architecture in Practice, 4/E (Bass, Clements, Kazman) 의 내용을 기반으로 합니다.

'소프트웨어 아키텍처' 카테고리의 다른 글

아키텍처 평가  (0) 2026.07.09
ADR과 의사결정 기록  (0) 2026.07.08
이벤트 스트리밍과 CDC  (0) 2026.07.06
캐싱 전략  (0) 2026.07.05
데이터 저장소 선택  (0) 2026.07.04