이 시리즈는 Spring Boot(Kotlin) 기반 서버를 처음 구성할 때
“실무에서 바로 쓰는 구조”를 그대로 따라 만들 수 있도록 정리한 개발 기록입니다.
기본적인 API 서버 셋팅을 잘 해놓으면,
이후 어떤 프로젝트든 같은 구조로 빠르게 개발할 수 있습니다.
📚 Spring Boot(Kotlin) 서버 기본 셋팅 — 시리즈 안내
(1편부터 12편까지 시리즈로 구성될 예정입니다)
- 왜 멀티 모듈 구조인가? (아키텍처 철학 & 전체 설계 편) ← 현재 글
- API Response 포맷 설계
- 글로벌 예외 처리(GlobalExceptionHandler)
- Swagger(OpenAPI) 설정
- Security(JWT) 기본 골격
- JWT TokenProvider
- Redis 설정
- Validation 설정
- Logging + MDC(traceId)
- application.yml 프로필 분리 (local/dev/prod)
- 멀티모듈 + JPA 기본 구조 정리
- 완성된 프로젝트 템플릿 git 공유
1편. 멀티 모듈 구조를 왜 사용하는가?
Spring Boot 프로젝트를 처음 만들면 대부분 이렇게 시작합니다:
src/main/kotlin
├─ controller
├─ service
├─ repository
├─ domain
즉, 단일 모듈 구조로 시작합니다. 초기에 서비스가 작을 땐 전혀 문제가 없습니다.
하지만 시간이 지나면서 문제가 하나씩 터지기 시작합니다.
⚠️ 1. 단일 모듈 구조의 문제점
1) 기능이 늘어날수록 Controller/Service가 늘어남
기능이 조금만 추가돼도 이렇게 늘어납니다:
- 로그인 / 소셜 로그인
- 푸시 알림
- 일정 관리
- 이미지 업로드
- 검색
- 관리자 페이지
- 외부 API 연동
- 배치 작업
2) 레이어드 아키텍처 규칙이 점점 무너짐
처음에는 이렇게 깨끗하게 시작합니다
- Controller → Service → Repository
하지만 시간이 지나면 이런 일들이 벌어지기 쉽습니다
- Controller에서 Service를 건너뛰고 Repository를 직접 호출
- Service에서 외부 API 호출
- 비즈니스 로직이 Controller/Service/Repository에 분산
- DTO 대신 Entity를 그대로 외부로 반환
이런 일이 생기는 이유는 파일이 많아지고 복잡해져도, 구조적으로 막아주는 장치가 없기 때문입니다.
3) 외부에서 들어오는 값을 그대로 내부로 넘기는 위험
클라이언트의 요청은 신뢰할 수 없는 외부 데이터입니다. 그런데 많은 단일 모듈 프로젝트가 이렇게 작성합니다:
@PostMapping("/signup")
fun signup(@RequestBody request: SignupRequest) =
userService.signup(request)
문제는 SignupRequest가 Service → Repository → Domain 계층까지
그대로 흘러가는 경우가 많다는 점입니다.
왜 위험할까요?
- 외부 API 스펙이 바뀌면 내부 코드가 연쇄적으로 깨짐
- 보안적으로 취약 (의도치 않은 필드가 그대로 전달될 수 있음)
- Domain 레벨의 무결성이 무너짐 (도메인 규칙이 DTO에 의존)
- 유지보수 난이도 증가 (어디까지가 외부 모델인지 경계가 모호)
그래서 필요한 것이 바로 “계층 간 매핑(DTO → Service Model → Entity)” 입니다.
즉,
- Request DTO (api 계층) : 클라이언트가 보내는 값을 받는 전용 모델
- Service 내부 모델(application 계층) : 비즈니스 로직이 다루기 편한 형태로 변환
- Entity (domain 계층) : DB와 매핑되는 순수 도메인 객체
이 구조를 자연스럽게 강제하려면, 멀티 모듈 구조가 훨씬 유리합니다.
모듈이 나뉘어 있으면 “외부 값 그대로 깊은 계층으로 흘러 들어가는 것”을 설계 차원에서 막기 쉬워집니다.
4) 배치(Batch)와 API가 섞이기 시작함
API 서버는 24시간 빠르게 응답해야 하고,
Batch는 대량 처리나 스케줄링을 담당합니다.
둘이 같은 모듈/같은 서버에 있으면:
- 배치 실행 중 장애 → API도 같이 다운
- 배치 로직이 무거워지면 API 응답 지연
- Batch만 수정해도 API 서버를 재시작해야 함
- 서로 다른 책임이 섞임 (SRP 위반)
어느 순간부터는 “API와 배치를 분리하고 싶다”는 생각이 들 수밖에 없습니다.
⚡ 그래서 등장한 구조: 멀티 모듈 아키텍처
위 문제들을 해결하기 위해, 이 시리즈에서는 프로젝트를 다음과 같이 나누어 사용합니다.
/api → Controller, Security, Filter, API 응답/요청 DTO
/application → Service, TokenProvider, Domain 규칙, Repository 인터페이스
/batch → Scheduler, Cron 작업
🟦 2. 멀티 모듈 구조의 장점
🟢 장점 1) 책임 분리가 명확하다 (SRP 준수)
| 모듈 | 역할 |
|---|---|
| api | 클라이언트 요청을 받고 응답을 만드는 영역 (Web Layer, Controller, Filter, Security) |
| application | 비즈니스 로직(Service), Domain 규칙, TokenProvider, Repository 인터페이스 |
| batch | 주기 작업, 대량 처리용 모듈 (스케줄러, Cron 기반 작업) |
덕분에 기능이 커져도 “어떤 코드가 어디 있어야 하는지”가 명확해집니다.
각 모듈은 자신의 책임에만 집중하게 되고, 구조가 무너지지 않습니다.
🟢 장점 2) 외부 입력값을 그대로 넘기지 않고 “계층 별 DTO”가 자연스럽게 분리
흐름을 정리하면 이렇게 됩니다.
- 클라이언트 요청 → Request DTO (api 계층)
- Request DTO → Service 내부 모델 (application 계층)
- Service 내부 모델 → Entity (domain 계층)
즉, 계층 간 매핑이 강제됩니다. 이 방식의 장점:
- 외부 스펙 변화가 내부 도메인을 직접 깨뜨리지 않음
- 보안적으로 안전 (불필요한 필드가 내부로 끼어들기 어려움)
- 도메인 모델(Entity)이 깔끔하게 유지됨
- 서비스 코드에서 “입력 모델”과 “도메인 모델”을 구분해서 다룰 수 있음
특히 Request/Response DTO는 api 모듈에,
실제 도메인/서비스 모델은 application 모듈에 둠으로써
“외부 값이 깊은 레이어까지 스며드는 것”을 구조적으로 막을 수 있습니다.
🟢 장점 3) API / Batch를 독립적으로 실행 가능
IntelliJ 기준으로,
ApiApplicationKt실행 → API 서버BatchApplicationKt실행 → 배치 서버
이렇게 서로 다른 Run Configuration으로 동시에 실행할 수 있습니다.
운영 환경에서도 각각 독립 배포가 가능해집니다.
🟢 장점 4) 테스트하기 쉽다
- api 모듈: Controller / Filter / Security 테스트
- application 모듈: Service 비즈니스 로직 테스트
- batch 모듈: 스케줄러 및 배치 로직 테스트
모듈이 분리되어 있기 때문에, 필요 없는 다른 계층을 끌어들이지 않고도 테스트를 작성할 수 있습니다.
🟢 장점 5) 대규모 서비스로 확장하기 좋은 구조
나중에 서비스가 커지면 이렇게 확장할 수도 있습니다:
/api
/application
/batch
/notification
/file
/auth
새로운 기능을 새 모듈로 분리해 나가는 것이 가능해집니다.
전체 프로젝트가 커져도, 모듈 단위로 관리하면서 복잡도를 통제할 수 있습니다.
🟦 3. 이 시리즈에서 다룰 모듈별 작업 맵
각 편이 어떤 모듈과 관련 있는지 한 번에 볼 수 있도록 정리해보면 아래와 같습니다.
| Step | 작업 내용 | 관련 모듈 |
|---|---|---|
| 1 | 왜 멀티 모듈 구조인가? (아키텍처 철학 & 전체 설계) | 전체 개요 |
| 2 | API Response 포맷 설계 | api / application |
| 3 | 글로벌 예외 처리(GlobalExceptionHandler) | api / application |
| 4 | Swagger(OpenAPI) 설정 | api |
| 5 | Security(JWT) 기본 골격 | api |
| 6 | JWT TokenProvider | application |
| 7 | Redis 설정 | application |
| 8 | Validation 설정 | api / application |
| 9 | Logging + MDC(traceId) | api |
| 10 | application.yml 프로필 분리 | 전체 |
| 11 | 멀티모듈 + JPA 기본 구조 정리 | api / application |
| 12 | 완성된 프로젝트 템플릿 zip 공유 | 전체 (GitHub / zip) |
🟩 다음 글 예고
다음 글에서는 실제 코드 작업을 시작합니다.
👉 2편. API Response 포맷 설계 — 왜 가장 먼저 해야 하는가?
공통 응답 포맷을 먼저 잡아두면, 이후 예외 처리, Security, Logging까지 모두 일관된 구조로 이어갈 수 있습니다.
다음 글:
https://jaemoi8.tistory.com/30
Spring Boot(Kotlin) — 2편. API Response 포맷 설계
📚 Spring Boot(Kotlin) 서버 기본 셋팅 — 시리즈 안내왜 멀티 모듈 구조인가? (아키텍처 철학 & 전체 설계 편) API Response 포맷 설계 ← 현재 글글로벌 예외 처리(GlobalExceptionHandler)Swagger(OpenAPI) 설정Secur
jaemoi8.tistory.com
'개발' 카테고리의 다른 글
| ⚠️ Spring Boot(Kotlin) — 3편. 글로벌 예외 처리(GlobalExceptionHandler) (0) | 2025.11.24 |
|---|---|
| Spring Boot(Kotlin) — 2편. API Response 포맷 설계 (0) | 2025.11.24 |
| DB 커넥션 풀과 Redis — 확장 가능한 서버 구조를 위한 핵심 정리 (0) | 2025.11.19 |
| 웹서버, WAS, 톰캣, JEUS의 관계 완전 정리 (0) | 2025.11.03 |
| 💡화이트리스트 기반 파일 업로드 구현 (0) | 2025.10.31 |