목차
1. [프로젝트 표준 폴더 구조](#1-프로젝트-표준-폴더-구조)
2. [레이어드 아키텍처 vs 도메인 중심 구조](#2-레이어드-아키텍처-vs-도메인-중심-구조)
3. [application.yml 계층 구조](#3-applicationyml-계층-구조)
4. [외부 설정 우선순위](#4-외부-설정-우선순위)
5. [@ConfigurationProperties 활용](#5-configurationproperties-활용)
6. [멀티 모듈 프로젝트 구조](#6-멀티-모듈-프로젝트-구조)
7. [Kubernetes ConfigMap/Secret 연동](#7-kubernetes-configmapsecret-연동)
8. [퀴즈](#8-퀴즈)
1. 프로젝트 표준 폴더 구조
Spring Boot 프로젝트는 Maven/Gradle 표준 디렉터리 레이아웃을 따릅니다. 일관된 구조는 팀 협업과 유지보수성을 크게 향상시킵니다.
my-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/app/
│ │ │ ├── AppApplication.java
│ │ │ ├── config/
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── WebMvcConfig.java
│ │ │ │ └── SwaggerConfig.java
│ │ │ ├── controller/
│ │ │ │ ├── UserController.java
│ │ │ │ └── ProductController.java
│ │ │ ├── service/
│ │ │ │ ├── UserService.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── repository/
│ │ │ │ └── UserRepository.java
│ │ │ ├── domain/
│ │ │ │ ├── User.java
│ │ │ │ └── dto/
│ │ │ │ ├── UserRequest.java
│ │ │ │ └── UserResponse.java
│ │ │ ├── exception/
│ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ └── BusinessException.java
│ │ │ ├── security/
│ │ │ │ ├── JwtTokenProvider.java
│ │ │ │ └── JwtAuthFilter.java
│ │ │ └── util/
│ │ │ └── DateUtils.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── application-dev.yml
│ │ ├── application-staging.yml
│ │ ├── application-prod.yml
│ │ ├── db/
│ │ │ └── migration/
│ │ │ ├── V1__create_users.sql
│ │ │ └── V2__add_user_index.sql
│ │ ├── static/
│ │ │ ├── css/
│ │ │ └── js/
│ │ ├── templates/
│ │ │ └── email/
│ │ │ └── welcome.html
│ │ └── messages/
│ │ ├── messages.properties
│ │ └── messages_ko.properties
└── test/
├── java/
│ └── com/example/app/
│ ├── controller/
│ ├── service/
│ └── repository/
└── resources/
├── application-test.yml
└── data.sql
각 패키지별 역할
| 패키지 | 역할 | 포함 클래스 |
| ---------- | ----------------------------------- | ----------------------------------- |
| config | Spring 설정 클래스 모음 | SecurityConfig, WebMvcConfig |
| controller | HTTP 요청 처리 (Presentation Layer) | @RestController 클래스 |
| service | 비즈니스 로직 (Business Layer) | @Service 클래스 |
| repository | 데이터 접근 (Data Layer) | @Repository, JpaRepository |
| domain | 도메인 모델 | @Entity, DTO, VO |
| exception | 예외 처리 | @ControllerAdvice, Custom Exception |
| security | 보안 관련 컴포넌트 | Filter, Provider, Handler |
| util | 공통 유틸리티 | Static helper 메서드 |
2. 레이어드 아키텍처 vs 도메인 중심 구조
전통적 레이어 구조 (기능별 패키지)
com/example/app/
├── controller/
│ ├── UserController.java
│ ├── OrderController.java
│ └── ProductController.java
├── service/
│ ├── UserService.java
│ ├── OrderService.java
│ └── ProductService.java
└── repository/
├── UserRepository.java
├── OrderRepository.java
└── ProductRepository.java
장점: 레이어별로 파일을 찾기 쉬움
단점: 도메인 단위로 변경 시 여러 패키지를 동시에 수정해야 함
도메인 중심 구조 (도메인별 패키지)
com/example/app/
├── user/
│ ├── UserController.java
│ ├── UserService.java
│ ├── UserRepository.java
│ ├── User.java
│ └── UserDto.java
├── order/
│ ├── OrderController.java
│ ├── OrderService.java
│ └── Order.java
└── product/
├── ProductController.java
└── Product.java
장점: 도메인별 응집도가 높고 독립 배포에 유리
단점: 같은 레이어의 파일을 한 번에 보기 어려움
헥사고날 아키텍처 폴더 구조
com/example/app/
└── user/
├── application/
│ ├── port/
│ │ ├── in/
│ │ │ └── CreateUserUseCase.java
│ │ └── out/
│ │ └── UserRepository.java
│ └── service/
│ └── CreateUserService.java
├── domain/
│ └── User.java
└── adapter/
├── in/
│ └── web/
│ └── UserController.java
└── out/
└── persistence/
└── UserJpaRepository.java
헥사고날 아키텍처는 외부 의존성(DB, 메시지 브로커 등)을 인터페이스로 격리하여 테스트 용이성과 교체 가능성을 높입니다.
3. application.yml 계층 구조
기본 구조와 Profile 분리
Spring Boot 2.4 이후에는 단일 파일에서 `---` 구분자로 여러 프로파일을 정의할 수 있습니다.
application.yml - 공통 설정
spring:
application:
name: my-service
jackson:
default-property-inclusion: non_null
time-zone: Asia/Seoul
logging:
level:
root: INFO
com.example: DEBUG
management:
endpoints:
web:
exposure:
include: health,info,metrics
dev 프로파일
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:postgresql://localhost:5432/devdb
username: devuser
password: devpass
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
logging:
level:
org.hibernate.SQL: DEBUG
staging 프로파일
spring:
config:
activate:
on-profile: staging
datasource:
url: jdbc:postgresql://staging-db:5432/stagingdb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
prod 프로파일
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://prod-db:5432/proddb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
logging:
level:
root: WARN
com.example: INFO
별도 파일로 Profile 분리 (권장 방식)
대규모 프로젝트에서는 파일을 분리하면 관리가 더 쉬워집니다.
resources/
├── application.yml # 공통 설정
├── application-dev.yml # 개발 환경
├── application-staging.yml # 스테이징 환경
└── application-prod.yml # 운영 환경
프로파일 활성화 방법:
커맨드라인으로 활성화
java -jar app.jar --spring.profiles.active=prod
환경변수로 활성화
export SPRING_PROFILES_ACTIVE=prod
4. 외부 설정 우선순위
Spring Boot는 다양한 소스에서 설정을 읽어오며, 우선순위가 높은 것이 낮은 것을 덮어씁니다.
우선순위 (높은 것이 낮은 것을 덮어씀)
1. 커맨드라인 인자 (`--server.port=9090`)
2. `SPRING_APPLICATION_JSON` 환경변수 (인라인 JSON)
3. `ServletConfig` 초기화 파라미터
4. `ServletContext` 초기화 파라미터
5. JNDI 속성 (`java:comp/env/`)
6. Java 시스템 프로퍼티 (`System.getProperties()`)
7. OS 환경변수
8. 무작위 값 프로퍼티 소스 (`random.\*)
9. 프로파일별 외부 application 파일 (`application-dev.yml` — JAR 외부)
10. 외부 application 파일 (`application.yml` — JAR 외부)
11. 프로파일별 내부 application 파일 (`application-dev.yml` — JAR 내부)
12. 내부 application 파일 (`application.yml` — JAR 내부)
13. `@PropertySource` 애노테이션
14. 기본값 (`SpringApplication.setDefaultProperties`)
환경변수를 이용한 설정 오버라이드
프로퍼티명의 점(.)은 밑줄(_)로, 대문자로 변환
export SERVER_PORT=9090
export SPRING_DATASOURCE_URL=jdbc:postgresql://prod-db:5432/mydb
export SPRING_DATASOURCE_USERNAME=produser
커맨드라인 인자 예시
java -jar app.jar \
--server.port=9090 \
--spring.profiles.active=prod \
--spring.datasource.url=jdbc:postgresql://prod-db:5432/mydb
5. @ConfigurationProperties 활용
`@ConfigurationProperties`는 관련 설정을 타입 안전한 방식으로 묶어 관리할 수 있게 해줍니다.
application.yml 설정
app:
api-key: my-secret-key
timeout: 30s
allowed-hosts:
- api.example.com
- admin.example.com
retry:
max-attempts: 3
delay: 1s
mail:
host: smtp.example.com
port: 587
from: no-reply@example.com
@ConfigurationProperties 클래스
@ConfigurationProperties(prefix = "app")
@Validated
@Getter
@Setter
public class AppProperties {
@NotNull
private String apiKey;
private Duration timeout = Duration.ofSeconds(30);
private List<String> allowedHosts = new ArrayList<>();
private Retry retry = new Retry();
private Mail mail = new Mail();
@Getter
@Setter
public static class Retry {
private int maxAttempts = 3;
private Duration delay = Duration.ofSeconds(1);
}
@Getter
@Setter
public static class Mail {
private String host;
private int port = 587;
private String from;
}
}
Bean 등록
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
또는 클래스에 직접 `@Component` 추가:
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties { ... }
IDE 자동완성 지원 (메타데이터 생성)
이 의존성을 추가하면 컴파일 시 `META-INF/spring-configuration-metadata.json`이 생성되어 IDE에서 application.yml 자동완성이 동작합니다.
6. 멀티 모듈 프로젝트 구조
대규모 프로젝트에서는 단일 모듈보다 멀티 모듈 구조가 코드 재사용과 경계 분리에 유리합니다.
Maven 멀티 모듈 구조
my-project/ # Parent POM
├── pom.xml # Parent POM
├── my-api/ # REST API 모듈
│ ├── pom.xml
│ └── src/main/java/
│ └── com/example/api/
├── my-domain/ # 도메인/비즈니스 로직 모듈
│ ├── pom.xml
│ └── src/main/java/
│ └── com/example/domain/
├── my-infrastructure/ # DB, 외부 API 연동 모듈
│ ├── pom.xml
│ └── src/main/java/
│ └── com/example/infra/
└── my-common/ # 공통 유틸리티 모듈
├── pom.xml
└── src/main/java/
└── com/example/common/
Parent pom.xml 예시
모듈 간 의존성 관리
my-api → my-domain → my-common
my-api → my-infrastructure → my-domain
순환 의존성이 발생하면 컴파일 오류가 발생합니다. 공통 인터페이스를 별도 모듈로 분리하여 순환을 방지합니다.
7. Kubernetes ConfigMap/Secret 연동
ConfigMap을 통한 설정 주입 (환경변수 방식)
kubernetes/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
data:
SPRING_PROFILES_ACTIVE: 'prod'
SERVER_PORT: '8080'
SPRING_DATASOURCE_URL: 'jdbc:postgresql://db-service:5432/mydb'
kubernetes/deployment.yaml
spec:
containers:
- name: my-app
envFrom:
- configMapRef:
name: my-app-config
- secretRef:
name: my-app-secret
Secret을 통한 민감 정보 주입
Secret 생성
kubectl create secret generic my-app-secret \
--from-literal=SPRING_DATASOURCE_USERNAME=produser \
--from-literal=SPRING_DATASOURCE_PASSWORD=secretpass \
--from-literal=JWT_SECRET=myJwtSecretKey
볼륨 마운트 방식 (파일로 주입)
ConfigMap을 파일로 마운트하면 application.yml 전체를 외부에서 관리할 수 있습니다.
kubernetes/deployment.yaml
spec:
volumes:
- name: config-volume
configMap:
name: my-app-config-files
containers:
- name: my-app
volumeMounts:
- name: config-volume
mountPath: /app/config
env:
- name: SPRING_CONFIG_LOCATION
value: 'classpath:/,file:/app/config/'
spring-cloud-kubernetes 연동
Spring Cloud Kubernetes를 사용하면 ConfigMap 변경 시 애플리케이션 재시작 없이 설정을 동적으로 갱신할 수 있습니다.
spring:
cloud:
kubernetes:
config:
enabled: true
name: my-app-config
namespace: default
reload:
enabled: true
mode: event
period: 15000
8. 퀴즈
**정답:** 운영 환경에서 애플리케이션을 재빌드하지 않고 설정을 변경할 수 있도록 하기 위해서입니다.
**설명:** Spring Boot의 외부 설정 우선순위 체계는 12-Factor App 원칙을 따릅니다. JAR 외부의 `application.yml`이나 환경변수, 커맨드라인 인자가 JAR 내부 설정보다 높은 우선순위를 가집니다. 이를 통해 같은 JAR 파일을 개발/스테이징/운영 환경에 그대로 배포하고, 설정만 환경별로 다르게 주입할 수 있습니다.
**정답:** 애플리케이션 시작 시 설정값의 유효성을 검사하여, 잘못된 설정으로 인한 런타임 오류를 사전에 방지하기 위해서입니다.
**설명:** `@Validated`와 JSR-303 Bean Validation 애노테이션(`@NotNull`, `@Min`, `@Max` 등)을 함께 사용하면, Spring Boot 애플리케이션 구동 시점에 설정값이 유효한지 검사합니다. 필수 설정이 누락되거나 값이 범위를 벗어나면 즉시 `BindValidationException`이 발생하여 빠른 피드백을 제공합니다.
**정답:** 공통 인터페이스나 공유 도메인 모델을 별도의 공통 모듈(common/shared 모듈)로 분리하고, 의존성 방향을 단방향으로 유지합니다.
**설명:** A 모듈이 B 모듈에 의존하고, B 모듈이 A 모듈에 의존하는 순환 의존성이 발생하면 컴파일이 불가능합니다. 두 모듈이 공통으로 필요한 인터페이스나 DTO를 별도의 `common` 모듈로 분리하면, A와 B가 모두 `common`에 의존하는 단방향 구조를 만들 수 있습니다.
**정답:** spring-cloud-kubernetes-client-config를 사용하고 `spring.cloud.kubernetes.reload.enabled=true`로 설정합니다.
**설명:** Spring Cloud Kubernetes는 ConfigMap의 변경을 감지하는 두 가지 모드를 제공합니다. `event` 모드는 Kubernetes API 이벤트를 감지하여 즉시 반응하고, `polling` 모드는 주기적으로 ConfigMap을 확인합니다. `@RefreshScope` 애노테이션이 붙은 Bean은 ConfigMap 변경 시 자동으로 재초기화됩니다.
**정답:** 해당 설정 블록이 특정 프로파일이 활성화되었을 때만 적용되도록 지정합니다.
**설명:** Spring Boot 2.4부터 단일 `application.yml` 파일에서 `---` 구분자로 여러 문서 블록을 나눌 수 있습니다. 각 블록에 `spring.config.activate.on-profile`을 지정하면 해당 프로파일이 활성화된 경우에만 그 블록의 설정이 적용됩니다. 이전 방식인 `spring.profiles`는 더 이상 권장하지 않으며 `spring.config.activate.on-profile`을 사용해야 합니다.
현재 단락 (1/347)
1. [프로젝트 표준 폴더 구조](#1-프로젝트-표준-폴더-구조)