자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서, 엔터프라이즈급 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 경량화된 솔루션.
엔터프라이즈급 개발을 뜻대로만 풀이하면 기업을 대상으로 하는 개발이라는 말이다. 즉, 대규모 데이터 처리와 트랜잭션이 동시에 여러 사용자로 부터 행해지는 매우 큰 규모의 환경을 엔터프라이즈 환경이라 일컫는다.
Spring Framework는 경량 컨테이너로 자바 객체를 담고 직접 관리한다.
객체의 생성 및 소멸 그리고 라이프 사이클을 관리하며, 언제든 Spring 컨테이너로 부터 필요한 객체를 가져와 사용할 수 있다.
이는 Spring이 IOC 기반의 Framwork임을 의미한다.
IOC란?
Inversion of Control의 약자로, 말 그대로 제어의 역전이다.
일반적으로 지금까지의 프로그램은 아래의 작업을 반복했다.
이는 각 객체들이 프로그램의 흐름을 결정하고, 각 객체를 구성하는 작업에 직접적으로 참여한 것이다.
즉, 모든 작업을 사용자가 제어하는 구조이다.
즉, 제어의 흐름을 사용자가 컨트롤 하지 않고 위임한 특별한 객체에 모든 것을 맡긴다.
IOC란, 기존 사용자가 모든 작업을 제어하더 것을 특별한 객체에 모든 것을 위임하여 객체의 생성부터 생명 주기 등 모든 객체에 대한 제어건이 넘어간 것을 IOC, 제어의 역전이라고 한다.

