Conceptly

Software Architecture 시각적으로 이해하기

각 개념의 아키텍처를 애니메이션 다이어그램으로 살펴보세요. 카드를 클릭하면 더 깊은 내용을 확인할 수 있습니다.

📱Client🖥️Server🗄️Data
🖥️

Client-Server

중앙 서버 기반 요청-응답 구조

클라이언트-서버 구조는 하나의 프로그램이 처리하던 역할을 '요청'과 '처리' 두 쪽으로 나누는 방식입니다. 클라이언트는 사용자 입력을 받고 결과를 보여주는 데 집중하고, 서버는 데이터 저장과 비즈니스 규칙을 중앙에서 책임집니다. 이 분리 덕분에 클라이언트가 여럿이어도 데이터의 기준점이 하나로 유지됩니다.

🖼️UI🧱Service🗄️DB
🧱

Layered Architecture

서버 내부 책임을 층으로 나누는 구조

Layered Architecture는 하나의 애플리케이션 안에서 코드를 책임별 층으로 나누는 구조입니다. 프레젠테이션, 애플리케이션(유스케이스), 도메인, 인프라 각 층은 자기 역할만 하고 바로 아래 층과만 소통합니다. 서버 코드가 복잡해졌을 때 처음 도달하게 되는 구조 정리 방식이고, Spring MVC의 Controller-Service-Repository 패턴이 대표적인 구현입니다.

👥Users🏢Single App🗄️Shared DB
🏢

Monolith

단일 코드베이스·단일 배포 구조

Monolith는 모든 기능을 하나의 프로세스와 하나의 배포 단위로 묶어서 운영하는 애플리케이션 구조입니다. 모듈이 여러 개 있더라도 빌드하고 나면 하나의 산출물이고, 배포할 때도 전체가 한 번에 움직입니다. 개발 초기에는 로컬 실행과 디버깅이 단순하지만, 시스템이 커질수록 변경과 릴리스가 전체 단위로 엮이게 됩니다.

🚪Entry🧩Svc A🧩Svc B🗄️Own Data
🧩

Microservices

도메인별 독립 서비스 구조

Microservices는 하나의 애플리케이션을 여러 독립 서비스로 자르되, 각 서비스가 자기 도메인의 로직과 데이터를 완전히 소유하도록 설계하는 아키텍처 스타일입니다. 서비스 사이는 DB를 직접 공유하지 않고 API나 이벤트로 협력하며, 팀마다 자기 서비스의 배포와 운영 주기를 독립적으로 가져갑니다. 단순히 코드베이스를 쪼개는 기술이 아니라, 조직과 시스템 경계를 함께 설계하는 방식입니다.

📱Clients🚪Gateway🧩Services
🚪

API Gateway

여러 백엔드의 공통 진입점

API Gateway는 여러 서비스 앞에 두는 단일 진입점입니다. 클라이언트는 내부 서비스 구조를 몰라도 게이트웨이 하나에만 요청을 보내면 되고, 게이트웨이가 인증, 라우팅, 공통 정책을 맡아 처리한 뒤 적절한 서비스로 연결합니다. 서비스마다 중복되던 입구 관리를 한 계층에 모으는 구조입니다.

🧩Service🧭Registry📍Endpoint
🧭

Service Discovery

동적 서비스 주소 해석 메커니즘

Service Discovery는 동적으로 변하는 서비스 인스턴스의 네트워크 위치를 런타임에 이름 하나로 찾아내는 내부 주소 해결 메커니즘입니다. 호출하는 쪽은 IP나 포트를 직접 알거나 관리하지 않아도 되며, 레지스트리가 현재 살아 있는 인스턴스 목록을 관리해 줍니다. 컨테이너와 오토스케일링이 보편화된 환경에서 이름과 실제 위치를 분리하지 않으면 내부 호출 자체가 운영 변화를 버티지 못합니다.

📤ProducerEvent Bus📥Consumer

EDA

사건 중심 비동기 연결 구조

