스프링에는 엔터프라이즈급 개발을 편하고 추후 유지관리가 쉽게하기 위해서 다양한 기능들을 제공해준다
그에 근간이 되는 기능은 강력한 디자인 패턴인 IoC, DI, AOP 등에 있다고 생각한다
IoC (제어의 역전)
사실 IoC는 비단 스프링에서만 사용하는게 아니라 큰 포괄적인 의미는 다음과같다
개발자가 구현한 프로그램이 외부에 의해서 제어되는 디자인 패턴을 의미한다

보통 프로그래밍을 하게되면 외부 라이브러리 코드를 개발자가 가지고 직접 이용하여 구현을 하게된다
개발자가 직접 외부코드를 제어하여 프로그램을 설계하고 구현을 하게되는데 IoC 디자인패턴은 반대로 간다
외부코드가 프로그래머의 코드를 호출하여 동작한다
설계 목적상 제어반전을 통해서 얻을수있는 이점은 다음과같다
- 시스템이 어떻게 동작할지 고민할필요없이 맞춰진 형식대로 프로그래밍을 할수있다
- 모듈을 바꿔도 다른 시스템에 부작용을 일으키지 않는다 (분리도가 높다)
- 프로그램 설계시 외부 프로그램에 결합에 고민할 필요없이 로직에 집중 할 수 있다
보통 IoC 디자인패턴은 클래스를 직접 호출하지 않고 외부 라이브러리(프레임워크)에게 맡겨 진행하게된다
Spring IoC
그렇다면 스프링에서는 IoC를 어떤식으로 적용하고 있을까?
그전에 IoC가 나오게 된 이유를 Spring에 비유해서 알아보도록 하자
OrderSerivce orderService = new orderServiceImpl();
해당 코드는 주문과 관련된 서비스 로직들을 담고있는 클래스이다
프로그래머가 직접 인스턴스를 생성하여 객체를 생성하고 있는 형태이며, 추후 여러 문제를 발생시킬수있다
- 강한 결합
이렇게 클래스 인스턴스를 직접 생성하게되면 다른 구현체로 변경하고싶을때 여러코드를 수정해야 할수있습니다
지금은 orderServiceImpl 클래스를 구현체로 사용하고있지만 추후 UserOrderService와 같은 다른 구현체로 변경해야 된다면
해당 Service가 10군데, 100군데 모두 쓰이고있다면 전부 변경을 해줘야 합니다
실수할 가능성도 높고, 확장성이 떨어지게 됩니다
- 테스트에 불편함
테스트 코드에서도 직접 OrderServiceImpl을 생성하게 되면 실제 구현체에 의존하게 되어
테스트가 독립적이지 않고, 다른 컴포넌트에 의해서 영향을 받을수 있게됩니다
public class OrderServiceTest {
@Test
public void testPlaceOrder() {
// 실제 구현체 사용
OrderService orderService = new OrderService();
// 테스트 시 외부 리소스에 의존하여 테스트가 제대로 되지 않거나, 예외 발생 가능
String result = orderService .placeOrder();
}
}
만약 orderService에 다른 외부 API나 DB 연결에 의존할 경우에 테스트는 더 어려워지게 됩니다
- DIP 위반
고수준에 클래스(OrderService)가 하위 클래스(OrderServiceImpl)에 의존하게 되므로 SOLID중 DIP 원칙에 위배됩니다
이는 나중에 확장성을 고려해봤을때 안좋은 영향을 끼칠수있습니다
스프링은 프로그래머가 직접 클래스를 생성하지 않고 IoC 컨테이너 (스프링 컨테이너)가 대신해서 할당해주게 됩니다
이게 위에서 언급한 IoC(제어의 역전) 형태입니다
Spring IoC 구조
IoC 컨테이너는 다른말로 스프링 컨테이너, DI 컨테이너로 불립니다
스프링 컨테이너 여러 객체들이 존재하고 이를 이를 Bean 이라고 부릅니다
스프링 컨테이너 안에 들어있는 Bean들은 싱글톤(Singleton) 형태로 관리되며
프로그래머가 필요로 하는 객체가 있을경우 컨테이너를 확인하여 주입을 대신해줍니다

