이번 프로젝트에서는 화면을 구성하는 것뿐만 아니라 Spring Boot 애플리케이션의 환경을 효율적으로 관리하는 방법을 학습하였다. 하나의 설정 파일에 모든 정보를 작성하는 것이 아니라 설정을 역할별로 분리하고, @ConfigurationProperties를 이용하여 여러 설정 값을 객체로 관리하는 방법을 익혔다.
또한 개발 환경과 운영 환경을 구분하기 위한 Profile, 동일한 타입의 Bean이 여러 개 존재할 때 사용할 Bean을 선택하는 @Primary와 @Qualifier, 그리고 사용자의 언어에 따라 다른 화면을 제공하는 국제화(i18n) 기능까지 실습하였다.
이번 프로젝트를 통해 단순히 기능을 구현하는 것을 넘어 프로젝트를 체계적으로 관리하고 확장 가능한 구조를 만드는 방법을 이해할 수 있었다.
Spring Boot에서는 데이터베이스 정보, 서버 포트, API Key와 같은 다양한 설정을 application.yml(또는 application.properties)에 작성한다.
하지만 프로젝트가 커질수록 하나의 파일에 모든 설정을 작성하면 관리가 어려워진다.
이번 프로젝트에서는 설정 파일을 목적에 따라 분리하여 관리하였다.
예를 들어
application.yml
↓
application-db.yml
↓
application-app.yml
처럼 역할에 따라 설정을 나누어 관리하였다.
이러한 방식은 프로젝트 규모가 커질수록 설정 파일을 찾기 쉽고, 환경별 설정을 관리하기에도 편리하다는 점을 학습하였다.
설정 파일의 값을 Java 코드에서 사용할 때는 @ConfigurationProperties를 활용하였다.
@ConfigurationProperties(prefix = "app")
public record AppProperties(
String message
) {
}
prefix = "app"은 설정 파일에서 app으로 시작하는 항목을 하나의 객체로 묶어 준다.
예를 들어 설정 파일에 다음과 같은 내용이 있다.
app:
message: Hello Spring Boot
Spring Boot는 애플리케이션이 실행될 때
application.yml
↓
app.message
↓
AppProperties.message
순서로 값을 자동으로 바인딩한다.
따라서 Controller에서는 문자열을 직접 읽어오는 것이 아니라 객체를 통해 사용할 수 있다.
appProperties.message();
이처럼 설정 값을 하나의 객체로 관리하면 여러 설정을 체계적으로 관리할 수 있으며, 타입이 지정되기 때문에 오타나 형변환 오류도 줄일 수 있다는 점을 배웠다.
이번 프로젝트에서는 @Value도 함께 사용하였다.
@Value("${app.message}")
private String msg;
@Value는 특정 설정 값을 하나만 가져올 때 사용하는 어노테이션이다.
예를 들어
app:
message: Hello
가 있다면
@Value("${app.message}")
를 통해 "Hello"가 주입된다.
실행 과정은 다음과 같다.
Spring 실행
↓
application.yml 읽기
↓
app.message 검색
↓
msg 변수에 저장
두 방법 모두 설정 값을 읽어오는 기능을 수행하지만 사용하는 목적이 조금 다르다.
| 구분 | 특징 |
|---|---|
@Value | 하나 또는 두 개 정도의 간단한 설정 값을 사용할 때 적합 |
@ConfigurationProperties | 여러 개의 관련 설정을 하나의 객체로 관리할 때 적합 |
예를 들어
app:
name: Pizza
version: 1.0
author: Hong
company: SSAFY
처럼 여러 개의 설정이 존재한다면
AppProperties
하나의 객체로 관리하는 것이 훨씬 효율적이라는 점을 이해하였다.
Spring에서는 필요한 객체를 직접 생성하지 않고 Spring Container가 생성하고 관리한다.
이번 프로젝트에서는 @Configuration과 @Bean을 이용하여 객체를 등록하였다.
@Configuration
public class AppConfig {
@Bean
public HelloService helloService() {
return new HelloService();
}
}
(프로젝트 구조 기반)
애플리케이션이 실행되면
Spring Boot 실행
↓
@Configuration 탐색
↓
@Bean 메서드 실행
↓
객체 생성
↓
Spring Container 저장
순서로 Bean이 등록된다.
이후 Controller나 Service에서는
@RequiredArgsConstructor
또는
@Autowired
를 이용하여 필요한 Bean을 주입받는다.
이를 통해 객체 생성과 관리를 Spring이 대신 수행하는 IoC(Inversion of Control) 구조를 이해할 수 있었다.
동일한 타입의 Bean이 여러 개 존재하면 Spring은 어떤 Bean을 주입해야 할지 결정하지 못한다.
이를 해결하기 위해 @Primary와 @Qualifier를 사용하였다.
예를 들어
PizzaService
CheesePizzaService
BulgogiPizzaService
세 개가 모두 같은 인터페이스를 구현했다고 가정하자.
이 경우
private final PizzaService pizzaService;
만 작성하면 Spring은 어떤 객체를 주입해야 하는지 알 수 없다.
CheesePizzaService
↓
@Primary
를 지정하면
기본적으로 CheesePizzaService가 주입된다.
특정 Bean을 사용하고 싶다면
@Qualifier("bulgogiPizzaService")
를 사용하여 원하는 Bean을 직접 선택할 수 있다.
애플리케이션은 실행 환경에 따라 서로 다른 설정이 필요하다.
예를 들어
| 환경 | 특징 |
|---|---|
| Local | 개발자가 사용하는 환경 |
| Dev | 개발 서버 |
| Test | 테스트 서버 |
| Prod | 운영 서버 |
이번 프로젝트에서는 Profile을 이용하여 이러한 환경을 구분하는 방법을 학습하였다.
Profile을 사용하면
개발 환경
↓
Dev Bean
운영 환경에서는
운영 환경
↓
Prod Bean
이 자동으로 선택된다.
덕분에 개발 환경에서는 테스트용 객체를 사용하고, 운영 환경에서는 실제 서비스를 사용하는 등 환경에 맞는 Bean을 쉽게 적용할 수 있다는 점을 이해하였다.
이번 프로젝트에서 가장 흥미롭게 학습한 기능은 국제화(i18n) 였다.
프로젝트에는
messages.properties
messages_ko.properties
messages_en.properties
파일이 존재하였다.
Thymeleaf에서는
<h1 th:text="#{page.headline}"></h1>
처럼 작성하였다.
사용자의 브라우저 언어가
한국어라면
message_ko.properties
↓
피자 주문 시스템
영어라면
message_en.properties
↓
Pizza Order System
이 출력된다.
즉,
같은 HTML을 사용하더라도
브라우저의 Locale에 따라 자동으로 다른 언어가 출력되는 구조라는 점을 이해하였다.
국제화 기능은 내부적으로 다음과 같은 과정으로 동작한다.
브라우저 요청
↓
Accept-Language
↓
Spring LocaleResolver
↓
MessageSource
↓
message_ko.properties
또는
message_en.properties
↓
Thymeleaf
↓
HTML 출력
예를 들어
한국 사용자가 접속하면
안녕하세요
영어 사용자라면
Hello
가 같은 HTML에서 출력된다.
이 기능을 활용하면 하나의 프로젝트로 여러 국가의 사용자를 지원할 수 있다는 점을 학습하였다.
| 학습 내용 | 세부 학습 내용 |
|---|---|
application.yml | 애플리케이션 설정을 역할별로 분리하여 관리하는 방법 |
@ConfigurationProperties | 여러 설정 값을 하나의 객체로 바인딩하는 방법 |
@Value | 단일 설정 값을 주입받는 방법 |
| Spring Container | Bean을 생성하고 관리하는 과정 |
@Bean | 필요한 객체를 Spring Container에 등록하는 방법 |
@Primary | 동일한 타입의 Bean 중 기본으로 사용할 Bean을 지정하는 방법 |
@Qualifier | 원하는 Bean을 직접 선택하여 주입하는 방법 |
| Profile | 개발·운영 환경에 따라 다른 Bean과 설정을 사용하는 방법 |
| 국제화(i18n) | Locale에 따라 서로 다른 메시지를 출력하는 방법 |
| MessageSource | Locale에 맞는 properties 파일을 선택하여 메시지를 제공하는 과정 |
이번 프로젝트를 통해 애플리케이션을 개발할 때 기능 구현뿐 아니라 환경 설정과 유지보수성을 고려한 설계가 중요하다는 점을 배울 수 있었다. @ConfigurationProperties를 이용하여 설정을 객체로 관리하고, @Value를 통해 필요한 설정만 간단하게 주입하는 방법을 익히면서 프로젝트 규모에 따라 적절한 설정 관리 방법을 선택할 수 있다는 점을 이해하였다. 또한 @Primary, @Qualifier, Profile을 활용하여 실행 환경과 Bean 선택을 유연하게 제어하는 방법을 학습하였고, 국제화(i18n)를 통해 하나의 애플리케이션으로 여러 언어를 지원하는 구조를 경험하면서 실제 서비스에서 사용자의 환경에 맞춰 애플리케이션을 제공하는 방법을 이해할 수 있었다.