Event-Driven Architecture는 서비스가 서로를 직접 호출하는 대신, '어떤 상태 변화가 일어났다'는 사실을 이벤트로 발행하고 관심 있는 서비스가 각자 반응하게 만드는 설계 방식입니다. 생산자는 소비자가 누구인지 알 필요가 없고, 소비자는 자신의 처리 속도에 맞게 독립적으로 반응합니다. 호출 그래프의 중심을 특정 서비스 주소가 아닌 발생한 사실로 바꾸는 것이 이 구조의 핵심입니다.

📤Producer📬Queue🛠️Worker
📬

Message Queue

비동기 작업 버퍼

Message Queue는 생산자가 보낸 작업 메시지를 소비자가 준비됐을 때 순서대로 꺼내 처리하도록 중간에 잡아 두는 비동기 작업 버퍼입니다. 생산 속도와 처리 속도가 달라도 어느 쪽도 상대를 직접 기다리지 않아도 되고, 소비자가 잠시 다운됐다 복구되더라도 큐에 쌓인 메시지를 이어서 처리할 수 있습니다.

📣Publisher🛰️Topic👂Subscribers
📣

Pub/Sub

이벤트 팬아웃 메시징 구조

Publish/Subscribe는 발행자가 토픽에 메시지를 올리면 그 토픽을 구독한 모든 구독자가 각자 독립적으로 메시지를 받아 처리하는 메시징 패턴입니다. 발행자는 누가 듣는지 알지 못하고, 구독자는 발행자를 직접 참조하지 않습니다. 이 분리 덕분에 새 구독자가 추가되어도 발행자 코드는 변경되지 않습니다.

🖥️App🧠Cache🗄️DB
🧠

Caching

반복 조회를 위한 읽기 가속 기법

Caching은 반복적으로 요청되는 데이터를 원본 저장소 대신 더 가까운 위치에 복사해 두고, 이후 요청이 원본까지 가지 않아도 되게 만드는 읽기 최적화 기법입니다. 원본 데이터를 바꾸지 않고 읽기 경로만 단축하기 때문에, 얼마나 오래 복사본을 믿을지와 언제 원본과 다시 맞출지를 함께 결정해야 합니다.

✍️WriteSync📖Read
✍️

CQRS

읽기·쓰기 모델 분리 구조

CQRS(Command Query Responsibility Segregation)는 데이터를 바꾸는 경로와 읽어 오는 경로를 서로 다른 모델로 분리하는 아키텍처 패턴입니다. 쓰기 쪽은 도메인 규칙과 정합성 검증에 맞게, 읽기 쪽은 화면이나 API가 필요한 형태로 각각 따로 유지합니다. 같은 데이터라도 '저장하는 질문'과 '보여주는 질문'이 다르면 모델도 달라야 한다는 전제에서 출발하며, 복잡한 도메인 시스템에서 쓰기 규칙과 읽기 성능이 서로를 잡아당기는 충돌을 풀어냅니다.

1️⃣Step 12️⃣Step 2↩️Compensate
🔄

Saga

보상 기반 분산 트랜잭션 흐름

Saga는 여러 서비스에 걸친 비즈니스 흐름을 단일 전역 트랜잭션 없이 이어 가는 분산 트랜잭션 패턴입니다. 각 서비스의 로컬 트랜잭션을 순서대로 실행하되, 중간에 실패하면 이미 완료된 단계를 되돌리는 보상 트랜잭션(compensating transaction)을 명시적으로 실행합니다. 성공 경로뿐 아니라 실패와 보상 경로까지 하나의 흐름으로 설계하는 것이 핵심이며, 마이크로서비스처럼 서비스별로 데이터를 소유하는 환경에서 분산 일관성을 현실적으로 다루는 방법으로 자리를 잡았습니다.

📞Caller🛑Breaker🌐Remote
🛑

Circuit Breaker

연쇄 장애 차단 패턴

Circuit Breaker는 원격 호출 경로에 삽입되어, 실패가 임계치를 넘으면 호출 자체를 잠시 차단하는 보호 패턴입니다. 응답을 기다리며 리소스를 붙잡는 대신 빠르게 실패하거나 대체 응답을 돌려줌으로써, 하나의 의존성 장애가 호출하는 쪽 서비스 전체를 끌어내리는 연쇄 장애를 막습니다. 분산 시스템에서 부분 장애를 격리하는 핵심 수단으로, 재시도 정책, 타임아웃, 관측 체계와 함께 운영됩니다.

