오늘만 무료

2026 소프트웨어 아키텍처 완전 가이드

모놀리스·마이크로서비스·DDD·BFF

by AI개발자
소프트웨어엔지니어링_0.png
이 장을 읽기 전에: 백엔드(4장)와 데이터베이스(5장)의 기초를 이해하고 있을 것.


아키텍처는 "나중에 깔끔하게 하기 위한 장식"이 아니라, 팀이 어떤 속도로 변경을 계속할 수 있는지를 결정하는 설계다. 중요한 것은 유행하는 이름을 아는 것이 아니라, 요건과 운용 능력에 대해 과불급 없는 구조를 선택하는 것이다.

� 한국 시장 맥락: 국내 주요 IT 기업(카카오·네이버·라인·쿠팡)은 대부분 모놀리스에서 시작해 성장 후 마이크로서비스로 전환했다. 토스는 모듈러 모놀리스를 거쳐 점진적으로 분리하는 사례를 기술 블로그에서 공개했다. 공공·금융 SI 환경은 여전히 대규모 모놀리스가 표준인 경우가 많다.



1. 모놀리스, 모듈러 모놀리스, 마이크로서비스

이 섹션이 답하는 질문: 어디까지 분할해야 하는가. 언제 나누어야 하는가.


왜 필요한가

분할은 자유도를 늘리는 한편, 통신, 감시, 배포, 장애 구분의 비용도 늘린다. "작게 나눌수록 좋다"는 것은 잘못이며, 실제로는 팀의 운용 능력이 상한이 된다.

swe-2026-14-01.png


판단기준

swe-2026-14-02.png
swe-2026-14-03.png
⚠️ 막히면 모놀리스 또는 모듈러 모놀리스에서 시작한다. 먼저 분산하면, 조직이 다룰 수 없는 복잡성만 늘어나기 쉽다.


단계적인 진행 방법

처음에는 모놀리스로 신속하게 사양을 굳힌다

변경이 빈번한 영역마다 모듈 경계를 명확화한다

독립 배포가 필요해진 부분만 외부로 분리한다


� 국내 기업 사례: 토스는 "마이크로서비스 아키텍처 전환기" 블로그에서 모놀리스→모듈러 모놀리스→마이크로서비스의 점진적 전환 과정을 공개했다. 배달의민족도 "배민 마이크로서비스 여행기"에서 도메인 주도 분리 경험을 공유했다.


정리

분할은 목적이 아니라 수단이다. 팀의 독립성과 운용 능력이 갖춰질 때까지는, 일체로 동작하는 구조 쪽이 강하다.



2. 레이어 설계와 의존 방향

이 섹션이 답하는 질문: 코드를 어떻게 정리하면 변경에 강해지는가.


왜 필요한가

기능이 늘어나면 화면, 업무 규칙, 데이터 접근, 외부 API 호출이 섞이기 쉽다. 이 혼재를 방치하면 작은 변경에도 부작용을 읽을 수 없게 된다.


클린 아키텍처, 헥사고날 아키텍처, 어니언 아키텍처는 이름은 달라도 목표는 가깝다.

공통된 요점:

비즈니스 규칙을 중심에 둔다

DB나 외부 API 등의 세부 사항은 바깥쪽에 둔다

의존 방향을 안쪽으로 향한다


구현의 최소형 — TypeScript(Node.js/Next.js)

소~중규모에서는, 다음 3계층으로 충분한 경우가 많다.

swe-2026-14-04.png
swe-2026-14-05.png

구현의 최소형 — Kotlin(Spring Boot)


// 국내 Spring Boot 표준 레이어 구조

// 입구층 (Controller)

@RestController

@RequestMapping("/api/v1/orders")

class OrderController(private val orderService: OrderService) {


@PostMapping

fun createOrder(

@AuthenticationPrincipal user: UserDetails,

@RequestBody @Valid request: CreateOrderRequest

): ResponseEntity<OrderResponse> {

val order = orderService.createOrder(user.userId, request)

return ResponseEntity.status(HttpStatus.CREATED).body(order.toResponse())

}

}


// 업무층 (Service) — 비즈니스 규칙 중심

