B이전 포스팅에서 스프링 프레임 워크에 대해서 소개해보았습니다.
이번 시간에는 스프링 프레임 워크의 주요 특징들에 대해서 알아보도록 하겠습니다.
스프링 프레임 워크의 주요 특징은 IOC, 제어 역전 / DI, 의존성 주입 / AOP, 관점 지향 프로그래밍 입니다.
오늘은 이 세가지 중 IOC, 제어 역전에 대해 알아보도록 하겠습니다.
01. 스프링에서 IOC의 중요성
02. Spring IOC의 필요성
03. IOC, 제어 역전이란?
04. Spring Container란? (IOC의 행동대장?)
04-1. Spring Container
04-2. Bean Factory
04-3. ApplicationContext
05. Spring Bean이란?
05-1. 빈등록 방법 2가지
05-2. 스테레오타입 어노테이션
05-3. 빈의 생명 주기에 개입하기
01. 스프링에서 IOC의 중요성
스프링 프레임 워크를 사용하는 이유는 객체의 생성 및 관리와 프로그램의 흐름을 프레임 워크에 위임하는 것입니다.
위의 역할을 해주는 것이 바로 IOC, 제어 역전입니다.
IOC는 프로그램 제어의 흐름을 외부에서 관리하도록 하여 개발자가 프로그램의 흐름을 작성할 필요가 없도록 해줍니다.
쉽게 말해 프로그램의 수행역할을 프로그램 자신이 가지고 있다는 것입니다.
또한 객체 생성 및 소멸을 개발자가 아닌 프로그램이 제어권을 가지고 있습니다.
이것을 보고 제어의 권한이 개발자에게 없다, 제어가 역전되었다고 하여 제어 역전이라고 부르는 것입니다.
IOC덕분에 개발자는 객체 생성과 관리의 책임으로 부터 벗어날 수 있으며
프로그램의 흐름을 프로그램이 관리하기 때문에 오류로 부터 벗어날 수 있는 것입니다.
지금부터 이러한 강점을 가진 IOC에 대해서 알아보도록 하겠습니다.
02. Spring IOC의 필요성
일반적인 코드를 한 번 살펴보겠습니다.
아래와 같은 코드가 있다고 할 때, 아래는 Main함수의 실행을 보여주고 있습니다.
Main함수에서 init 메소드로 초기화를 한 뒤 process 메소드로 로직을 수행한 뒤 destroy 메소드로 자원을 제거해주는 모습을 볼 수 있을 것입니다.
public class Main {
public static void main(String[] args) throws Exception {
Steps steps = new Communication();
steps.initialize();
steps.process();
steps.destroy();
}
}
-> 위의 코드에 대해 장단점을 생각해 보면 좋을 것 같습니다.
- 장점 : 코드의 흐름이 명확하다.
- 단점 :
- initialize() -> process() -> destroy() 순서로 호출이 되어야 하는데, 순서가 바뀌거나 누락될 위험이 있다.
- 객체는 초기화를 해주어야하는데, 여러 코드에서 객체를 사용하는 경우, 초기화하는 위치가 나뉘어져 있으면 처리하기가 번거로워진다.
- 특정 객체의 의존성을 바꿀 때, 예를들어 Local DB를 Oracle DB로 바꿀 때, 코드 변경이 일어나는데 DB가 사용된 모든 코드에 수정이 필요하다.
- 프로그램 테스트가 쉽지 않다.
(위의 코드의 경우 테스트 하려면 관련있는 의존성이 있는 모든 코드를 구현해줘야한다.
public class Communication implements Steps {
private final Greeting greeting;
private final Farewell farewell;
private final Sender sender;
public Communication() {
this.greeting = new EnglishGreeting();
this.farewell = new EnglishFarewell();
this.sender = new ConsoleSender();
}
public void initialize() {
greeting.sayHello();
}
public void process() {
Request request = new Request("sando", "hello");
sender.sendMessage(request);
}
public void destroy() {
farewell.sayGoodBye();
}
}
※ 위의 코드는 실제 프로그램 흐름에 대한 내용을 프로그램이 가지고 있다고 했을 때 개발자해 구현만 하면 되는 함수들이다. init함수와 destroy함수, process함수의 순서 상관 없이 구현만 하면 프로그램이 알아서 호출하여 사용하는 것이다.
03. IOC, 제어 역전이란?
정의 : 제어 역전이란 프로그램의 제어 권한을 개발자가 아닌 Framework가 역으로 갖는다는 의미입니다.
쉽게 말해 프로그램의 제어 흐름을 직접 제어하는 것이 아닌 외부에서 관리하도록 만든 것을 말합니다.
프레임 워크에서의 개발자 역할 :
1. 제어 역전이 되면 개발자는 코드의 흐름이나 객체 생성에 관련된 코드를 짤 필요가 없어진다.
2. 개발자는 프레임 워크가 제공하는 방법에 따라 코드를 작성해주면 된다.
3. 프레임워크는 개발자가 정의한 것에 따라 객체를 생성하고 코드 동작 순서를 결정하여 실행한다.
4. framework는 개발자가 건드는 부분이 아니며 framework에는 객체 생성 및 프로그램 흐름이 정해져있다.
04. Spring Container란? (IOC의 행동대장?)
IOC, 제어역전라는 개념은 알게 되었는데 이를 동작하는 것은 무엇일까요?
제어의 역전을 동작하는 것은 바로 Spring Container입니다.
04-1. Spring Container
Spring Container란 객체의 생성, 관리, 소멸을 담당하여 개발자가 직접 객체를 생성하고 관리하는 책임을 없애주는 역할을 말합니다. 쉽게 말해 컨테이너라는 공간 안에 빈 객체들이 들어있다는 것입니다.
Spring Container를 구현하는 인터페이스가 있는데 Bean Factory입니다.
04-2. Bean Factory
Bean Factory는 가장 기본적인 IOC 컨테이너를 말합니다.
여기서 말한 IOC 컨테이너는 IOC개념을 구현한 스프링 컨테이너의 일종입니다.
쉽게 말해 IOC를 구현하는 개념이 Spring Container이고 IOC를 구현했기 때문에 이를 IOC 컨테이너라고도 하는 것입니다. 그런데 Bean Factory는 가장 기본적인 컨테이너로 확장자들이 나왔는데 Bean Factory의 기능을 확장한 것이 ApplicationContext입니다.
04-3. ApplicationContext
ApplicationContext는 Bean Factory의 기능을 확장한 IOC 컨테이너입니다.
더 강력한 기능을들을 담고 있는데 가장 큰 장점은 즉시 로딩입니다.
Bean Factory의 경우에는 빈을 생성하는 방식이 지연 로딩 방식입니다.
지연 로딩은 빈이 사용될 때, 필요할 때에 빈을 생성하는 방식이고, 즉시 로딩은 어플리케이션 시작 시 모든 빈을 미리 생성해 놓는 방식을 말합니다. 또한 AOP 지원, 환경 변수 관리 등 추가적인 기능들이 많이 추가되었습니다.
정리 :
정리를 해보자면 IOC를 동작하는 Spring Container, IOC를 동작하기 때문에 IOC 컨테이너라고도 불린다.
IOC 컨테이너의 대표적인 구현체로는 Bean Factory가 있고 Bean Factory의 확장 버전인 ApplicationContext가 있다.
05. Spring Bean이란?
IOC의 핵심 중 하나인 객체의 생성과 소멸. 여기서 말하는 객체가 바로 Bean입니다.
Bean이란 Spring 컨테이너에서 생성하고 관리하는 객체를 빈이라고 합니다.
05-1. 빈등록 방법 2가지
1. @Component
@Component
public class EnglishFarewell implements Farewell {
@Override
public void sayGoodBye() {
System.out.println("good bye");
}
}
2. @Configuration + @Bean
@Configuration
public class GreetingConfig {
@Bean
Greeting englishGreeting() {
return new EnglishGreeting();
}
@Bean
Greeting koreaGreeting() {
return new KoreaGreeting();
}
}
@Component는 빈으로 등록하고 싶은 클래스에 @Component을 붙이면 Spring Framework가 실행될 때 가장 먼저 실행되는 @SpringBootApplication의 내부에 있는 @CompoentScan을 통해 빈으로 등록을 해줍니다.
@Configuration는 해당 클래스 내부의 @Bean이 붙은 메소드를 통해 클래스를 빈으로 등록을 해줍니다.
그런데 @Configuration도 @Compnent를 내포하고 있기 때문에 @Configuration이 붙은 클래스 또한 빈으로 등록이 됩니다.
정리 :
@Component는 클래스 자체를 Bean으로 등록하는 반면, @Configuration은 클래스 내의 @Bean 이 붙은 메소드를 통해 Bean을 정의하고 생성한다.

05-2. 스테레오타입 어노테이션
스테레오타입은 개발자가 특정 클래스가 어떤 역할을 하는지를 표현하기 위해 사용하는데
기능적으로 @Component와 동일한 역할을 하며, 대신 클래스 역할에 맞게 표현한 것을 말합니다.
이는 코드의 가독성을 높여주고 유지 보수에 도움을 줄 수 있으며, Spring이 표현에 맞게 인식하고 관리할 수 있도록 하는 역할도 합니다.
05-3. 빈의 생명 주기에 개입하기
Spring Container에서 빈의 생성부터 소멸까지 모두 관리하여준다.
그런데 만약 빈의 종료 전에 특정 행동을 적용하고 싶으면 어떻게 해야할까?
예를 들어 종료 전 Connection을 반환하거나 소멸 전 알람을 보내고 싶을 때 등...
Spring Bean 생명주기는 다음과 같습니다.
[ 스프링 IoC 컨테이너 생성 ]→ [스프링 빈 생성 ]→ [의존관계 주입 ]→ [(초기화 콜백 메소드 호출)] → [사용 ]
→ [(소멸 전 콜백 메소드 호출)] → [스프링 종료]
Spring에는 Bean의 생명 주기에 개입하는 방법이 3가지가 있다.
1. Bean클래스에서 InitializingBean, DisposableBean 상속받기 : 아래 코드와 같이 두 인터페이스를 상속받으면 특정 함수를 구현하는데 destroy는 소멸 전 호출되고 afterPropertiesSet는 초기화 후 호출되는 메소드들이다.
public class EnglishGreeting implements Greeting, InitializingBean, DisposableBean {
public EnglishGreeting() {
System.out.println("english java constructor");
}
@Override
public void sayHello() {
System.out.println("hello world");
}
@Override
public void destroy() {
System.out.println("english DisposableBean destroy");
}
@Override
public void afterPropertiesSet() {
System.out.println("english InitializingBean afterPropertiesSet");
}
}
2. Bean등록 시 @Bean(initMethod = "customInit", destroyMethod = "customDestroy") 선언하기 : 아래 코드와 같이 Bean등록시에 init메소드와 destory메소드를 지정하면 Bean내부에 선언되어 있는 함수가 초기화할 때, 소멸 될 때 호출된다.
@Configuration
public class GreetingConfig {
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
Greeting englishGreeting() {
return new EnglishGreeting();
}
}
3. @PostConstruct @PreDestroy어노테이션 붙이기 : 등록할 Bean내부에 초기화 할 때 호출할 메소드와 소멸전 호출할 메소드를 구현한 뒤 위의 두 어노테이션을 붙이면 Bean 초기화, 소멸 시 자동으로 호출된다.
public class EnglishGreeting implements Greeting {
public EnglishGreeting() {
System.out.println("eng java constructor");
}
@Override
public void sayHello() {
System.out.println("hello world");
}
@PostConstruct
public void customPostConstruct() {
System.out.println("env PostConstruct");
}
@PreDestroy
public void customPreDestroy() {
System.out.println("env preDestroy");
}
}
지금까지 IOC에 대해서 학습해 보았습니다.
IOC가 왜 필요한지부터 어떻게 작동하고 어떤 기능들이 있고 어떤 점 때문에 스프링에서 가장 중요한 개념 중 하나가 되었는지를 알 수 있었습니다.
다음으로는 빈에 의존성 주입 원칙을 구현한 DI와 관점 지향 프로그래밍인 AOP를 순차적으로 학습하며
스프링의 3대 철학에 대해서 알아보도록 하겠습니다.
감사합니다.
'Spring' 카테고리의 다른 글
| [ Spring ] HttpMessageConverter 파헤치기 - 개발 지식 (0) | 2025.04.18 |
|---|---|
| [ Spring ] HandlerMethodArgumentResolver이란? - 개발 지식 (0) | 2025.04.15 |
| [HTTP API 설계] Spring HTTP API란? - 개발 지식 (0) | 2025.04.14 |
| [Spring Boot] 스프링 부트란? - 개발 지식 (2) | 2025.03.31 |
| [Spring] 시작, Spring이란? - 개발 지식 (1) | 2024.10.02 |