← 목록으로
📌 PINNED 완료 기타 백엔드 아키텍처 결정 2026.02.10 ✍️ 김도연

백엔드 - System Architecture

백엔드 - System Architecture
고민 포인트
기술 선택 이유

고민 1. 배포 단위 및 시스템 설계

요구사항을 바탕으로 기능 명세서 작성 및 구체화 - 2월 9일 진행

  • 기능 단위로 보았을때 추천 + 분석 + 고객 CRUD(로그인/상담 진행) + 관리자 + CRM 방향성의 발송
  • 각 서비스의 트래픽 분산도장애 범위의 분리 단위는 어떻게 가져가야 하는가 ⇒ 예를 들자면, 추천 시스템에서 실시간 또는 주기적인 분석을 통해 추천 알고리즘이 동작하는데 해당 부분에서 장애가 발생하면 고객 CRUD 부분까지 장애가 전파되며 기본적인 상담과 상품 조회 등을 하지 못하는 장애가 발생한다.

고민 2. 각 서비스들의 Database 분리와 배포 단위는 어떻게 분리해야 하는가

1) MVP

  • backend-app은 상시로 운영하며 고객 CRUD(로그인/세션), 상담(텍스트), 상품 조회/요금제/혜택, 관리자 API를 포함한다.
  • 추천 API는 초기에는 backend-app에 포함할 수 있으나, 이후 분리 배포가 가능해야 하므로 코드와 DB 경계는 미리 나누어 두는 방식으로 정리했다.

batch-worker는 상시 운영하지 않고 스케줄 또는 트리거에 의해 실행되며(raw 이벤트 전처리, KPI/통계 생성, 유저 피처 생성, 페르소나/리스크 생성, 추천용 인덱스 갱신을 수행한다).

notification-worker는 상시 운영하지 않거나 최소화하며 큐 기반으로 발송 요청을 처리하고 외부 SMS provider 호출과 재시도, 결과 저장을 수행한다.

프론트는 Next.js를 사용하며 customer/admin은 초기에는 하나의 Next 앱에서 라우팅(/admin)과 권한으로 분리하는 방식으로 운영해 비용과 운영 복잡도를 낮추는 방향으로 정리했다.

2) 확장

확장 단계에서는 추천/검색이 핫스팟이 되는 경우(호출량 증가, 검색/벡터 인덱스 부하 증가, 추천 장애가 core로 전파되는 리스크 증가)에 한하여 reco-service를 분리 배포 단위로 승격하는 방식으로 했다.

이때 reco-service는 retrieval(키워드/벡터/하이브리드), ranking(가중치 스코어링), 캐시, 추천 로그 적재를 담당하며, 고객 CRUD와 상담을 담당하는 backend-app과 런타임을 분리하여 장애 전파를 차단하는 것이 목적임.

추천과 분석의 연결 방식은 _“직접 조인/직접 결합”_이 아닌 _“분석이 만든 read-model을 추천이 읽는 방식”_으로 정리했다. 즉 이벤트(raw)가 쌓임 → 배치가 이를 집계하여

analytics

영역에 유저 피처/페르소나/리스크/KPI를 생성 → 추천은 이 피처를 읽어서 ranking을 수행하는 구조.


고민 3. 7주 동안의 프로젝트 기간동안 develop 서버(또는 stage 서버)+운영 서버(production) 형태로 배포 시 60만원이라는 제한된 비용 이내로 구축가능한가?

7주 기간 동안 항상 배포를 유지한채로 서버를 유지하지는 않는다. 하지만, 프론트엔드 측에서 렌더링 성능 향상을 위한 프레임 워크로 Next.Js를 선택하였고, Next.Js는 SSR(Sever Side Rendering) 방식을 채택함으로써 AWS에서 구축하려면, ECS 또는 EC2와 같은 인스턴스 유형을 사용해야 한다. 즉, 기존 React.Js 사용 시, S3에 정적 리소스로써 배포하는 비용보다 당연히 부담될 수 밖에 없으며, 고객(customer-web)와 관리자 페이지(admin-web)를 필수적으로 분리함으로써 추가적인 인스턴스 또는 ALB(Application Load Balencer)는 필수적이다. 뿐만 아니라, 추천 알고리즘에 사용되는 벡터DB(어떤 종류인지는 미정)를 사용하여 서버 리소스 비용은 큰 부담으로 다가오며 이는 Infra와 System 설계 단계에서 많은 부담.

여기서 초안에서 “customer-web과 admin-web 분리 시 인스턴스 또는 ALB가 필수”라는 부분은 초기 단계 기준으로는 필수라고 보지 않았다. 초기에는 하나의 Next 앱에서 /admin 경로와 권한으로 분리하는 방식으로 운영 가능하다고 판단했다. 이 방식은 서버 런타임을 1개로 유지할 수 있어 비용과 운영 복잡도를 낮추는 데 도움이 된다고 판단하여 필요해지는 시점(**트래픽/배포주기/권한 분리가 더 강하게 필요해지는 시점**)에만 customer/admin을 물리적으로 분리하는 방식으로 정리.

develop 서버를 포기하고 production 서버만을 두는 건 안전하다고 볼 수 있는가?

RDS 비용은 이전 프로젝트에서의 2개 비용을 직접 체감했는데 비용을 적게 가져갈 수 없을까?

DB 분리는 “처음부터 RDS를 여러 개로 쪼개는 방식”이 아니라, MVP 단계에서는 **RDS 1개 내부에서 스키마와 권한 경계를 강제**하여 비용을 낮추는 방식으로 정리.