@Service

@Transactional

class OrderService(

private val orderRepository: OrderRepository,

private val paymentGateway: PaymentGateway, // 외부 의존은 인터페이스로

private val stockChecker: StockChecker,

) {

fun createOrder(userId: Long, request: CreateOrderRequest): Order {

// 비즈니스 규칙: 재고 확인 후 결제

stockChecker.check(request.productId, request.quantity)

val payment = paymentGateway.charge(userId, request.amount)

return orderRepository.save(Order.create(userId, request, payment))

}

}


// 데이터층 (Repository) — DB 접근

interface OrderRepository : JpaRepository<Order, Long> {

fun findByUserIdAndStatus(userId: Long, status: OrderStatus): List<Order>

}


추상화하지 않는다

⚠️ 단순한 CRUD에 대해 포트, 어댑터, 유스케이스, DTO, 팩토리를 과잉으로 나누면, 변경하기 쉬워지기 전에 읽기 어려워진다.


다음 경우에만 추상화를 강화하는 가치가 있다.

업무 규칙이 복잡하다

외부 의존을 교체하고 싶다 (결제 게이트웨이 전환 등)

테스트나 재이용의 형편으로 경계가 명확하게 필요하다


정리

아키텍처 이름보다, 의존 방향과 책무 분리 쪽이 중요하다. 먼저 작은 레이어 분리에서 시작한다.



3. 동기 처리, 비동기 처리, 이벤트 구동

이 섹션이 답하는 질문: 직접 호출해야 하는가, 큐나 이벤트로 흘려야 하는가.


왜 필요한가

모든 것을 동기 API로 연결하면, 하나의 지연이나 장애가 전체에 연쇄된다. 한편 처음부터 비동기나 이벤트 구동에 너무 기울면, 정합성과 디버그가 어려워진다.

swe-2026-14-06.png


처음에 넣어야 할 비동기화

다음은 동기 API에서 분리하기 쉽다.

카카오 알림톡·SMS 발송 — 동기에 두면 카카오 API 지연이 요청 지연으로 직결

이메일 전송

이미지 변환·리사이즈

주문 완료 후 리포트 생성

AI 추론이나 배치 평가

swe-2026-14-07.png


CQRS와 Event Sourcing의 위치

CQRS는 읽기 모델과 쓰기 모델을 나누는 사고 방식, Event Sourcing은 상태 변화를 이벤트로 저장하는 사고 방식이다. 둘 다 강력하지만 항상 필요한 것은 아니다.

적합한 것은 다음과 같은 경우다.

감사 추적이 중요하다 (금융·의료·공공)

복수의 읽기 뷰가 필요하다

상태 변화의 이력 그 자체가 가치를 갖는다


� 국내 금융 시스템 Event Sourcing: 은행·증권 시스템에서는 거래 이력의 불변성 보장을 위해 Event Sourcing 패턴이 활용된다. 일반 서비스에서 도입하기 전에 "감사 로그가 일반 DB로 충분하지 않은가"를 먼저 확인한다.


정리

처음은 동기로 좋다. 느린 처리와 부작용부터 순서대로 비동기화한다. 이벤트 구동은 소결합의 이익이 운용 복잡성을 상회하는 장면에서 사용한다.



4. 모노레포와 폴리레포

이 섹션이 답하는 질문: 코드베이스를 하나의 리포지토리로 통합해야 하는가.
swe-2026-14-08.png

판단 기준

프론트엔드와 백엔드에서 타입이나 스키마를 공유하고 싶다면 모노레포가 유리

팀이나 권한이 명확하게 나뉘어 있다면 폴리레포가 더 자연스럽다

먼저 리포지토리 수가 아닌, 릴리스 단위와 리뷰 도선으로 결정한다


도구의 위치

모노레포에서는 패키지 매니저의 workspaces에 더하여, 필요하다면 작업 실행이나 캐시 구조를 추가한다.

swe-2026-14-09.png
swe-2026-14-10.png


정리

모노레포인가 폴리레포인가에 절대적인 정답은 없다. 공유 변경의 쉬움과 독립 운용의 쉬움 중 어느 것을 우선하는가로 결정한다.



