[Spring Boot] 사용자/관리자 API 분리를 위한 멀티모듈 설계 실전 가이드 (Common, User, Admin)

2025. 12. 13. 16:27·ServerDev/SpringBoot

[Spring Boot] 사용자/관리자 API 분리를 위한 멀티모듈 설계 실전 가이드 (Common, User, Admin)

백엔드 개발을 하다 보면, 일반 사용자용 API와 관리자용(Back-office) API를 분리해야 할 때가 옵니다. 트래픽 패턴도 다르고, 보안 요구사항도 다르며, 배포 주기도 다르기 때문입니다.

하지만 무턱대고 프로젝트를 2개로 쪼개면 User, Product 같은 엔티티와 도메인 로직을 양쪽에서 복사/붙여넣기 해야 하는 문제가 발생합니다.

오늘은 domain-common(공통), wecam-backend(사용자), wecam-admin-backend(관리자)로 구성된 멀티모듈 아키텍처를 설계하고, 실제 build.gradle 설정부터 설정하면서 했던 고민(Repository/Service 위치, 최근 트렌드)까지 다뤄보겠습니다.

 

 

해당 멀티모듈을 설정하며 발생한 오류와 에러는 아래 장을 참고해주세요.

https://ye-seul0-0.tistory.com/57

 

[Spring Boot 멀티모듈] domain-common 빌드 실패 원인과 해결 과정 정리 - Lombok, JPA @Entity 인식 안됨, Gradl

Spring Boot + Gradle 기반의 멀티모듈 프로젝트를 구성하던 중,공통 엔티티와 Enum, 도메인 객체를 모아둔 domain-common 모듈을 분리하여 관리하고 있었습니다. 프로젝트 구조는 다음과 같았습니다.Wecam-p

ye-seul0-0.tistory.com

 

 

 


 

1. 왜 멀티모듈인가? (Architecture Design)

 

단일 프로젝트(Monolithic)로 시작하면 초기 개발 속도는 빠르지만, 프로젝트가 커질수록 빌드 시간이 길어지고 수정의 영향 범위를 파악하기 힘들어집니다. 해당 프로젝트 설계의 핵심 목표는 다음과 같았습니다. 

 

  1. 독립 배포: 관리자 기능을 수정했는데 사용자 서버를 재배포할 필요가 없도록 한다.
  2. 재사용성: JPA Entity, Enum, 공통 Util은 한곳에서 관리한다.
  3. 일관성: Swagger, Flyway, Test 설정을 공통화한다.

구조도 (Module Structure)

wecam-all-backend (Root)
├── domain-common      : 엔티티, Enum, 공통 DTO/Exception (DB 의존성 O, 웹 의존성 X)
├── wecam-backend      : 일반 사용자용 API 서버 (Web)
└── wecam-admin-backend  : 관리자용 API 서버 (Web)

 

의존성 방향 (Dependency Direction)

  • wecam-backend → domain-common
  • wecam-admin-backend → domain-common
  • backend ↔ admin-backend (서로 의존하지 않음)

 


 

2. Gradle 설정 핵심 (Configuration)

루트 build.gradle에서 공통 설정을 잡고, 각 모듈은 필요한 것만 가져갑니다.

 domain-common (가볍게 유지하기)

이 모듈은 라이브러리(Jar) 처럼 동작해야 합니다. 따라서 bootJar를 비활성화하고, 웹 관련 의존성을 배제합니다.

// domain-common/build.gradle
plugins {
    id 'java-library' // api, implementation 구분을 위해 java-library 추천
}

bootJar { enabled = false } // 실행 가능한 jar로 만들지 않음
jar { enabled = true }

dependencies {
    // JPA Entity 및 Validation을 위한 최소 의존성
    api 'jakarta.persistence:jakarta.persistence-api:3.1.0'
    api 'org.hibernate.orm:hibernate-core:6.4.4.Final'
    implementation 'jakarta.validation:jakarta.validation-api:3.0.2'

    // Lombok 등 공통 유틸
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}
설정하면서 간과했던 점이,, 모듈을 다 만들고 보니
JPA 설정을 안해서 Repos 를 넣을지 말지 추후 생각하려 했는데,, 첫 스타트를 잘못 끊었었습니다..ㅜㅜ 모듈 설정 시 처음부터 웹 선택을 하지 않았어서.. 다시하려면 프로젝트를 새로 만들어야 되더라구요.. (찾아보면 다른 방법이 있겠지만 )


