Spring · 설정, 자동 구성, 프로파일
Spring Boot? 의 가장 큰 매력은 "설정을 거의 안 해도 알아서 돌아간다" 입니다. 라이브러리를 하나 추가하기만 하면, Spring이 "아, 이걸 쓰려는구나" 하고 필요한 부품을 스스로 조립해 줍니다(자동 구성). 그리고 바꾸고 싶은 값만 설정 파일 에 적으면 그 부분만 덮어쓰입니다. 이 페이지에서는 스타터로 기능을 들여오는 법, 자동 구성의 원리, 설정값을 외부에서 주입하는 법, 환경별로 다르게 동작시키는 프로파일 까지 차근차근 풀어봅니다.
1. 스타터 — 기능을 묶음으로 들여오기
어떤 기능을 쓰려면 보통 라이브러리 여러 개를 일일이 골라 버전까지 맞춰 넣어야 합니다.
스타터(starter) 는 이 번거로움을 없애 줍니다. "웹을 만들고 싶다"면 spring-boot-starter-web
하나만 넣으면, 그 안에 필요한 라이브러리(웹 서버 톰캣, JSON 변환기 잭슨, MVC 등)가 검증된 버전 조합으로 한 묶음
으로 딸려 옵니다. 마치 라면을 끓일 때 면·스프·건더기가 한 봉지에 들어 있는 것과 같습니다.
아래는 이 프로젝트의 build.gradle 에 실제로 들어 있는 주요 스타터입니다.
| 스타터 | 들여오는 기능 |
|---|---|
spring-boot-starter-web | 웹·REST API? 서버. 내장 톰캣 + Spring MVC + 잭슨(JSON) 포함. |
spring-boot-starter-data-jpa | JPA?/Hibernate로 DB를 객체처럼 다루는 ORM? 기능. |
spring-boot-starter-data-jdbc | JPA보다 가벼운, JDBC 기반 데이터 접근. |
spring-boot-starter-security | Spring Security? — 인증·인가(로그인·권한) 기능. |
spring-boot-starter-validation | Bean Validation? — 입력값 검증(@NotNull 등). |
spring-boot-starter-actuator | 운영용 모니터링 엔드포인트(상태 점검 등). 아래 7번에서 다룸. |
spring-boot-starter-jooq | jOOQ로 타입 안전한 SQL을 작성하는 기능(DSLContext 자동 구성). |
spring-boot-starter-test | 테스트 도구 묶음(JUnit, Mockito, AssertJ 등). 테스트에서만 사용. |
이 프로젝트는 스타터 외에도 PostgreSQL 드라이버, Liquibase, JWT(jjwt), QueryDSL, MyBatis, Lombok 등을 직접 의존성으로 넣어 씁니다. 스타터는 "묶음 주문"일 뿐, 개별 라이브러리를 따로 추가하는 것도 얼마든지 가능합니다.
2. 자동 구성 — 클래스패스를 보고 알아서 조립
스타터로 라이브러리를 들여오면, Spring Boot가 자동 구성(auto-configuration) 으로 필요한 빈(객체)을 스스로 만들어 등록합니다. 핵심 아이디어는 단순합니다 — "있는 부품을 보고 판단한다." 예를 들어 클래스패스(앱이 실행될 때 들고 있는 라이브러리 목록)에 PostgreSQL 드라이버가 보이면 "DB를 쓰려는구나" 하고 데이터소스 빈을 자동으로 준비합니다.
@SpringBootApplication 은 세 가지를 묶은 애너테이션입니다.
- @SpringBootConfiguration — 사실상
@Configuration. 이 클래스가 설정의 출발점임을 표시. - @EnableAutoConfiguration — 자동 구성 기능을 켠다. 이게 핵심 스위치.
- @ComponentScan — 이 클래스가 있는 패키지(여기선
com.example.study)와 그 하위를 빈으로 스캔.
자동 구성이 "있는 부품을 보고 판단한다"는 것은, 내부적으로 조건부 구성 애너테이션으로 이루어집니다. 빈을 등록하기 전에 "이 조건이 맞을 때만 등록해"라고 단서를 다는 것이죠.
| 조건 애너테이션 | 의미 — "이럴 때만 빈을 등록" |
|---|---|
@ConditionalOnClass | 특정 클래스가 클래스패스에 있을 때만. (예: JPA 라이브러리가 있으면 JPA 설정을 켠다) |
@ConditionalOnMissingBean | 같은 타입 빈이 아직 없을 때만. 즉 내가 직접 만든 빈이 있으면 자동 구성은 비켜선다. |
@ConditionalOnProperty | 특정 설정값이 있을 때만. (예: 어떤 기능을 true로 켰을 때만) |
@ConditionalOnMissingClass | 특정 클래스가 없을 때만. |
@ConditionalOnMissingBean 이
바로 이 "주인이 이미 해 놨으면 건드리지 않는다"는 매너입니다.
이 "주인이 해 놓은 게 우선"이라는 성질 덕분에 자동 구성은 비침습적(non-invasive) 입니다.
내가 @Bean 으로 직접 만든 객체가 있으면 자동 구성은 양보하고 물러납니다. 그래서 기본은
알아서 돌아가되, 원하는 부분만 직접 손볼 수 있습니다. 어떤 자동 구성이 적용됐는지 궁금하면 실행 시
--debug 옵션을 주면 적용/미적용 내역이 로그로 나옵니다.
3. 외부 설정 — properties와 yml
코드 안에 값을 박아두면 바꿀 때마다 다시 빌드해야 합니다. Spring Boot는 값을 코드 바깥(외부) 에
두도록 권합니다. 가장 흔한 곳이 src/main/resources 의 application.properties 또는
application.yml 입니다. 둘은 적는 문법만 다르고 역할은 같습니다.
// application.properties — 키=값, 평면적 server.port=8084 spring.datasource.url=jdbc:postgresql://localhost:5432/studydb spring.jpa.show-sql=true
# application.yml — 계층(들여쓰기) 구조, 중복 접두사를 줄임
server:
port: 8084
spring:
datasource:
url: jdbc:postgresql://localhost:5432/studydb
jpa:
show-sql: true이 프로젝트는 application.properties 를 사용합니다. 같은 설정을 두 형식으로 적은 것뿐, 내용은 동일합니다.
3-1. 설정 우선순위 — 누가 누구를 덮어쓰나
설정값은 한 곳에만 있는 게 아닙니다. 파일·환경변수·실행 옵션 등 여러 곳에서 올 수 있고, 같은 키가 겹치면 우선순위가 높은 쪽이 이깁니다. 아래는 위로 갈수록(아래를 덮어씀) 강한 순서입니다.
| 설정 출처 | 우선순위 |
|---|---|
커맨드라인 인자 --server.port=9000 | 가장 강함 (다른 모든 것을 덮어씀) |
OS 환경변수 SPRING_DATASOURCE_URL=... | 강함 |
| application-{프로파일}.properties | 중간 (기본 파일을 덮어씀) |
| application.properties / .yml (기본) | 약함 |
| 코드에 박힌 기본값 | 가장 약함 |
비밀번호 같은 값을 파일에 적기 꺼려진다면, 파일엔 개발용 기본값만 두고 운영 서버에서는 환경변수로 덮어쓰면 됩니다.
환경변수는 점(.)을 못 쓰므로 대문자+밑줄로 적습니다. 이것을 느슨한 바인딩(relaxed binding) 이라 합니다.
예를 들어 spring.datasource.url 은 환경변수 SPRING_DATASOURCE_URL 로,
spring.jpa.hibernate.ddl-auto 는 SPRING_JPA_HIBERNATE_DDL_AUTO 로 적으면 같은 키에 연결됩니다.
재빌드 없이 실행 시점에 --server.port=9000 같은 커맨드라인 인자로도 바꿀 수 있습니다.
4. 설정값을 코드로 주입받기
파일에 적은 값을 코드에서 꺼내 쓰는 방법은 두 가지입니다. 값 하나를 콕 집어 넣는 @Value 와,
관련 값을 묶어 객체로 받는 @ConfigurationProperties 입니다.
4-1. @Value — 값 하나씩 주입
@Value("${키}") 로 설정값 하나를 필드에 바로 꽂습니다. : 뒤에 기본값도 줄 수 있어,
그 키가 없으면 기본값이 쓰입니다. 간단하지만 값이 많아지면 흩어져서 관리가 번거롭습니다.
@Service
public class JwtService {
@Value("${app.jwt.secret}") // 설정에서 비밀키 주입
private String secret;
@Value("${app.jwt.expirationMs}") // 토큰 만료 시간(ms)
private long expirationMs;
@Value("${app.stage:dev}") // 없으면 기본값 "dev"
private String stage;
}4-2. @ConfigurationProperties — 묶음으로 타입 안전하게
같은 접두사(prefix)로 시작하는 값들을 한 객체에 통째로 받습니다. 키 이름이 필드 이름과 자동으로 연결(바인딩)되고, 타입(숫자·불리언 등)도 알아서 변환됩니다. 관련 설정이 한 곳에 모여 읽기 좋고, 오타가 줄어듭니다.
@ConfigurationProperties(prefix = "app.jwt") // app.jwt.* 를 묶어서 바인딩
public class JwtProperties {
private String secret; // app.jwt.secret
private long expirationMs; // app.jwt.expirationMs
private long refreshExpirationMs; // app.jwt.refreshExpirationMs
// getter / setter
}이렇게 만든 설정 객체를 빈으로 등록하는 방법은 두 가지입니다.
| 방법 | 설명 |
|---|---|
@EnableConfigurationProperties(JwtProperties.class) | 설정 클래스 위에 붙여 특정 프로퍼티 클래스만 빈으로 등록. |
@ConfigurationPropertiesScan | 지정 패키지를 훑어 @ConfigurationProperties 클래스를 모아서 등록. |
@ConfigurationProperties 클래스에 @Validated 를 붙이고 필드에
검증? 애너테이션(@NotBlank 등)을 달면,
잘못된 설정값으로 앱이 시작될 때 바로 실패 시켜 알려줍니다. 운영 중 엉뚱한 값으로 도는 사고를 막는 안전장치입니다.
@Value 에는 이런 검증 기능이 없습니다.
5. 프로파일 — 환경별로 다르게
개발 PC, 테스트 서버, 운영 서버는 DB 주소나 로그 수준이 서로 다릅니다. 프로파일(profile) 은 이런 "환경 꼬리표"입니다. 환경마다 다른 설정 파일과 빈을 준비해 두고, "지금은 어느 환경"인지만 골라 켜면 그에 맞는 것이 적용됩니다.
| 요소 | 설명 |
|---|---|
application-{프로파일}.properties | 프로파일별 설정 파일. 예: application-dev.properties, application-prod.properties. |
spring.profiles.active=prod | 지금 켤 프로파일을 지정. 켜진 프로파일 파일이 기본 파일을 덮어씀. |
@Profile("prod") | 그 프로파일일 때만 등록되는 빈/설정 클래스를 표시. |
# application-dev.properties — 개발 환경 spring.datasource.url=jdbc:postgresql://localhost:5432/studydb spring.jpa.show-sql=true # 개발 땐 SQL을 콘솔에 보기
# application-prod.properties — 운영 환경 spring.datasource.url=jdbc:postgresql://db.prod:5432/studydb spring.jpa.show-sql=false # 운영 땐 SQL 로그를 끔
// 운영에서만 동작하는 빈
@Configuration
@Profile("prod")
public class ProdMailConfig {
@Bean
public MailSender realMailSender() { /* 실제 메일 발송 */ return null; }
}실행할 때 --spring.profiles.active=prod 커맨드라인 인자나 SPRING_PROFILES_ACTIVE=prod
환경변수로 켤 수 있습니다. 아무것도 안 켜면 application.properties 의 기본값만 적용됩니다.
이 프로젝트는 프로파일별 파일을 따로 두는 대신, app.stage 라는 자체 설정값(dev | test | prod)으로
단계를 구분합니다.
6. 직접 구성 — 내가 손으로 빈을 정의
자동 구성으로 부족하거나, 외부 라이브러리 객체를 내 방식대로 만들어야 할 때는 직접 빈을 정의합니다. (이 주제는 IoC · DI · 빈 페이지에서 더 깊이 다룹니다.)
| 애너테이션 | 역할 |
|---|---|
@Configuration + @Bean | 설정 클래스 안의 메서드가 반환하는 객체를 빈으로 등록. |
@Import | 다른 설정 클래스를 끌어와 합친다. |
@Conditional(...) | 특정 조건이 맞을 때만 빈을 등록(조건은 코드로 정의). |
@Configuration
public class AppConfig {
@Bean // 외부 라이브러리 객체를 직접 조립
public ObjectMapper objectMapper() {
ObjectMapper m = new ObjectMapper();
m.registerModule(new JavaTimeModule());
return m;
}
}7. Actuator — 앱의 건강검진 창구
spring-boot-starter-actuator 는 운영 중인 앱의 상태를 들여다보는 엔드포인트(점검 창구) 들을
열어 줍니다. 가장 많이 쓰는 것이 /actuator/health 로, "앱과 DB가 정상인가?"를 알려줍니다.
로드밸런서나 쿠버네티스가 이 창구를 주기적으로 두드려 살아 있는지 확인합니다.
| 엔드포인트 | 알려주는 것 |
|---|---|
/actuator/health | 앱·DB 등 구성요소의 정상 여부(UP/DOWN). |
/actuator/info | 앱 정보(버전 등, 직접 채워 넣음). |
/actuator/metrics | 메모리·요청 수 같은 지표. |
보안상 기본으로는 일부 창구만 열려 있습니다. 어떤 창구를 웹으로 공개할지는 설정으로 고릅니다.
# health, info 창구만 웹으로 공개 management.endpoints.web.exposure.include=health,info
8. 실제 프로젝트 application.properties
아래는 이 프로젝트의 src/main/resources/application.properties 에 실제로 들어 있는 주요 설정값입니다.
(비밀키 값 자체는 보안을 위해 생략)
| 키 | 값 / 의미 |
|---|---|
spring.application.name | 이 프로젝트 — 앱 이름. |
server.port | 8084 — 서버가 뜨는 포트. |
spring.datasource.url | jdbc:postgresql://localhost:5432/studydb?currentSchema=lds — DB 주소(스키마 lds). |
spring.datasource.driverClassName | org.postgresql.Driver — PostgreSQL 드라이버. |
spring.datasource.username | postgres — DB 접속 계정. |
spring.jpa.open-in-view | true — 요청이 끝날 때까지 영속성 컨텍스트 유지. |
spring.jpa.show-sql | true — 실행되는 SQL을 콘솔에 출력. |
spring.jpa.properties.hibernate.format_sql | true — SQL 로그를 보기 좋게 줄바꿈. |
spring.liquibase.change-log | classpath:/db/changelog/changelog-index.yaml — Liquibase 변경 이력 파일. |
spring.jooq.sql-dialect | POSTGRES — jOOQ가 쓸 SQL 방언. |
spring.servlet.multipart.max-file-size | 100MB — 단일 업로드 파일 최대 크기. |
spring.servlet.multipart.max-request-size | 100MB — 요청 전체 최대 크기. |
logging.level.com.example.study | DEBUG — 앱 패키지 로그 수준. |
app.jwt.secret | JWT? 서명용 비밀키(값은 생략). |
app.jwt.expirationMs | 43200000 — Access 토큰 만료(12시간). |
app.jwt.refreshExpirationMs | 604800000 — Refresh 토큰 만료(7일). |
app.upload.path | ${user.home}/temp — 업로드 저장 경로(다른 값을 끌어 씀). |
app.stage | dev — 단계 구분(prod | test | dev). |
spring.* 으로 시작하는 키는 Spring이 정해둔 표준 키지만, app.jwt.*,
app.upload.*, app.stage 처럼 app.* 으로 시작하는 키는
이 프로젝트가 직접 만든 사용자 정의 설정 입니다. 이런 값은 @Value 나
@ConfigurationProperties(prefix = "app.jwt") 로 코드에서 꺼내 씁니다.
한눈에 — 핵심 정리
| 개념 | 핵심 |
|---|---|
| 스타터 | 관련 라이브러리를 검증된 버전 묶음으로 한 번에 들여옴. |
| 자동 구성 | 클래스패스를 보고 필요한 빈을 알아서 등록. @EnableAutoConfiguration 으로 켜짐. |
| @SpringBootApplication | @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan 묶음. |
| 조건부 구성 | @ConditionalOnClass / @ConditionalOnMissingBean 등으로 "있을 때만/없을 때만" 등록. |
| 외부 설정 | application.properties/.yml. 커맨드라인 > 환경변수 > 프로파일 파일 > 기본 파일 순으로 덮어씀. |
| @Value | 값 하나를 콕 집어 주입. 기본값 ${키:기본} 가능. |
| @ConfigurationProperties | 같은 접두사 값들을 객체로 묶어 타입 안전하게 바인딩. @Validated 결합 가능. |
| 프로파일 | 환경 꼬리표. application-{프로파일}.properties + spring.profiles.active + @Profile. |
| 직접 구성 | @Configuration + @Bean 으로 손수 정의. 자동 구성보다 우선. |
| Actuator | 운영 점검 창구. /actuator/health + management.endpoints.web.exposure. |
이 프로젝트의 백엔드(com.example.study 패키지)는 application.properties 로 설정하며,
포트는 8084, DB는 studydb(스키마 lds)에 연결하고 Liquibase 로 테이블을 관리합니다.
버전은 Spring Boot 3.3.1 / Spring Framework 6.x / Java 17 / Jakarta EE 10 입니다.
웹·data-jpa·security·validation·actuator·data-jdbc·jooq 스타터를 사용합니다.
설정과 실행 환경을 더 보려면 → 백엔드 환경, 실행 방법.
다음 단계
- 빈을 등록·주입하는 모든 방법 → Spring IoC · DI · 빈
- 이 설정으로 연결된 DB와 트랜잭션 다루기 → Spring 데이터 · 트랜잭션
- 백엔드 실행 환경 → 백엔드 환경 · 실행 방법