📨Req♻️KeyOne Result
♻️

Idempotency

중복 요청 안전성

Idempotency는 같은 요청이 두 번, 세 번 실행되더라도 최종 상태가 한 번 실행한 것과 동일하게 유지되는 속성입니다. 네트워크 재시도, 메시지 큐 재전달, 사용자의 중복 제출처럼 '정확히 한 번 실행'을 보장할 수 없는 환경에서, 중복 실행을 안전하게 흡수하는 안전망 역할을 합니다. 분산 시스템과 메시지 기반 아키텍처에서 재시도를 마음 놓고 쓸 수 있게 해주는 전제 조건이기도 합니다.

🧩Service🔭Signals👩‍💻Ops
🔭

Observability

시스템 상태 추적 가능성

Observability는 시스템 내부에서 무슨 일이 벌어지고 있는지를 바깥에서 추론할 수 있게 하는 관측 기반입니다. 에러가 발생했다는 사실 하나를 넘어서, 어느 서비스의 어느 구간이 느려졌고 그 실패가 어디서 시작해 어떻게 퍼졌는지까지 추적할 수 있는 신호를 시스템이 구조적으로 남기게 합니다. 분산 시스템에서 운영 판단의 근거를 만드는 기반이며, 로그, 메트릭, 트레이스 세 가지 신호가 함께 작동할 때 비로소 의미 있는 관측이 이뤄집니다.

🏢Domain🧭Bounded Context📐Model
🧭

DDD

도메인 경계를 모델로 다루는 설계

Domain-Driven Design은 화면, 테이블, API 경로보다 업무 개념 자체를 먼저 모델링하는 설계 접근입니다. 핵심은 코드를 기술 계층의 모음으로 보는 대신, 주문·결제·정산처럼 실제 비즈니스가 쓰는 언어와 경계를 코드에 반영하는 것입니다. 그래서 DDD는 단순한 클래스 설계 기법이 아니라, 시스템이 무엇을 중심으로 나뉘어야 하는지 결정하는 사고 틀에 가깝습니다.

🌐Request🧩Module Boundary📦Single Deploy
🧩

Modular Monolith

모듈 경계를 지키는 단일 배포 구조

Modular Monolith는 전체 애플리케이션을 하나의 배포 단위로 유지하되, 내부는 도메인별 모듈 경계로 강하게 분리하는 구조입니다. 겉으로는 Monolith이지만 안쪽에서는 각 모듈이 자기 공개 계약을 통해서만 협력하고, 다른 모듈의 내부 구현을 직접 건드리지 않도록 설계합니다. 즉, 분산 배포는 하지 않지만 경계 감각은 Microservices처럼 미리 훈련하는 방식입니다.

⬅️Inbound Adapter🔷Core➡️Outbound Adapter
🔷

Hexagonal

핵심 로직을 포트로 감싸는 구조

Hexagonal Architecture는 핵심 비즈니스 로직을 중심에 두고, 바깥의 웹, DB, 메시지 브로커 같은 기술 요소를 포트와 어댑터로 둘러싸는 구조입니다. 중요한 점은 코어가 외부 기술을 직접 참조하지 않는다는 것입니다. 코어는 필요한 능력을 포트라는 계약으로 표현하고, 실제 구현은 바깥 어댑터가 맡습니다. 그래서 이 구조는 흔히 Ports and Adapters라는 이름으로도 설명됩니다.

📱Client Type🧩BFF⚙️Services
📱

BFF

클라이언트별 전용 백엔드 진입점

Backend for Frontends는 웹, 모바일, 파트너 포털처럼 서로 다른 클라이언트 채널마다 전용 백엔드를 두는 패턴입니다. 핵심은 도메인 서비스를 클라이언트가 직접 조합하게 두지 않고, 각 채널에 맞는 응답 형태와 호출 흐름을 중간 계층에서 맞춰 주는 것입니다. 같은 비즈니스 기능을 다루더라도 채널마다 필요한 데이터와 호출 빈도, 인증 방식이 다르다는 사실을 구조에 반영합니다.

