주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 자주 쓰는 서버 구조와 설계 패턴 - thought-corner/backend-roadmap GitHub Wiki

MVC 패턴

  • 애플리케이션을 데이터(Model), 화면(View), 제어(Controller) 세 가지 역할로 나누는 아키텍처 패턴이다.
  • 핵심 목적은 관심사의 분리(Separation of Concerns)로 서로 다른 책임을 가진 코드를 섞지 않는 것이다.
  • Model(데이터와 비즈니스 로직)
    • 애플리케이션의 핵심 데이터와 그 데이터를 다루는 규칙(비즈니스 로직)을 담당한다.
    • DB 접근, 계산, 상태 관리
    • ViewController가 어떻게 생겼는지 알지 못한다. 화면과 완전히 독립적이다.
  • View(화면 표현)
    • Model의 데이터를 사용자가 보는 형태(HTML, JSON 등)로 렌더링한다.
    • "어떻게 보여줄지"만 담당하고, 데이터를 직접 가공하거나 로직을 처리하지 않는다.
  • Controller(흐름 제어와 중재)
    • 사용자 요청을 받아 어떤 Model을 호출할지, 결과를 어떤 View에 넘길지 결정한다.
    • ModelView 사이의 다리 역할만 하고, 자신은 비즈니스 로직도 화면 렌더링도 직접 하지 않는다.

계층형 아키텍처

  • Controller(Presentation Layer) — 표현 계층
    • 외부(클라이언트)와 맞닿는 진입점. HTTP 요청을 받고 응답을 반환한다.
    • 요청 파라미터 검증, 인증·인가 확인, DTO ↔ 도메인 객체 변환을 담당한다.
    • 비즈니스 로직은 여기 두지 않는다. 받은 요청을 Service에 넘기고 결과를 돌려주는 데 집중한다.
  • Service(Business Layer) — 비즈니스 계층
    • 애플리케이션의 핵심 로직과 규칙이 모이는 곳이다.
    • 여러 Repository를 조합해 하나의 업무 단위를 처리하고, 트랜잭션 경계를 관리한다.
    • "무엇을 어떻게 처리할지"에 대한 판단이 전부 여기서 이뤄진다.
  • Repository(Persistence Layer) — 영속성 계층
    • 데이터베이스 접근만 전담한다. 쿼리 실행, 엔티티 저장·조회를 담당한다.

DDD와 전술 패턴

  • 복잡한 소프트웨어의 중심을 도메인(비즈니스 문제 영역)에 두고 설계하는 접근법이다.
  • 기술(DB, 프레임워크)이 아니라 비즈니스 규칙이 코드 구조를 이끌도록 하는 것이 핵심이다.
  • 전술적 패턴
    • Entity(엔티티) : 고유 식별자(ID)로 구분되는 객체. 속성이 바뀌어도 ID가 같으면 같은 객체이다.
    • Value Object(값 객체) : 식별자가 없고 값 자체로 동등성을 판단한다. 불변(Immutable)으로 설계하는 것이 원칙이다. 바꾸려면 새 객체를 만든다.
    • Aggregate(애그리거트) & Aggregate Root : 함께 변경되어야 하는 Entity·VO를 묶은 일관성의 경계이다. 트랜잭션은 애그리거트 하나 단위로 처리하는 것이 원칙이다.
    • Repository(리포지토리) : 애그리거트를 저장·조회하는 컬렉션 같은 인터페이스이다.
    • Domain Service(도메인 서비스) : 특정 Entity/VO 하나에 자연스럽게 담기지 않는, 여러 객체에 걸친 도메인 로직을 담는다. 계층형의 애플리케이션 Service와 달리, 여기엔 순수 도메인 규칙만 들어간다.
    • Factory(팩토리) : 생성 과정이 복잡한 애그리거트를 일관되게 만들어 주는 책임을 캡슐화한다. 생성 로직이 도메인 객체 밖으로 흩어지는 것을 막는다.
    • Domain Event(도메인 이벤트) : 도메인에서 의미 있는 사건을 표현한다. 발행하면 다른 애그리거트나 외부 시스템이 반응할 수 있어, 결합도를 낮추고 비동기 처리를 가능하게 한다.