API Modules (backend & admin)

실제 실행되는 서버 모듈입니다. domain-common을 의존성으로 추가합니다.

// wecam-backend/build.gradle
dependencies {
    implementation project(':domain-common') // 공통 모듈 참조

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    // ... Security, Redis, Swagger 등
}

 

 


 

3. 멀티모듈 설계의 딜레마 

 

멀티모듈을 구성할 때 고민했던 것들입니다.. 

Q1. Repository는 어디에 두어야 할까? (Common vs API Module)

추천 전략: 공통 Repository는 domain-common에 둔다.

  • UserRepository의 findByEmail 같은 기본 조회 메서드는 사용자 API나 관리자 API나 똑같이 필요합니다.
  • 이를 각 모듈에 따로 만들면, 엔티티 필드명이 바뀔 때 수정 포인트가 2배가 됩니다.
  • 주의: 이를 위해서는 domain-common에 spring-boot-starter-data-jpa 의존성을 추가해야 합니다. (단, Web 의존성은 여전히 제외)
  • 심화: 만약 관리자용 통계 쿼리가 매우 복잡하다면?
    • 기본 UserRepository는 domain-common에 둡니다.
    • wecam-admin-backend 안에 AdminUserRepository를 따로 만들거나, QueryDSL Repository를 별도로 구현하는 방식을 섞어 씁니다.

Q2. Service(CRUD) 로직도 공통으로 빼야 할까?

  • 추천 전략: Service는 절대 공통으로 빼지 않는다 (각 모듈에 구현).
    • 이유 1 (Business Context): 같은 "회원가입"이라도 로직이 다릅니다.
      • 사용자 API: 이메일 인증 발송, 환영 쿠폰 지급, 약관 동의 확인.
      • 관리자 API: 강제 가입, 약관 동의 생략, 권한(Role) 수동 부여.
    • 이유 2 (God Object): CommonUserService를 만들면, 여기저기서 필요한 로직이 섞여 거대한 클래스가 되고 유지보수가 불가능해집니다.
    • 예외: 순수 유틸리티 성격의 로직(예: 이메일 포맷 검증기, 암호화 로직)은 공통으로 뺄 수 있습니다.

Q3. 요즘 멀티모듈을 다시 합치는 추세....?

최근 "Microservices에서 Modular Monolith로 회귀"하는 글들이 많이 보입니다. (Amazon Prime Video 사례 등)
또한 카카오도 마찬가지인거 같더라구요..? 그래서 멀티 모듈의 단점을 찾아봤습니다. 

MSA에 대해서는 다른 장에서 다루겠습니다...

https://ye-seul0-0.tistory.com/65

 

[Architectur] MSA(Microservices Architecture), 무조건 정답일까? (Monolith vs Modular Monolith 비교)

개발자 채용 공고를 보면 우대사항에 빠지지 않고 등장하는 단어가 있습니다. 바로 MSA (Microservices Architecture)입니다. 넷플릭스, 아마존 같은 테크 자이언트들이 MSA를 통해 엄청난 트래픽을 감당

ye-seul0-0.tistory.com

 

찾아본 내용과는 다르긴 하지만,, 실제 실무자분께 들었는데 추가적으로 멀티모듈 설계해둔 사람이 퇴사했을 때 돌아오는 게 크다고 합니다.. ㅜㅜ
  • 멀티모듈의 단점 (Overhead)
    1. 설정 복잡도: Gradle 설정이 어렵고, 의존성 충돌 해결이 까다롭습니다.
    2. 생산성 저하: 코드 한 줄 고치고 전체 빌드/테스트를 돌리는 시간이 길어질 수 있습니다.
    3. 배포 복잡성: 모듈별로 Docker 이미지를 만들고 관리하는 파이프라인이 필요합니다.
  • 언제 합쳐야 하나?
    • 팀 규모가 작거나(3~4명 이하), 트래픽이 많지 않은데 굳이 MSA 흉내를 내고 있다면 합치는 게 낫다고 합니다

4. 개발 시 마주칠 이슈와 해결책 

