💡IoC (Inversion of Control)
- 객체지향 언어에서 Object간의 연결 관계를 런타임에 결정
- IoC 의 구현 방법 중 하나가 DI (Dependency Injection)
- 필요한 위치에서 개발자가 필요한 객체 생성 로직을 구현하는 기존의 방식에서 벗어나, 객체 생성을 Container에게 위임하려 처리함
아래와 같은 경우, A객체
에서 B,C객체
를 사용할 때 (의존할 때) A객체에서 직접 생성하는 것이 아니라 외부 (IoC 컨테이너)에서 생성된 B,C 객체를 주입시켜 setter 혹은 생성자를 통해 사용하는 방식
✔️Factory 호출 방식
- 객체 간의 결합도를 떨어뜨릴 수 있음 (객체간 결합도가 높으면 해당 클래스가 유지보수 될 때, 그 클래스와 결합된 다른 클래스도 유지보수 되어야 할 가능성이 높음)
- 인터페이스 변경 시 팩토리만 수정하면 되고 호출 클래스에는 영향을 미치지 않음
✔️Assembler를 통해 결합도를 낮추는 방식
- IoC 호출 방식
- 팩토리 패턴의 장점을 더하여 어떠한 것에도 의존하지 않는 형태가 됨
- 실행시점 (Runtime)에 클래스 간의 관계가 형성이 됨
💡Container
✔️Container
Container란?
- 객체의 생성, 사용, 소멸에 해당하는 라이프사이클 담당
- 라이프 사이클을 기본으로 애플리케이션 사용에 필요한 주요 기능 제공
- 비지니스 로직 외에 부가적인 기능들에 대해서는 독립적으로 관리되도록 하기 위함
- 서비스 look up이나, Configuration에 대한 일관성을 갖기 위함
- 서비스 객체를 사용하기 위해 각각 Factory 또는 Singleton 패턴을 직접 구현하지 않아도 됨
IoC Container
- 오브젝트의 생성과 관계 설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당
- 컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있어 IoC라고 부름 -> 스프링 컨테이너를 IoC 컨테이너라고 부르기도 함
(BeanFactory, ApplicationContext)
Spring DI Container
- Spring DI Container가 관리하는 객체를 bean이라 하고, 이들의 생명 주기를 관리하는 의미로
BeanFactory
라 함 -> Bean Factory에 여러 컨테이너 기능을 추가하여 ApplicationContext
라 함
💡DI
- Object에 lookup 코드를 사용하지 않고, 컨테이너가 직접 의존 구조를 Object에 설정할 수 있도록 지정해주는 방식
- 의존성 주입의 종류로는
Field Injection
, Setter Injection
, Constructor Injection
3가지 방법이 있음
1. Field Injection (필드 주입)
@Component
public class SampleController {
@Autowired
private SampleService sampleService;
}
- 변수 선언부에
@Autowired
Annotation을 붙임
- 단일 책임 원칙 (SRP)을 해칠 가능성이 높아짐 -> 간단한 사용법 아래 개수 제한 없이 무한정 추가하게 되고 class가 많은 책임을 떠안게 됨
Constructor Injection
과 다르게 Field Injection
은 final
을 선언할 수 없어서 객체가 변할 수 있음
2. Setter Injection (수정자 주입)
@Component
public class SampleController {
private SampleService sampleService;
@Autowired
public void setSampleService(SampleService sampleService) {
this.sampleService = sampleService;
}
}
Setter Injection
은 선택적인 의존성을 사용할 때 유용해서 상황에 따라 의존성 주입이 가능
- Setter Injection을 통해서 Service의 구현체를 주입해주지 않아도 Controller 객체는 생성이 가능
- set을 통해 Service의 구현체를 주입해주지 않았으므로 NullPointerException이 발생 -> 주입이 필요한 객체가 주입이 되지 않아도 얼마든지 객체를 생성할 수 있다는 것이 문제
3. Constructor Injection (생성자 주입)
@Component
public class SampleService {
private SampleDAO sampleDAO;
@Autowired
public SampleService(SampleDAO sampleDAO) {
this.sampleDAO = sampleDAO;
}
}
@Component
public class SampleController {
private final SampleService sampleService = new SampleService(new SampleDAO());
...
}
Constructor
에 @Autowired Annotation
을 붙여 의존성을 주입받을 수 있음
- 필수적으로 사용해야하는 의존성 없이는 Instance를 만들지 못하도록 강제할 수 있기 때문에 권장
- 의존관계를 주입하지 않은 경우에는 Controller 객체를 생성할 수 없음
- final 사용 가능 -> final로 선언된 레퍼런스 타입 변수는 반드시 선언과 함께 초기화 되어야 하므로 setter 주입시에는 의존 관계 주입을 받을 필드에 final 선언할 수 없음
- final을 붙이면 Lombok가 결합되어 코드를 간결하게 작성할 수 있음
✔️Bean
- 스프링이 IoC 방식으로 관리하는 오브젝트
- 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 Bean이라 부름
✔️BeanFactory
- 스프링이 IoC를 담당하는 핵심 컨테이너
- Bean을 등록, 생성, 조회, 반환하는 기능
- 일반적으로 BeanFactory를 바로 사용하지 않고 이를 확장한 ApplicationContext이용
✔️ApplicationContext
- BeanFactory를 확장한 IoC 컨테이너
- Bean을 등록하고 관리하는 기본적인 기능은 BeanFactory와 동일
- 스프링이 제공하는 각종 부가 서비스를 추가로 제공
✔️Annotation
- 빈으로 사용될 클래스에 특별한 annotation을 부여해 주면 자동으로 빈 등록 가능
- Annotation으로 빈을 설정할 경우, 반드시
component-scan
설정 필요
@Repository
Data Access Layer
의 DAO또는 Repository 클래스에 사용
DataAccessException
자동 변환과 같은 AOP의 적용 대상을 선정하기 위해 사용
@Service
@Controller
- Presentation Layer의 MVC Controller에 사용
- 스프링 웹 서블릿에 의해 웹 요청을 처리하는 컨트롤러 빈으로 선정
@Component
- 위의 Layer 구분을 적용하기 어려운 일반적인 경우
@ResponseBody
- 메소드에서 리턴되는 값이 View로 출력되지 않고 HTTP Response Body에 직접 쓰여지게 됨 -> return시에 json, xml과 같은 데이터를 return
- null 여부를 반드시 체크해주어야 함! (null을 리턴하면 오류도 발생하지 않고, 브라우저에 response도 없기 때문)
Reference