그렇다면 스프링 컨테이너에 객체는 어떻게 등록하는 걸까?
먼저 Apllication을 실행하면 먼저 @Controller, @Service, @Component 등 애노테이션이 붙은
클래스들을 스캔하고 해당 클래스들을 스프링 컨테이너에 Bean으로 등록하게 된다

스프링 컨테이너에 등록된 Bean들은 value : key 와같은 형태로 저장이 된다
만약 UserService라는 클래스를 Bean으로 등록하게 되면 다음과같은 형태로 저장된다
userService : UserService.class
위와같은 정보를 토대로 아래에서 언급할 DI (의존성 주입)할때 이름을 가지고 객체를 할당해주게된다
또한 스프링 컨테이너에 직접 접근하여 Bean을 조회하거나, 환경설정 관리, 애플리케이션 상태를 파악할수있는데
ApplicationContext 클래스를 통해서 제어할수있다 해당 내용은 Spring 공식문서를 참고하자
DI (의존성 주입)
IoC 컨테이너 생성 → Bean 객체 등록 → 의존성 주입
스프링 컨테이너에 등록되어있는 Bean 객체들을 직접 사용하기 위해서는 DI (의존성 주입)을 통해서 진행하게된다

DI 를 통해서 프로그래머는 직접 인스턴스를 만들지않고 스프링 컨테이너에 등록되어있는 Bean들을 사용해서 할당하게된다
Spring에서는 의존성을 주입하는 3가지 방법을 제공해준다
@Autowired를 통한 의존성 주입
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void getUser() {
userRepository.selectUser();
}
}
주입할려는 클래스를 선언해주고 @Autowired 애노테이션을 통해서 스프링 컨테이너에 있는 Bean을 주입할수있다
// 생성자 주입
@Service
public class UserService {
private UserRepository userRepository;
private MemberService memberService;
public UserService(UserRepository userRepository, MemberService memberService) {
this.userRepository = userRepository;
this.memberService = memberService;
}
}
생성자를 통해서 스프링 컨테이너에 있는 Bean을 주입할수있다
// 수정자 주입
@Service
public class UserService {
private UserRepository userRepository;
private MemberService memberService;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
}
Setter를 통해서스프링 컨테이너에 있는 Bean을 주입할수있다
Spring은 3가지 방법중 생성자 주입을 추천하며 Setter주입은 거의 쓰이지 않고, @Autowired와 생성자주입이 주로쓰인다
Bean 생명주기
프로그래머라면 사용하는 기능에 대해서 어떤식으로 동작하는 대충 알고있어야 한다
Spring에서 Bean 생명주기는 다음과같다
IoC 컨테이너 생성 → 객체 생성 → 의존성 주입 → 콜백전 함수(초기화) → 객체 사용 → 소멸
IoC 컨테이너 생성
Bean을 담을 스프링 컨테이너를 등록하는 단계
객체 생성
@Controller, @Service, @Component 등이 붙은 애노테이션을 스캔후 Bean으로 등록
의존성 주입
등록되어있는 Bean을 의존성 주입
콜백전 함수(초기화)
Bean이 생성되고 의존성 주입까지 완료되어 완전히 준비된 상태에서 추가적인 초기화 작업 실행
@PostConstruct 를 통해서 초기화 작업 수행가능
객체 사용
Bean이 최종 초기화 된후 Application에서 Bean을 사용하여 로직을 수행
소멸
Application이 종료되거나 컨테이너가 Bean을 더이상 필요로 하지않을때 소멸
소멸할때 외부 자원등을 해제할수 있으며, @PreDestroy를 통해서 소멸작업 수행가능
'개발 > Spring' 카테고리의 다른 글
Springboot에서 Mybatis 사용하기 (2) | 2024.11.08 |
---|---|
Springboot에서 MySQL 쿼리문 작성하기(JDBC) (0) | 2024.11.07 |
SpringBoot를 이용하여 MySQL과 연동하기(JDBC) (2) | 2024.11.06 |
Spring boot Validation과 @Valid 애노테이션 활용하기 (0) | 2024.10.28 |
SpringbootApplication에 run() 메서드 실행시 일어나는 일 (0) | 2024.10.26 |