프레임워크는 일종의 반제품이다. 프레임워크가 내가 만든 것들을 동작시켜준다. 그렇기 떄문에 프레임워크가 어떻게 동작하는지를 이해해야 한다.
프레임워크 코어(Cold Sopt) :
변경되지 않고 반복적으로 재사용 되는 부분. 프레임워크에서 제공하는 라이브러리들을 의미하고, 사용하는 자원 관리나 처리 흐름을 제어한다.
확장 포인트(Hook Point) :
애플리케이션을 구축할 때 사용할 확장 포인트를 제공한다. 추상 클래스나 인터페이스 형태로 제공되는 것이 일반적이다.
확장 모듈(Hot Spot) :
각 애플리케이션이 확장 포인트를 상속해서 애플리케이션만의 비즈니스를 구현하는 것을 말한다.
메타 데이터 :
프레임워크에서 제공하는 Cold Spot과 Hook Point를 상속해서 구현한 Hot Spot을 유기적으로 결합하여 동작하도록 하는 환경 설정 파일이다. 일반적으로 XML, java Config class 형태로 작성한다.
Spring Boot는 Spring 프레임워크를 기반으로 만들어진 도구이다.
Spring Boot는 복잡한 Spring 구성을 단순화하고, 더 빠르고, 광범위한 접근성을 제공하기 위해 개발되었다. 이는 개발자가 최소한의 설정으로 즉시 실행 가능한 어플리케이션을 개발할 수 있도록 지원하는 것을 목표로 하고 있다.
어떤 환경에서나 실행하기 위해 필요한 모든 요소를 포함하는 소프트웨어 패키지
DI Container와 Docker Container에서 두 Container는 같은 것일까?
결론부터 말하면, DI Container와 Docker Container는 서로 다른 개념이다.
DI Container는 소프트웨어 디자인 패턴에서 객체의 생성 및 의존성 관리를 자동화 하는 도구인 반면, Docker Container는 응용 프로그램을 그 종속성과 함께 패키지화하여 실행 환경의 차이로 인한 문제를 해결하는 기술이다.
정리하자면,
Inversion Of Control : 제어의 역전
IoC에 대해서는 위에서 언급하였으므로, 간단하게 정리하고 넘어가도록 한다.
Dependency Injection : 의존성 주입
* 스코프 : 변수, 함수, 객체 등이 유효한 범위 또는 영역을 의미한다. 즉, 특정 변수가 접근 가능하고 사용할 수 있는 콛의 부분을 정의한다.
Dependency Injection Container : 의존성 주입을 관리하는 도구
객체 생성 관리, 의존성 주입 관리, 수명 주기 관리, 설정 및 구성 관리 등의 역할을 한다.
의존 객체 (Dependency) :
객체 간의 관계 중 하나로, 객체 A가 객체 B를 사용하는 경우, 객체 A가 객체 B에 의존하고 있다고 표현한다.
의존 주입 (Dependency Injection) :
객체 간의 의존 관계를 설정하는 방법 중 하나로, 외부에서 의존 객체를 생성자, setter, 필드 등을 통해 주입하는 방법이다.
Bean :
Spring에서 DI를 사용하기 위해 생성되는 객체를 의미한다.
BeanFactory :
Spring에서 Bean을 생성하고 관리하는 컨테이너이다.
ApplicationContest :
BeanFactory를 상송한 Spring Container로, 더 다양한 기능을 제공한다.
Component :
Spring에서 Bean을 생성하기 위한 어노테이션 중 하나로, 해당 클래스를 Bean으로 등록한다.
Qualifier :
같은 타입의 Bean이 여러 개 있을 경우, 어떤 Bean을 사용할지 결정하는 용도로 사용된다.
AutoWiring :
자동으로 Bean을 주입하는 기능으로, @Autowired 애노테이션을 통해 사용된다.
* DI Container는 객체간 의존 관계를 설정하고, 관리하는 역할을 수행한다.
DI Container에서 자주 사용되는 용어들은 다음과 같다.
Bean :
DI Container가 관리하는 객체를 Bean이라고 부른다. Bean은 DI Container가 생성하고, 초기화하고, 보관하며, 필요한 곳에서 제공한다.
Container :
DI Container 자체를 Container라고 부르기도 한다. DI Container는 객체의 생성과 의존 관계를 설정하는 일을 담당한다. 여기서 객체는 Bean이다.
Configuration :
Di Container가 객체를 생성하고 의존 관계를 설정하기 위해 참조하는 설정 정보를 '구성(Configuration)'이라고 한다.
Injection :
DI Container는 생성된 빈에 필요한 의존 객체를 '주입(Injection)'한다.
AutoWiring :
DI Container가 Bean과 Bean 간의 의존 관계를 자동으로 설정해주는 기능을 Autowiring(자동 주입)이라고 한다.
Scope :
Bean의 생성 주기와 관련된 범위(Scope)를 설정할 수 있다. 대표적인 Bean Scope로는 Singleton, Prototype, Request, Session 등이 있다.
Proxy :
Di Container는 Bean을 가져올 때, 해당 Bean을 감싸는 Proxy 객체를 생성할 수도 있다. Proxy 객체는 빈의 메서드 호출을 가로채서 보안, 로깅, 트랜잭션 등의 작업을 수행할 수 있다.
DI와 DI Container에서 자주 사용되는 용어를 길지만 모두 정리해봤다. 이렇게 각자 길게 정리한 이유는 한 번 정리해두면 나중에 찾아보기 좋을 것이 때문이다. 나중에 용어에 대해 헷갈리 때가 오면 이 글을 참고하도록 하자.
Java에서 인스턴스 생성
개발자가 직접 인스턴스를 만든다.
Book book = new Book();
Spring Boot에서 Bean은 Spring IoC Container가 관리하는 객체를 의미한다. Spring Framwork에서는 객체의 생성, 생명주기 관리, 그리고 객체 간의 의존성을 Container가 처리하며, 이렇게 관리되는 객체를 'Bean'이라고 부른다.
Spring에서 Bean을 정의하는 방법은 크게 세 가지이다.
XML 기반 구성 :
XML 파일 내에 <bean> 태그를 사용하여 Bean을 선언하고 구성할 수 있다.
이 방법은 초기 Spring에서 널리 사용되었다.
자바 기반 구성 :
@Configuration 클래스 내부에서 @Bean 어노테이션을 사용하여 메서드로부터 Bean을 생성하고 구성할 수 있다.
@Configuration이란?
@Configuration은 하나 이상의@Bean어노테이션을 사용하여 Spring Container에 Bean 정의를 제공하는 클래스에 붙인다.
@Configuration을 사용하면 해당 클래스가 Spring IoC Container를 위한 설정 클래스로 인식되며, 이 클래스 안에서 정의된 메서드들이 Spring Container에 의해 관리되는 Bean을 반환한다.
이 방법은 코드 내에서 직접적이고 명확한 구성을 제공한다.
컴포넌트 스캐닝 :
@Component, @Service, @Repository, @Controller 등의 어노테이션을 '클래스'에 적용하여, Spring Boot의 자동 스캔 기능을 사용해 자동으로 Bean을 등록할 수 있다.
@SpringBootApplication 어노테이션은 @ComponentScan을 포함하고 있어서, 어플리케이션의 메인 클래스가 위치한 패키지 및 그 하위 패키지를 자동으로 스캔한다.
Spring에서 여러 인스턴스를 생성하려면, 각 인스턴스에 대해 별도의 Bean으로 정의해야 한다. 이번 예제는 각각 다른 설정을 가진 Book 인스턴스 세 개를 생성하므로, 각각의 Book 객체에 대한 Bean을 BookConfig 클래스에 정의한다.
@Configuration이 붙은 클래스를 Java Config라고 한다. 클래스이지만, 일종의 설정인 것이다.
메소드의 이름은 Bean의 이름이 된다!!
public class Book {
private String title;
private int price;
public Book(String title, int price) {
this.title = title;
this.price = price;
}
//Getter
@Configuration
public class BookConfig {
@Bean
public Book book1() {
return new Book("Java", 10000);
}
@Bean
public Book book2() {
return new Book("Spring Book", 12000);
}
@Bean
public Book book3() {
return new Book("Microservices", 15000);
}
}
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
Book book1 = context.getBean("book1", Book.class);
Book book2 = context.getBean("book2", Book.class);
Book book3 = context.getBean("book3", Book.class);
System.out.println("Book 1 Title: " + book1.getTitle() + ", Price: " + book1.getPrice());
System.out.println("Book 2 Title: " + book2.getTitle() + ", Price: " + book2.getPrice());
System.out.println("Book 3 Title: " + book3.getTitle() + ", Price: " + book3.getPrice());
}
}
ApplicationContext의 getBean() 메서드는 첫 번째 인자로 Bean의 이름을 사용하여 특정 Bean을 가져올 수 있다.
자바 기반의 설정에서는 @Bean 어노테이션이 붙은 메소드에 @Scope 어노테이션을 추가해서 스코프를 명시한다.
Bean은 기본적으로 singleton이지만, prototype으로 할 경우 매번 새로운 인스턴스가 생긴다.
@Bean
@Scope("prototype")
MyService myService() {
return new MyService();
}
다른 스코프의 빈 주입
생성자 기반 의존성 주입 방식
(constructor-based dependency injection)
설정자 기반 의존성 주입 방식
(setter-based dependency injection)
필드 기반 의존성 주입 방식
(field-based dependency)
생성자 기반 의존성 주입 방식은 각 클래스의 필수 의존성을 생성자를 통해 주입받는 방식이다.
아래의 예제는
PostService에PostRepository의 의존성을 주입하는 방법을 보여준다.
public class Post {
private Long id;
private String title;
private String content;
// Getter, Setter, AllArgsConstructor
}
public class PostRepository {
private List<Post> posts = new ArrayList<>();
public void save(Post post) {
posts.add(post);
}
public Optional<Post> findById(Long id) {
return posts.stream().filter(post -> post.getId().equals(id)).findFirst();
}
public List<Post> findAll() {
return posts;
}
}
아래의 PostService 클래스는 PostRepository에 대한 의존성을 생성자를 통해 주입받는다.
public class PostService {
private final PostRepository postRepository;
//생성자를 통해 Pository 의존성 주입
public PostService(PostRepository postRepository) {
this.postRepository = postRepository;
}
public void publishPost(Post post) {
postRepository.save(post);
}
public List<Post> getAllPosts() {
return postRepository.findAll();
}
}
Spring의 설정 클래스에서 PostRepository와 PostService를 Bean으로 정의하고, 의존성을 주입한다.
//@Configuration : 설정 클래스
@Configuration
public class PostConfig {
@Bean
public PostRepository postRepository() {
return new PostRepository();
}
@Bean
public PostService postService(PostRepository postRepository) {
return new PostService(postRepository);
}
}
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
PostService postService = context.getBean(PostService.class);
postService.publishPost(new Post(1L, "Hello World", "This is a spring boot application."));
postService.getAllPosts().forEach(post -> {
System.out.println("Post ID: " + post.getId() + ", Title: " + post.getTitle());
});
}
}
설정자 기반 의존성 주입 방식은 객체의 필수 의존성이 아닌, 선택적 의존성을 주입할 때 주로 사용된다.
아래의 예제는
EmailService클래스가NotificationService에 의존하는 방법을 보여준다.설정자 주입은 필수적이지 않은 의존성에 유용하며, 객체 생성 후 언제든지 의존성을 변경할 수 있는 유연성을 제공한다.
알림을 처리하는 NotificationService 클래스
public class NotificationService {
public void sendNotification(String message, String recipient) {
// 실제 알림 전송 로직은 여기에 구현
System.out.println("Sending notification to " + recipient + ": " + message);
}
}
EmailService 클래스는 NotificationService를 사용하여 이메일 알림을 보낸다.
public class EmailService {
private NotificationService notificationService;
// 설정자(Setter) 메서드를 통한 의존성 주입
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void sendEmail(String message, String recipient) {
// 이메일 전송 로직은 실제로는 복잡할 수 있지만 여기서는 단순화
notificationService.sendNotification(message, recipient);
}
}
Spring 설정 클래스에서 EmailService와 NotificationService를 Bean으로 정의하고, 의존성을 주입한다.
@Configuration
public class AppConfiguration {
@Bean
public NotificationService notificationService() {
return new NotificationService();
}
@Bean
public EmailService emailService() {
EmailService emailService = new EmailService();
emailService.setNotificationService(notificationService()); // 설정자 주입
return emailService;
}
}
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
EmailService emailService = context.getBean(EmailService.class);
emailService.sendEmail("Hello Spring!", "user@example.com");
}
}
필드 기반 의존성 주입 방식은 Spring Framework에서 어노테이션을 사용하여 클래스 필드에 직접 의존성을 주입하는 방법이다.
이 방식은 코드의 양을 줄이고 간결하게 의존성을 주입할 수 있으나, 단위 테스트를 어렵게 만들고, 클래스 외부에서 의존성을 설정할 수 없는 등의 당점이 있다.
여기서는
@Autowired어노테이션을 사용한다.
@Repository
public class UserRepository {
public String findUserNameById(Long userId) {
// 실제 애플리케이션에서는 데이터베이스 조회 로직이 포함될 것입니다.
// 여기서는 단순화를 위해 하드코딩된 값을 반환합니다.
return "User" + userId;
}
}
UserService 클래스는 UserRepository에 의존하여 사용자 데이터를 관리한다. 이 클래스에서는 필드 주입을 사용한다.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public String getUserName(Long userId) {
return userRepository.findUserNameById(userId);
}
}
MainApplication 클래스에서는 Spring Boot 애플리케이션을 실행하고 UserService를 통해 사용자 이름을 조회합니다.
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getUserName(1L)); // "User1"을 출력
}
}
필드 주입은 Spring Container가 객체를 생성한 후, 의존성을 클래스의 private 필드에 직접 주입한다.
필드 주입 방식은 매우 편리하긴 하나,
클래스가 스프링 컨테이너에 너무 강하게 의존하게 되며,
필드에 final 키워드를 사용할 수 없어 객체의 불변성을 보장할 수 없다는 단점이 있다.
자바 기반 설정 방식에서 @Bean 메소드를 사용하거나, XML 기반 설정 방식에서 <bean> 요소를 사용하는 것처럼 명시적으로 빈을 정의하지 않고도 DI 컨테이너에 빈을 자동으로 주입하는 방식이다.
오토와이어링 방식
@Autowired 어노테이션은 타입으로 오토와이어링을 하는 방식NoSuchBeanDefinitionException이 발생한다.NoUnuqueBeanDefinitionException이 발생한다.@Qualifier 어노테이션으로 Bean 이름을 지정한 후 선택해서 사용해야 한다.@Primary 어노테이션을 사용하면 @Qualifier를 사용하지 않았을 때, 우선적으로 선택할 Bean을 지정할 수 있다.@Qualifier 역할을 하는 사용자 정의 어노테이션을 사용해서 표현할 수도 있다.@Qualifier를 설정한다.빈의 이름이 필드명이나 프로퍼티명과 일치할 경우, 빈 이름으로 필드 인젝션을 할 수 있다.
JSR-250 사양을 지원하는 @Resource 어노테이션을 활용한다.
@Resource 어노테이션에는 name 속성을 생략할 수 있는데,
필드 인젝션을 하는 경우에는 필드 이름과 같은 이름의 빈이 선택되고,
Setter 인젝선을 하는 경우에는 프로퍼티 이름과 같은 이름의 빈이 선택된다.
생성자 인젝션에서는 @Resource 어노테이션을 사용할 수 있다.
이 예제에서는 @Resurce 어노테이션을 사용하여 Field Injection을 수행한다.
public class ExampleService {
public String getServiceName() {
return "Example Service";
}
}
설정 클래스에서는 ExampleService를 Bean으로 정의하며, @Resource를 통해 주입할 예정이다.
@Configuration
public class AppConfiguration {
@Bean(name = "examService")
public ExampleService exampleService() {
return new ExampleService();
}
}
메인 클래스에서 @Resource를 사용하여 ExampleService 인스턴스를 주입받는다.
이때 필드 이름 exampleService는 설정된 빈의 이름과 일치한다.
@SpringBootApplication
public class MainApplication {
@Resource(name = "examService")
private ExampleService exampleService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
MainApplication app = context.getBean(MainApplication.class);
System.out.println(app.exampleService.getServiceName()); // "Example Service" 출력
}
}
중간에 언급된 JSR-250은 뭘까?
JSR-250은 자바 커뮤니티 프로세스(JCP)를 통해 정의된 자바 표준화요청(Java Specification Request) 중 하나이다. 자바 플랫폼에서 공통적으로 사용되는 어노테이션들을 정의하기 위한 목적으로 개발되었다.
JSR-250은 여러 기본적인 어노테이션을 포함하며, 이러한 어노테이션들은 리소스 주입, 생명주기 콜백 등의 기능을 제공하여, 개발자가 보다 편리하게 해준다.
주요 어노테이션
@PostConstruct
- 객체의 생성과 의존성 주입이 완료된 후, 초기화 목적으로 실행할 메서드에 사용된다.
- 해당 메서드는 객체가 생성된 후 단 한 번만 호출되며, 초기화 작업에 주로 사용된다.
@PreDestroy
- 컨테이너에 의해 빈이 제거되기 전에 호출될 메서드에 사용된다.
- 리소스의 해제, 정리 작업을 수행하는 데 사용될 수 있다.
@Resource
- 리소스나 서비스에 대한 참조를 주입받기 위해 사용된다.
- 이름, 타입 등을 기반으로 의존성을 주입하는 데 사용된다.
- Ex. 데이터 소스, 세션, 기타 환경 자원 등을 주입받는 데 활용된다.
Java Config 파일에
@Bean으로 일일이 등록하는 것이 아니라, Bean이 될 수 있는 클래스들을 찾아서 자동으로 등록하게 하는 방법!
@Component, @Controller, @Service, @Repository, @RestController@Configuration@ControllerAdvice@ManagedBean(java.annotation.ManagedBean)@Named(jabax.inject.Named)@ComponentScan(basePackages = "examples.di")@ComponentScan(basePackages = "examples.di" includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
classes = {MyService.class })
})
@Service
public class MessageService {
public String getMessage () {
return "Hello from MesageService";
}
}
@Repository
public class DataRepository {
public String getDate() {
return "Data From DataRepository";
}
}
@SpringBootApplication
public class MainApplication {
@Autowired
private MessageService messageService;
@Autowired
private DataRepository dataRepository;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
MainApplication app = context.getBean(MainApplication.class);
System.out.println(app.messageService.getMessage());
System.out.println(app.dataRepository.getData());
}
}
빈의 초기화와 소멸 과정에서 호출되는 메소드들을 직접 정의하고, JSR-250의 @PostConstruct와 @PreDestroy 어노테이션을 활용하여, Spring의 initMethod와 destroyMethod를 사용한다.
public class MyBean implements InitializingBean, DisposableBean {
public MyBean() {
System.out.println("My Bean 생성자 호출");
}
@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct 메소드 호출");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean의 afterPropertiesSet 메소드 호출");
}
public void init() {
System.out.println("사용자 정의 init 메소드 호출");
}
@PreDestroy
public void preDestroy(){
System.out.println("@PreDestroy 메소드 호출");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean의 destroy 메소드 호출");
}
public void cleanup() {
System.out.println("사용자 정의 cleanup 메소드 호출");
}
}
Spring 설정 클래스(@Configuration)에서 MyBean을 Bean으로 등록하고, 초기화(initMethod) 및 소멸(destroyMethod) 메소드를 지정한다.
@Configuration
public class AppConfiguration {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
}
어플리케이션 컨텍스트를 직접 종료하여 빈의 소멸 과정을 테스트한다.
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
// 애플리케이션 컨텍스트를 가져와서 사용하고 직접 종료시킵니다.
context.close();
}
}
이 애플리케이션을 실행하면,
MyBean의 생성자,@PorstConstruct메소드,InitializingBean의afterPropertiesSet메소드, 사용자 정의init메소드가 순서대로 호출된다.애플리케이션 종료 시,
@PreDestroy메소드,DisposableBean의destroy메소드, 사용자 정의cleanup메소드가 호출된다.호출 결과
MyBean 생성자 호출 @PostConstruct 메소드 호출 InitializingBean의 afterPropertiesSet 메소드 호출 사용자 정의 init 메소드 호출 2024-05-08T15:01:52.496+09:00 INFO 50535 --- [demo] [ main] com.example.my.MainApplication : Started MainApplication in 0.376 seconds (process running for 0.584) @PreDestroy 메소드 호출 DisposableBean의 destroy 메소드 호출 사용자 정의 cleanup 메소드 호출
DI Container에서 관리하는 빈이 많아지면 많아질수록 설정 내용도 많아져서 관리하기가 어려워진다.
이럴 때는 빈 설정 범위를 명확히 하고 가독성도 높이기 위해 모적에 맞게 문활하는 것이 좋다.
자바 기반의 설정 분할
@Import어노테이션을 사용한다.XML 기반 설정의 분할
<import>요소를 사용한다.
Spring Boot에서 Java 기반 설정을 분할하고, @Improt 어노테이션을 사용하여 다른 설정 클래스를 임포트하는 방법을 설명하는 예제 코드이다.
이 예제에서는 두 개의 설정 클래스를 만들고, 하나의 클래스에서 다른 클래스를 import하여 관리를 분할하는 방법을 보여준다.