5. 클라이언트별 API 설계: BFF와 GraphQL

이 섹션이 답하는 질문: 웹과 모바일에서 필요한 데이터가 다를 때, API를 어떻게 구성하는가.


왜 필요한가

클라이언트가 늘어나면 공통 API만으로는 과잉 취득이나 부족 취득이 발생하기 쉽다. 화면별 사정으로 백엔드가 끌려가면 변경 비용이 올라간다.

swe-2026-14-11.png

판단 방법

먼저 공통 REST API로 시작한다

클라이언트 차이가 커지면 BFF를 검토한다

데이터 취득의 유연성이 지배적인 과제라면 GraphQL도 후보에 넣는다


� 국내 현황: 카카오·네이버의 대규모 서비스는 웹/모바일/파트너 API를 별도로 관리하는 BFF 패턴을 사용한다. 토스는 타입 안전성을 위해 tRPC를 일부 서비스에 도입한 사례가 있다.


정리

BFF는 "복수 클라이언트의 사정을 정리하기 위한 층"이며, 처음부터 필수가 아니다.



6. 도메인 주도 설계의 요점

이 섹션이 답하는 질문: 복잡한 업무 규칙을 어떻게 정리하는가.


왜 필요한가

업무가 복잡해지면 화면이나 DB 중심의 설계만으로는 언어가 어긋나기 시작한다. 같은 "주문"이라도, 판매, 배송, 청구에서 의미가 다른 경우가 있다.


DDD의 중요한 점은 패턴을 전부 도입하는 것이 아니라, 업무의 경계에 따라 모델을 나누는 것이다.

swe-2026-14-12.png

// DDD 컨텍스트 분리 예시 — 이커머스 주문 도메인

// "주문"이 각 컨텍스트에서 의미가 다름


// 판매 컨텍스트

package com.company.sales


data class Order(

val id: OrderId,

val customerId: CustomerId,

val items: List<OrderItem>, // 판매 관점: 어떤 상품을 얼마나?

val totalAmount: Money,

val status: SalesOrderStatus, // PENDING, CONFIRMED, CANCELLED

)


// 배송 컨텍스트

package com.company.delivery


data class ShipmentOrder(

val id: ShipmentId,

val salesOrderId: SalesOrderId, // 판매 컨텍스트와 연결

val deliveryAddress: Address,

val items: List<ShipmentItem>, // 배송 관점: 어디로, 무엇을?

val status: ShipmentStatus, // PREPARING, SHIPPED, DELIVERED

)


// 청구 컨텍스트

package com.company.billing


data class BillingOrder(

val id: BillingId,

val salesOrderId: SalesOrderId,

val paymentMethod: PaymentMethod, // 청구 관점: 어떻게 결제?

val amount: Money,

val status: BillingStatus, // PENDING, PAID, REFUNDED

)


먼저 도입하면 좋은 것

용어를 맞춘다 — 기획자·디자이너·개발자가 같은 언어 사용

컨텍스트마다 폴더나 모듈을 나눈다 (패키지 분리)

업데이트의 일관성을 어디서 지키는지 결정한다 (트랜잭션 경계)


정리

DDD는 복잡한 도메인에서 효과가 있다. 작은 CRUD에서 무리하게 풀세트 도입할 필요는 없다.



7. AI 기능을 전제로 한 아키텍처

이 섹션이 답하는 질문: LLM이나 에이전트를 넣으면, 설계의 어디가 바뀌는가.


왜 필요한가

AI 기능은 통상의 API보다 느리고, 비용이 높고, 비결정적이며, 외부 의존이 강하다. 따라서 기존의 동기적인 CRUD 설계를 그대로 적용하면 다루기 어렵다.

swe-2026-14-13.png


AI 기능 아키텍처 — 전형적인 구성

swe-2026-14-15.png


실무에서의 정석

AI 추론은 동기 API에 너무 많이 넣지 않는다

긴 처리는 작업화하고, 진행 취득이나 스트리밍을 사용한다

모델 호출을 앱 전체에 흩뿌리지 않고, 경계를 만든다 (추상 레이어)