❗계층형 아키텍처와의 관계

  • DDD는 보통 Application / Domain / Infrastructure 계층 위에서 구현된다.
  • DDD는 Repository 인터페이스를 도메인에 두고 구현을 Infrastructure로 밀어내(DIP), 도메인이 어떤 기술에도 의존하지 않는 가장 안쪽 핵심이 된다.
  • 위와 같은 DDD 구성 요소를 사용해 도메인 로직을 애그리거트 단위로 묶는다. 복잡한 모델을 애그리거트 단위로 관리할 수 있게 함으로써 복잡도를 낮추고 애그리거트에 관련 로직을 모아 응집도를 높인다. 이는 복잡한 도메인의 유지보수성을 높인다.

마이크로서비스 아키텍처

구분 모놀리식 (Monolithic) 마이크로서비스 (MSA)
구조 하나의 배포 단위에 모든 기능 통합 기능별로 독립된 여러 서비스
개발 초기 속도 빠름 (단순한 구조, 바로 시작) 느림 (인프라·분산 설계 선행)
배포 전체를 한 번에 배포 서비스별 독립 배포
확장(Scaling) 전체를 통째로 확장 (비효율) 필요한 서비스만 개별 확장
장애 격리 한 부분 장애가 전체로 전파 서비스 단위로 격리, 부분 장애
기술 스택 단일 스택으로 통일 서비스마다 다른 언어·DB 선택 가능
데이터 단일 DB, 트랜잭션 단순 DB per Service, 분산 트랜잭션 복잡
코드 복잡도 커질수록 강하게 결합, 유지보수 난이도↑ 서비스는 작지만 시스템 전체는 복잡
팀 구성 하나의 코드베이스 공유 서비스별 팀 독립 운영 가능
통신 메서드 호출 (인메모리, 빠름) 네트워크 호출 (지연·실패 고려 필요)
테스트/디버깅 로컬 통합 테스트 쉬움 분산 추적·통합 테스트 어려움
운영 부담 낮음 (배포·모니터링 단순) 높음 (오케스트레이션·모니터링 인프라 필요)

이벤트 기반 아키텍처

  • 이벤트 생성자는 이벤트를 생성해서 브로커에 전달하고 브로커는 해당 이벤트에 관심 있는 소비자에게 이벤트를 전달한다. 그럼 이벤트 소비자는 이벤트를 받아 적절하게 반응한다.
  • 예를 들어, 주문 시스템에서 '결제 완료' 이벤트가 발생하면 이 이벤트가 배송 시스템으로 전달되고 배송 시스템은 전달받은 주문 정보를 바탕으로 배송을 시작한다.

CQRS 패턴(Command Query Responsibility Segregation)

  • 명령과 조회가 서로 다른 데이터를 사용하는데도 하나의 모델로 명령과 조회를 모두 구현하면 모델이 복잡해지고 코드의 유지보수가 어려워진다.
  • 도메인이 복잡해질 경우에는 CQRS 패턴을 사용해서 단일 모델 사용으로 인해 발생하는 복잡도 문제를 해결할 수 있다.
  • CQRS는 명령을 위한 모델과 조회를 위한 모델을 분리하는 패턴이다. 여기서 명령은 시스템의 상태를 변경하는 것을, 조회는 상태를 조회하는 것을 의미한다.
  • CQRS는 명령 모델과 조회 모델을 구분하므로 각 기능에 맞게 모델을 구현할 수 있다는 이점이 있다. 단일 모델을 사용할 경우 조회 기능 때문에 명령 기능이 영향을 받거나 반대로 명령 기능 때문에 조회 기능이 영향을 받을 수 있다. CQRS는 두 모델을 분리함으로써 이런 상호 영향 문제를 최소화할 수 있다.
  • 또한 조회 모델이 별도로 존재하므로 조회 성능을 향상시키기 쉽다. 조회 모델에 캐시를 적용하거나 조회 전용 DB를 확장하는 방식이 이에 해당한다.
  • 그러나 각 기능마다 모델을 따로 만들어야 하므로 공수가 늘어난다. 따라서 추가되는 코드 대비 얻는 이점이 더 큰가를 따져보아야 한다. 단순한 모델에 CQRS를 적용하면 작업량만 늘어나고 얻는 이점이 적을 수 있다.
  • 또 다른 단점은 구현 기술이 늘어날 수도 있다는 점이다. 명령 모델과 조회 모델을 서로 다른 기술로 구현할 때도 많은데, 특히 높은 트래픽을 처리하기 위해 조회 모델을 별도 조회 전용 DB애 구축하는 경우가 있다. 이 경우에는 서로 다른 DB 간의 데이터 동기화를 위해 추가적인 메시징 수단을 도입해야 한다.