MainConfig는 ServiceConfig를 임포트(@Import)하고 있다. 이는 두 설정 클래스 간의 명시적인 연결을 나타낸다.
ServiceConfig는 MessageService 클래스의 인스턴스(@Bean)를 생성하며, 이는 의존성 주입을 나타낸다.
MainApplication은 MessageService를 사용하고 있다.
MessageService 클래스는 간단한 메시지를 반환하는 서비스 로직을 포함한다.
public class MessageService {
public String getMessage() {
return "Hello from MessageService";
}
}
첫 번째 설정 클래스는 Service 관련 Bean을 정의한다.
@Configuration
public class ServiceConfig {
@Bean
public MessageService messageService() {
return new MessageService();
}
}
두 번째 설정 클래스는 애플리케이션의 메인 설정을 담당하며, ServiceConfig를 임포트한다.
@Configuration
@Import(ServiceConfig.class)
public class MainConfig {
//application의 Main configuration
//추가적인 Bean 설정은 이곳에 작성될 수 있습니다.
MainConfig() {
System.out.println("MainConfig created");
}
}
Spring Boot 애플리케이션의 메인 클래스에서 설정된 Bean을 사용한다.
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
MessageService messageService = context.getBean(MessageService.class);
System.out.println(messageService.getMessage()); // "Hello from MessageService" 출력
}
}
@Import어노테이션은ServiceConfig클래스를MainConfig에 임포트하여,MainApplication이MessageService빈을 사용할 수 있도록 해준다.이 방식으로 애플리케이션의 설정을 여러 파일로 나누어 관리할 수 있으며, 각 설정 파일은 특정 기능이나 컴포넌트 그룹에 초점을 맞출 수 있다.
이는 대규모 프로젝트에서 설정의 복잡성을 줄이고, 관리를 용이하게 하는데 도움이 된다.
Spring Framwork에서는 설정 파일을 특정 환겅이나 목적에 맞게 선택적으로 사용할 수 있도록 그룹화할 수 있으며, 이 기능을 Profile이라고 한다.
@Profile 어노테이션을 사용한다.@Profile("dev")@Profile("dev", "real")Spring Boot에서 환경별(프로파일별)로 다른 설정을 제공하는 방법을 보여주는 예제이다.
이 예제에서는 dev와 prod 두 가지 프로파일을 만들고, 각 프로파일에 맞는 설정을 @Profile 어노테이션을 사용하여 적용한다.
간단한 데이터 서비스를 제공하는 클래스이다.
public class DataService {
private String environment;
public DataService(String environment) {
this.environment = environment;
}
public String getEnvironment() {
return environment;
}
}
개발 환경(dev)용 설정을 포함하는 클래스이다.
@Configuration
@Profile("dev")
public class DevelopmentConfig {
@Bean
public DataService dataService() {
return new DataService("Development environment");
}
}
생산 환경(prod)용 설정을 포함하는 클래스이다.
@Configuration
@Profile("prod")
public class ProductionConfig {
@Bean
public DataService dataService() {
return new DataService("Production environment");
}
}
@Configuration
@Profile("default")
public class DefaultConfig {
@Bean
public DataService dataService() {
return new DataService("Default environment");
}
}
Spring Boot의 메인 애플리케이션 클래스에는 DataService 빈을 요청하여 환경에 맞는 메시지를 출력한다.
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
DataService dataService = context.getBean(DataService.class);
System.out.println(dataService.getEnvironment()); // 환경에 따라 다른 결과 출력
}
}
이 코드를 실행할 때 환경에 맞는 프로파일을 활성화하기 위해 다음과 같은 방법을 사용할 수 있다.
java -Dspring.profiles.active=dev -jar target/your-application.jar
이 명령은 개발 환경 설정을 활성화한다.
java -Dspring.profiles.active=prod -jar target/your-application.jar
이 명령은 생산 환경 설정을 활성화한다.
이 예제는 프로파일에 따라 다르게 설정된 빈을 로드하고, 프로파일이 활성화되면 해당 환경에 맞는 데이터 서비스를 제공하는 방식을 보여준다.
이를 통해 개발과 생산 환경을 명확하게 구분할 수 있다.
Default encironment
IntelliJ에서 Profile을 prod로 지정하고 실행하면,

Production environment
가 출력된다.
이번 글을 작성한 목적은 SpringBoot에 대해 공부하는 시간을 갖기 위함이다. SpringBoot를 배우는 기간에 하필 몸이 좋지 않아 응급실 갖다오고 병원 다니다보니 수업을 빠지는 날이 많았다. 그래서 이번 글은 강의를 들으며 궁금했던 것이나 내용에 대해 생각해본 부분들을 빼고 작성하였다.
이후 강의를 들으며 Bean의 개념을 따로 찾아보아도 이해되지 않는 부분이 많았는데 이번에 쭉 정리해보니 조금은 이해하는 데 도움이 되었다. velog의 장점은 자동으로 목차가 정리되어, 나중에 원하는 내용을 찾기 쉽다는 것이다. 이 글에 Spring Boot에 대해 개념부터 예제까지 꼼꼼히 정리하였으니, 추후 기억이 안 나는 부분이 있다면 이 글을 쉽게 참고할 수 있을듯 하다.