🌐Traffic⚖️Load Balancer🖥️Instance Pool
⚖️

Load Balancer

여러 인스턴스로 트래픽을 분산하는 계층

Load Balancer는 외부에서 들어오는 요청을 여러 서버 인스턴스에 나눠 보내는 계층입니다. 클라이언트는 실제 인스턴스 주소를 일일이 알 필요 없이 하나의 진입 주소만 보고 요청하고, 로드 밸런서가 현재 건강한 대상 중 어디로 보낼지 결정합니다. 그래서 이 개념의 핵심은 단순 분산이 아니라, 인스턴스 교체와 장애를 클라이언트로부터 숨기는 완충 계층이라는 점입니다.

🧩Service A🕸️Mesh Proxy🧩Service B
🕸️

Service Mesh

서비스 간 통신을 프록시로 제어하는 계층

Service Mesh는 서비스 간 통신에서 반복되는 재시도, 타임아웃, 인증, 트레이싱 같은 공통 관심사를 애플리케이션 코드 밖의 프록시 계층으로 옮기는 구조입니다. 각 서비스는 자기 비즈니스 로직에 집중하고, 실제 네트워크 호출은 옆에 붙은 프록시가 대신 조정합니다. 그래서 메시의 핵심은 또 다른 비즈니스 계층을 만드는 것이 아니라, east-west 트래픽 제어를 인프라 레이어로 승격시키는 데 있습니다.

✍️Command📜Event Store📘Read Model
📜

Event Sourcing

상태 대신 이벤트를 저장하는 모델

Event Sourcing은 현재 상태를 테이블 한 줄로 덮어쓰는 대신, 상태를 바꾼 사건들을 시간순 이벤트로 저장하는 모델입니다. 현재 상태는 이 이벤트들을 재생해서 얻고, 필요한 읽기 모델도 같은 이벤트 흐름에서 따로 만들 수 있습니다. 따라서 이 구조에서 진짜 시스템 기록은 현재값이 아니라 '무슨 일이 어떤 순서로 일어났는가'입니다.

📝Local TX📤Outbox📣Broker
📤

Outbox

DB 변경과 이벤트 발행을 묶는 정합성 패턴

Outbox Pattern은 비즈니스 상태 변경과 외부 이벤트 발행을 같은 로컬 트랜잭션 안에 묶기 위해, 별도의 outbox 레코드를 함께 저장하는 방식입니다. 애플리케이션은 상태 테이블과 outbox 테이블에 동시에 기록하고, 이후 릴레이 프로세스가 outbox를 읽어 메시지 브로커나 다른 시스템으로 전달합니다. 즉 발행을 즉시 끝내는 대신, 먼저 기록을 안전하게 남기고 나중에 전달하는 구조입니다.

✍️Local Commit🕰️Async PropagationConverged State
🕰️

Eventual Consistency

시간차를 허용해 상태를 맞추는 일관성 모델

Eventual Consistency는 분산된 여러 저장소나 서비스가 같은 순간에 똑같은 값을 보장하지는 않지만, 시간이 지나면 최종적으로 같은 상태에 수렴하도록 설계하는 일관성 모델입니다. 핵심은 '지금 즉시 동일해야 하는가'보다 '잠시 다를 수 있어도 전체가 수렴하는가'를 설계 기준으로 삼는다는 점입니다. 그래서 이 개념은 단순한 타협이 아니라, 조정 비용과 가용성을 함께 계산한 구조적 선택입니다.

🌊Traffic🚧Pool A🚧Pool BCritical Path
🚧

Bulkhead

리소스 풀을 나눠 장애 전파를 막는 패턴

Bulkhead는 하나의 서비스 안에서 자원 풀을 목적별로 나눠, 한 영역의 부하나 장애가 다른 영역의 용량까지 먹어 치우지 못하게 만드는 패턴입니다. 이름은 배의 격벽처럼 한 칸이 침수돼도 배 전체가 가라앉지 않게 막는 구조에서 왔습니다. 즉 이 패턴의 본질은 실패를 막는 것이 아니라, 실패가 퍼지는 범위를 미리 잘라 두는 것입니다.