
Spring ์ ํ๋ฆฌ์ผ์ด์
์์ ์ธ๋ถ ์ค์ (properties, yml) ๊ฐ์ ํ์์ ์. ์ด ๊ฐ๋ค์ ์ฝ๋์ ์ฃผ์
ํ๋ ๋ํ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก @Value์ @ConfigurationProperties๊ฐ ์์. ๋์ ์ฐจ์ด๋ฅผ ๋ช
ํํ ์ดํดํ๊ณ ์ํฉ์ ๋ง๊ฒ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํจ.
@Value๋ SpEL(Spring Expression Language)์ ์ด์ฉํด ํ๋กํผํฐ ๊ฐ์ ํ๋์ฉ ํ๋๋ ์์ฑ์ ํ๋ผ๋ฏธํฐ์ ์ฃผ์
ํ๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์์.
@Value ์ด๋
ธํ
์ด์
์ด ์ฌ๋ฌ ํด๋์ค์ ํฉ์ด์ ธ ์ถ์ ๊ณผ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง."${...}")๋ก ์์ฑํ์ฌ ์คํ๊ฐ ๋ฐ์ํด๋ ์ฐพ๊ธฐ ์ด๋ ค์.List๋ Map ๊ฐ์ ๋ณต์กํ ์๋ฃ๊ตฌ์กฐ๋ ๊ณ์ธต์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด ๋ถํธํจ.@ConfigurationProperties๋ ํน์ ์ ๋์ฌ(prefix)๋ฅผ ๊ฐ์ง ํ๋กํผํฐ๋ค์ ํ๋์ ๊ฐ์ฒด(POJO/Record)์ ํต์งธ๋ก ๋ฐ์ธ๋ฉํ๋, ํ๋ Spring Boot์์ ๊ฐ์ฅ ๊ถ์ฅ๋๋ ๋ฐฉ์์.
final ํ๋๋ฅผ ๊ฐ์ง ๋ถ๋ณ ๊ฐ์ฒด๋ก ์ค์ ์ ๊ด๋ฆฌํ ์ ์์. (Setter๊ฐ ํ์ ์์ด์ง)@Validated์ JSR-303(@NotEmpty, @Max ๋ฑ) ์ด๋
ธํ
์ด์
์ผ๋ก ๊ฐ ํ๋กํผํฐ ๊ฐ์ ์ ํจ์ฑ์ ์ ์ธ์ ์ผ๋ก ๊ฒ์ฆํ ์ ์์.spring-boot-configuration-processor ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด application.yml ์์ฑ ์ ํค ์๋์์ฑ ๋ฐ ๋ฌธ์๋ณด๊ธฐ๋ฅผ ์ง์ํจ.@ConfigurationProperties๊ฐ ๋ถ์ ํด๋์ค๋ฅผ Spring ์ปจํ
์ด๋๊ฐ ์ธ์ํ๊ฒ ํ๋ ๋ฐฉ๋ฒ์ ์ฃผ๋ก ๋ ๊ฐ์ง์.
@Component ์ฌ์ฉ: ์ค์ ํด๋์ค์ @Component๋ฅผ ๋ถ์ฌ ์ปดํฌ๋ํธ ์ค์บ ๋์์ผ๋ก ๋ง๋ฆ. ๊ฐ์ฅ ๊ฐ๋จํ์ง๋ง, ์ค์ ํด๋์ค๊ฐ ๋ค๋ฅธ ๋น์ฆ๋์ค ๋ก์ง ์ปดํฌ๋ํธ์ ์์ด๋ ๋จ์ ์ด ์์.@EnableConfigurationProperties ์ฌ์ฉ: @Configuration์ด ๋ถ์ ์ค์ ํด๋์ค๋ ๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์
ํด๋์ค์ @EnableConfigurationProperties(MyProperties.class)๋ฅผ ๋ช
์ํ์ฌ ๋ฑ๋กํจ. ์ค์ ์ ์ฑ
์๊ณผ ์ญํ ์ ๋ช
ํํ ๋ถ๋ฆฌํ ์ ์์ด ์ด ๋ฐฉ์์ด ๋ ๊ถ์ฅ๋จ.| ๊ตฌ๋ถ | @Value | @ConfigurationProperties |
|---|---|---|
| ๋ฐ์ธ๋ฉ ๋จ์ | ๊ฐ๋ณ ํ๋กํผํฐ (๋ฑ๊ฐ) | ํ๋กํผํฐ ๊ทธ๋ฃน (๊ฐ์ฒด) |
| ์ปดํ์ผ ์์ ํ์ ์์ ์ฑ | ๋ฎ์ (๋ฐํ์ ์ฃผ์ ) | ๋์ (๊ฐ์ฒด ํ๋ ํ์ ์ผ๋ก ๋ณํ) |
| ๊ตฌ์กฐ์ ๋ฐ์ธ๋ฉ | ๋ถ์ ํฉ | ์ ํฉ (๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ค์ฒฉ ํด๋์ค๋ก ๋งคํ) |
| ๋ถ๋ณ์ฑ(Immutability) | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ (์์ฑ์ ๋ฐ์ธ๋ฉ) |
| ์ ํจ์ฑ ๊ฒ์ฌ | ์ง์ ๊ตฌํ ํ์ | @Validated๋ก ์ ์ธ์ ์ง์ |
| IDE ์๋์์ฑ | ๋ฏธ์ง์ | ์ง์ (์์กด์ฑ ์ถ๊ฐ ์) |
| ๊ถ์ฅ ์ฌ์ฉ์ฒ | ๋จ์ผ ๊ฐ, ์์/ํ ์คํธ ์ค์ | ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ, ๋ณต์ก/๊ณ์ธต์ ์ค์ |
application.yml ์์ API ์๋ฒ์ ์ธ์ฆ ์ ๋ณด, CORS ํ์ฉ ๋ชฉ๋ก ๋ฑ ๋ณตํฉ์ ์ธ ์ค์ ์ ๊ด๋ฆฌํ๋ ์์ .
# application.yml
server:
port: 8080
# API ๊ด๋ จ ์ค์ ๊ทธ๋ฃน
api:
# ์ค์ฒฉ ๊ตฌ์กฐ (auth)
auth:
jwt-secret-key: "a-very-long-and-secure-secret-key-for-development"
token-expires-in: 3600 # 1์๊ฐ (์ด ๋จ์)
# List ํํ์ ๋ฐ์ดํฐ
cors:
allowed-origins:
- "http://localhost:3000"
- "https://my-frontend.com"
---
spring:
config:
activate:
on-profile: prod
api:
auth:
# ์ด์ ํ๊ฒฝ์์๋ ์ธ๋ถ ํ๊ฒฝ๋ณ์๋ Secret Manager๋ฅผ ํตํด ์ฃผ์
jwt-secret-key: ${API_JWT_SECRET_KEY}
token-expires-in: 86400 # 24์๊ฐ
cors:
allowed-origins:
- "https://my-real-service.com"
ํ๋กํผํฐ๊ฐ ๋์ด๋ ์๋ก ์์ฑ์๊ฐ ๊ธธ์ด์ง๊ณ ๊ด๋ฆฌ ํฌ์ธํธ๊ฐ ๋ถ์ฐ๋จ.
@Service
public class SimpleAuthService {
private final String jwtSecretKey;
private final List<String> allowedOrigins;
public SimpleAuthService(
@Value("${api.auth.jwt-secret-key}") String jwtSecretKey,
@Value("${api.cors.allowed-origins}") List<String> allowedOrigins) {
this.jwtSecretKey = jwtSecretKey;
this.allowedOrigins = allowedOrigins;
}
// ...
}
1. ๋ถ๋ณ(Immutable) ์ค์ ํ๋กํผํฐ ํด๋์ค ์์ฑ (with ์์ฑ์ ๋ฐ์ธ๋ฉ)
Java 17 ์ด์์์๋ record๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ ์ ์์.
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Positive;
import java.util.List;
@ConfigurationProperties(prefix = "api")
@Validated // ํ๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ์ฑํ
public class ApiProperties {
@NotEmpty // jakarta.validation.constraints.NotEmpty
private final Auth auth;
@NotEmpty
private final Cors cors;
// ์์ฑ์ ๋ฐ์ธ๋ฉ: Spring Boot๊ฐ ์ด ์์ฑ์๋ฅผ ํตํด yml ๊ฐ์ ์ฃผ์
ํจ (Setter ๋ถํ์)
public ApiProperties(Auth auth, Cors cors) {
this.auth = auth;
this.cors = cors;
}
// --- Getters ---
public Auth getAuth() { return auth; }
public Cors getCors() { return cors; }
// --- ์ค์ฒฉ๋ ์ค์ ์ ์ํ ์ ์ (static) ๋ด๋ถ ํด๋์ค ---
public static class Auth {
@NotEmpty
private final String jwtSecretKey;
@Positive
private final int tokenExpiresIn;
public Auth(String jwtSecretKey, int tokenExpiresIn) {
this.jwtSecretKey = jwtSecretKey;
this.tokenExpiresIn = tokenExpiresIn;
}
public String getJwtSecretKey() { return jwtSecretKey; }
public int getTokenExpiresIn() { return tokenExpiresIn; }
}
public static class Cors {
@NotEmpty
private final List<String> allowedOrigins;
public Cors(List<String> allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
public List<String> getAllowedOrigins() { return allowedOrigins; }
}
}
2. ๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ๋กํผํฐ ํด๋์ค ๋ฑ๋ก
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(ApiProperties.class) // ์์ฑํ ํ๋กํผํฐ ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋ก
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. ํ๋กํผํฐ ๊ฐ์ฒด๋ฅผ ์ฃผ์ ๋ฐ์ ์ฌ์ฉ
@Service
public class AdvancedAuthService {
private final ApiProperties apiProperties;
// ์์ฑ์๋ฅผ ํตํด ApiProperties ๊ฐ์ฒด๋ฅผ ํต์งธ๋ก ์ฃผ์
๋ฐ์
public AdvancedAuthService(ApiProperties apiProperties) {
this.apiProperties = apiProperties;
}
public void issueToken() {
String secretKey = apiProperties.getAuth().getJwtSecretKey();
int expirySeconds = apiProperties.getAuth().getTokenExpiresIn();
System.out.println("Using Secret Key (first 5 chars): " + secretKey.substring(0, 5) + "...");
System.out.println("Token expires in: " + expirySeconds + " seconds");
System.out.println("Allowed Origins: " + apiProperties.getCors().getAllowedOrigins());
}
}
@ConfigurationPropertiesScan: Spring Boot 2.2๋ถํฐ ์ถ๊ฐ๋ ์ด๋
ธํ
์ด์
. @EnableConfigurationProperties ๋์ ๋ฉ์ธ ํด๋์ค์ @ConfigurationPropertiesScan์ ๋ถ์ด๋ฉด ์ง์ ๋ ํจํค์ง ๋ด์ @ConfigurationProperties๋ฅผ ์๋์ผ๋ก ์ค์บํ์ฌ ๋ฑ๋กํด์ค. ํจ์ฌ ํธ๋ฆฌํจ.build.gradle์ annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด, IDE๊ฐ yml/properties ํ์ผ์์ api.auth.jwt-secret-key ๊ฐ์ ํค๋ฅผ ์๋์์ฑํ๊ณ , Javadoc ์ฃผ์์ ํดํ์ผ๋ก ๋ณด์ฌ์ค ๊ฐ๋ฐ ์์ฐ์ฑ์ ๊ทน๋ํํจ.application.yml ์ธ๋ถ ํ์ผ > application.yml ๋ด๋ถ ํ์ผ). ์ด ์์๋ฅผ ์ดํดํ๋ฉด ๋ฐฐํฌ ํ๊ฒฝ์์ ์ค์ ์ถฉ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ํฐ ๋์์ด ๋จ.