core, reco, analytics, notification 스키마로 나누고, 서비스별 DB 계정을 분리하여 각 서비스가 자기 스키마에만 쓰기 권한을 가지도록 정리했다. 추천/코어 서비스가 필요한 데이터가 있을 경우에는 analytics가 만들어주는 read-model만 읽도록 하여 결합을 낮추는 방향.

GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA core TO core_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA reco TO reco_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA analytics TO batch_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA notification TO noti_app;

-- 앞으로 생성될 테이블도 자동으로 권한 부여
ALTER DEFAULT PRIVILEGES IN SCHEMA core
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO core_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA reco
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO reco_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA analytics
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO batch_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA notification
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO noti_app;

-- sequence 권한
ALTER DEFAULT PRIVILEGES IN SCHEMA core
  GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO core_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA reco
  GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO reco_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA analytics
  GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO batch_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA notification
  GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO noti_app;

고민 4. 이전 프로젝트에서 여러 번 시도하였지만 실패하였던 DDD 중심의 아키텍처의 적용

이번 프로젝트 요구사항을 분석하던 중 도메인 모델의 확장성을 고려하며 배포 단위를 고려했을때 DDD+모듈러 모노리스식이 가장 적합하다고 판단한다. 이유는 아래와 같다.

  1. 트래픽이 몰리는 지점이 다르므로(추천/분석 부분에서의 로그 수집 VS 클라이언트의 일반 사용 트래픽 부하) 배포 단위를 분할하고 스케일 범위를 다르게 가져가야 함.
  2. 배치 시스템은 비교적 많은 CPU 자원과 Disk I/O가 발생하므로 다른 서비스와 같이 DB 및 인스턴스 공유 시 다른 서비스의 장애는 분명하다.(장애 전파 가능성)
  3. 기능 명세서를 작성하기 이전 이벤트 스토밍은 아니지만 요구 사항 정의를 위해 포스트를 붙여가며 진행한 요구사항 추출과정에서 추천+분석+발송 이라는 명확한 도메인 모델의 추출

총 3번의 DDD 기반의 architecture 적용 시도와 완전 실패 및 부분 실패

결론:

  • DDD 적용이 아니라, 레이어드를 기본으로 두고 변화 가능성이 큰 지점Port로 분리하는 구조를 적용하는 것으로 판단.
  • 추천에서 후보군을 가져오는 방식은 SQL 기반에서 OpenSearch/Vector로 바뀔 수 있다고 판단했고, 이 지점은 **인터페이스(Port)**로 분리하여 **구현체(Adapter)**를 교체하는 방식으로 판단.
  • 이벤트 적재(S3/SQS), SMS 발송 provider, 캐시(Redis) 등 외부 연동도 동일하게 Port/Adapter로 분리하는 방식으로 정리했다. 이 방식은 레이어드 기반 개발 경험을 유지하면서도, 확장 단계에서 reco-service 분리나 검색/벡터 엔진 교체를 가능하게 하는 최소한의 유연성을 제공한다고 판단.

REST API 구현이니깐 모두 JPA를 사용하면 될까?

결론부터 말하면 “모두 JPA”로도 구현은 가능하다(아마?). 다만 우리 서비스 모델(추천/분석/배치/로그 집계가 핵심)에서 내가 판단하였을 때 SW에서 가능”과 “좋다”는 엄연히 다르다. JPA를 전부에 적용하면, 개발 속도는 초반에 확실히 빨라 보이지만 배치·집계·랭킹 쿼리에서 성능/예측 가능성/디버깅 비용이 급격히 올라갈 가능성이 크다.(쿼리 튜닝 힘듦/다중 조인 및 집계 어려움/양방향 매핑)

JPA / Querydsl / jOOQ / JDBC를 비교한 내용이다.(참고로 이미 기능 자체가 매우 hard한 주제이므로 최대한 러닝 커브가 낮다고 판단하는 선에서 선정 ex) Mybatis같은 경우는 xml 형태로 구성되기 때문에 제외했다.)

1) JPA

장점

단점

JPA vs Mybatis, 현직 개발자는 이럴 때 사용합니다. I 이랜서 블로그

[Hibernate Core Reference Guide Red Hat JBoss Web Server 3 Red Hat Documentation](https://docs.redhat.com/en/documentation/red_hat_jboss_web_server/3/html-single/hibernate_core_reference_guide/index#d0e340)

적합한 곳(우리 기준)

2) Querydsl

장점

단점

적합한 곳(우리 기준)

3) jOOQ

장점

단점

적합한 곳(우리 기준)

4) JDBC

장점

단점

적합한 곳(우리 기준)


결론

  1. 성능/운영 관점

    • 추천/분석은 “데이터 계산”이 많다 → SQL 튜닝 지점이 명확해야 운영이 된다.
    • JPA는 쿼리가 간접적으로 생성되어 병목 원인 추적이 느려질 가능성 존재.
    • 배치에서 대량 처리하면 flush/clear, batch size, fetch 전략 등 ORM 최적화 지식이 필요해져 러닝커브가 다시 생김.
  2. 팀 러닝커브 관점

    • “JPA만”은 초반엔 편하지만, 결국 집계/추천 로직에서 native SQL이 늘어나며
    • “JPA + 네이티브 혼합”으로 복잡도가 올라갈 가능성 존재
    • 차라리 분석/배치는 처음부터 SQL 도구(jOOQ/JDBC) 로 통일하는 편이 좋다고 판단.
  3. 아키텍처/결합도 관점(우리가 중요하게 본 부분)

#백엔드 #시스템 아키텍처 #설계