멀티에이전트는 단일 에이전트로 부족해진 후에 도입한다


⚠️ 멀티에이전트는 "고도하기 때문에 뛰어나다"는 것이 아니다. 추가되는 것은 자유도뿐만 아니라, 상태 관리, 감사, 장애 해석의 복잡성이기도 하다.
� 국내 AI 서비스 아키텍처 고려사항: 개인정보가 포함된 데이터를 LLM에 전달할 경우 글로벌 API(Claude·GPT) 사용 시 개인정보 국외 이전 동의 필요. 금융·의료·공공 도메인은 NCP CLOVA Studio 또는 온프레미스 LLM 우선 검토.


정리

AI 기능을 넣으면 성능보다 비결정성과 운용성이 설계의 중심 과제가 된다. 단일 모델, 단일 실행 경로로 충분하다면, 거기서 넓히지 않는 편이 강하다.



8. 국내 아키텍처 실무 특이사항

전자정부 표준 프레임워크 (eGovFrame) 아키텍처

공공기관 프로젝트는 행정안전부의 eGovFrame을 기반으로 하는 경우가 많다.

swe-2026-14-14.png

eGovFrame은 Spring Boot 표준과 다른 관행이 있으므로 공공 프로젝트 투입 시 별도 학습 필요.


국내 배달/커머스 마이크로서비스 패턴

swe-2026-14-16.png


국내 대형 이커머스(배달의민족·쿠팡·무신사)는 위와 같이 도메인별 독립 서비스 + Kafka 이벤트 기반 아키텍처를 채택하는 경우가 많다.



✅ 이 장의 체크리스트

모놀리스, 모듈러 모놀리스, 마이크로서비스의 차이를 설명할 수 있는가

분할 판단을 팀 구성, 운용 능력, 정합성 요건으로 생각할 수 있는가

레이어 설계에서 의존 방향을 어떻게 맞춰야 하는지 설명할 수 있는가

동기, 작업 큐, 이벤트 구동의 사용 구분을 설명할 수 있는가

모노레포와 폴리레포를 릴리스 단위와 공유 코드의 관점에서 선택할 수 있는가

BFF와 GraphQL의 역할의 차이를 설명할 수 있는가

DDD의 경계 지어진 컨텍스트의 의미를 설명할 수 있는가

AI 기능을 넣을 때 응답 시간, 비용, 안전성, 감사성을 어떻게 설계에 넣는지 설명할 수 있는가

국내 이커머스(쿠팡·배달의민족)의 마이크로서비스 전환 패턴을 설명할 수 있는가

eGovFrame이 공공 프로젝트의 레이어 설계에 미치는 영향을 설명할 수 있는가


⚠️ 편집 노트: 본 문서는 지속적으로 보완 중입니다. 국내 기업 기술 블로그의 아키텍처 전환 사례(토스·카카오·배달의민족), eGovFrame 최신 버전 업데이트, Spring Boot 마이크로서비스 패턴 변화는 각 공식 문서와 기술 블로그를 통해 최신 정보를 확인하세요.



©2024-2026 MDRules dev., Hand-crafted & made with Jaewoo Kim.

이메일문의: jaewoo@mdrules.dev


AI강의/개발/기술자문, AI 업무 자동화 컨설팅 문의: https://talk.naver.com/ct/w5umt5


AI 업무 자동화/에이전트/워크플로우설계 컨설팅/AI교육: https://mdrules.dev


이 작가의 멤버십 구독자 전용 콘텐츠입니다.
작가의 명시적 동의 없이 저작물을 공유, 게재 시 법적 제재를 받을 수 있습니다.

brunch membership
AI개발자작가님의 멤버십을 시작해 보세요!

AI Workflow Architect, LLM Engineer, Vibe Engineering, Claude Code, AI 업무 자동화 컨설팅/AI강의

83 구독자

오직 멤버십 구독자만 볼 수 있는,
이 작가의 특별 연재 콘텐츠

  • 최근 30일간 24개의 멤버십 콘텐츠 발행
  • 총 44개의 혜택 콘텐츠
최신 발행글 더보기
이전 13화2026 웹 보안 완전 가이드