이 구조로 개발하면서 100% 마주친 문제였습니다..
순환 참조 (Circular Dependency)

  • 실수로 domain-common이 wecam-backend의 DTO를 참조하게 하면 안 됩니다. 화살표는 무조건 한 방향(API -> Common)으로만 흘러야 합니다.
  1. Flyway 스키마 관리
    • 두 서버가 하나의 DB를 씁니다. 마이그레이션 스크립트(V1__init.sql)는 어느 한쪽(주로 사용자 API)에서만 실행되도록 설정하거나, 배포 파이프라인에서 별도의 마이그레이션 단계를 둬야 충돌을 막을 수 있습니다.
  2. Swagger 중복
    • springdoc 사용 시 패키지 스캔 경로를 지정하지 않으면, 공통 모듈의 클래스들이 양쪽 Swagger에 어지럽게 나올 수 있습니다. packagesToScan 옵션을 통해 명확히 분리하세요!!

5. 마무리


가장 중요한 원칙은 domain-common을 최대한 얇게 유지하는 것입니다. 여기에 비즈니스 로직을 넣기 시작하면, 나중에 분리하고 싶어도 못 하는....

  • Common: Entity, Repository(기본), Enum, Utils
  • API Modules: Service, Controller, Security, DTO

이 경계만 잘 지킨다면, 괜찮다 봅니당


[참고]
이 글은 실제 공개 프로젝트인 wecam 의 Gradle 설정과 아키텍처를 기반으로 작성되었습니다.
JPA 설정, 프로파일 분리 등 전체 코드가 궁금하다면 깃허브를 참고해주세요!! 

'ServerDev > SpringBoot' 카테고리의 다른 글

[SpringBoot - WebSocket] 실시간 음성 스트리밍 서버 구조 (Project: SpeakNote)  (0) 2025.12.13
[Spring Boot 멀티모듈] domain-common 빌드 실패 원인과 해결 과정 정리 - Lombok, JPA @Entity 인식 안됨, Gradle 의존성 삽질기  (0) 2025.12.13
[SpringBoot & FormData] JSON + 파일 업로드: HttpMediaTypeNotSupported 에러 해결  (0) 2025.12.09
[JPA] 동시성 제어 : LockModeType.PESSIMISTIC_WRITE  (0) 2025.12.07
[SpringBoot&JPA] QR 코드 기반 게스트 계정 자동 생성 및 로그인 구현  (0) 2025.12.07
'ServerDev/SpringBoot' 카테고리의 다른 글
  • [SpringBoot - WebSocket] 실시간 음성 스트리밍 서버 구조 (Project: SpeakNote)
  • [Spring Boot 멀티모듈] domain-common 빌드 실패 원인과 해결 과정 정리 - Lombok, JPA @Entity 인식 안됨, Gradle 의존성 삽질기
  • [SpringBoot & FormData] JSON + 파일 업로드: HttpMediaTypeNotSupported 에러 해결
  • [JPA] 동시성 제어 : LockModeType.PESSIMISTIC_WRITE
yeseul-kim01
yeseul-kim01
  • yeseul-kim01
    슬 개발일지
    yeseul-kim01
  • 전체
    오늘
    어제
    • 분류 전체보기 (79)
      • 자격증 (1)
        • 정보보안기사 (0)
      • DevOps (17)
        • Docker (6)
        • Kubernetes (1)
        • GitHub Actions (0)
        • AWS (4)
        • Monitoring (1)
        • Nginx (1)
        • GCP (3)
      • ServerDev (34)
        • SpringBoot (13)
        • DJango (5)
        • FastAPI (14)
        • Next (0)
        • Flask (0)
        • Database (2)
      • Algorithm (2)
        • BFS (0)
        • DFS (1)
        • 다익스트라 (0)
      • CS (8)
      • Data Engineering (1)
      • AI&MLOps (2)
      • Architecture (6)
      • Software Engineering (0)
        • Library Packaging (0)
      • Project (5)
        • docx-generator (0)
        • speak-note (2)
        • ms-serving (1)
        • keyshield (2)
      • ProgrammingLanguages (3)
        • Python (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    grpc
    Django
    백엔드
    docker
    아키텍처
    실무일기-인프라편
    SpeakNote
    STT
    SpringBoot
    depends
    NLP부트캠프
    FastAPI
    multipartfile
    프로젝트기록-KeyShield
    MLops
    rag
    트러블슈팅
    멀티모듈
    실무일기-백엔드편
    Kubernetes
    FastAPI - CORS 마스터
    KServe
    동시성제어
    비동기처리
    프로젝트기록-speaknote
    아키텍처설계
    KeyShield
    di
    실시간시스템
    하이브리드아키텍처
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
yeseul-kim01
[Spring Boot] 사용자/관리자 API 분리를 위한 멀티모듈 설계 실전 가이드 (Common, User, Admin)
상단으로

티스토리툴바