👇🏻이전 글
2024.08.06 - [Spring] - Spring을 3 Layer Architecture로 역할 분리하기
IoC(제어의 역전)와 DI(의존성 주입)란?
좋은 코드, 즉 중복을 제거하고 표현이 명확한 코드, 처음 보는 사람도 쉽게 이해하고 수정할 수 있는 코드, 새로운 기능을 추가하더라도 구조에 큰 변화가 없는 코드를 만들기 위해서는 결합도와 의존성을 최소화 해야 한다.
메모장 프로젝트 속 제어의 흐름은 Controller → Service → Repository 순서로 흐르게 되는데 이 때문에 한 부분에서의 코드 추가나 수정이 연쇄적으로 다른 부분들과도 이어져 개발에 있어 피로함과 비효율성을 일으킨다. 의존성 주입을 사용해 역순으로 흐르게 하면 결합도와 의존성이 최소화되어 이러한 문제를 해결 할 수 있다. ( = 제어의 역전 )
제어의 역전을 위해서는 각 객체에 대한 생성은 1번씩만 해야하며, 생성된 객체를 모든 곳에서 재사용하고, 생성자 주입을 사용하여 필요로하는 객체에 해당 객체 주입하는 방법을 사용하면 된다.
의존성 주입에는 3가지 방법이 있다.
- 필드에 직접 주입
- 메소드를 통한 주입
- 생성자를 통한 주입
기존 코드에서는 생성자를 통해 Controller가 생성될 때 Service도 같이 생성해 JDBCtemplate까지 넘겨주는 과정을 거쳤다. 동시에 Service의 생성자를 통해 Service가 생성될 때 Repository에 JDBCtemplate를 넘겨준다. 때문에 흐름이 Controller → Service → Repository 인 걸 확인 할 수 있는데 이를 역순으로 바꿔주기 위해서는 생성자 주입을 사용해야 한다.
Repository 객체를 Service로 넘겨주고, Service 객체를 Controller에 넘겨주어야 제어 흐름이 역순이 될 수 있다. 이를 코드로 구현하면 다음과 같다.
MemoRepository.java
private final JdbcTemplate jdbcTemplate;
public MemoRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 변경 사항 없음
MemoService.java
// 변경 전
private final MemoRepository memoRepository;
public MemoService(JdbcTemplate jdbcTemplate) {
this.memoRepository = new memoRepository(jdbcTemplate);
}
👇🏻
// 변경 후
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
MemoController.java
// 변경 전
private final MemoService memoService;
public MemoController(JdbcTemplate jdbcTemplate) {
this.memoService = new MemoService(jdbcTemplate);
}
👇🏻
// 변경 후
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
이렇게 생성자 주입을 사용하여 코드 내에서 선언되어 만들어지는 객체가 아닌 이미 만들어져 있는 객체를 끌어와 사용하는 구조로 변경이 되었다. 여기서 드는 의문점은, 이미 생성되어 있는 객체를 끌어와 사용하려면 객체 생성이 우선되어야 하는데 이 부분을 어디서 처리할까? (memoService에 빨간 줄이 뜨는 이유기도 하다.)
IocContainer와 Bean
이 부분은 Spring이 알아서 해준다. ( 프레임워크의 편리함.. )
객체를 끌어와 넣어주는 부분을 Spring이 대신 해주는데, 이 때 주입될 수 있는 객체는 Spring이 관리하는 객체인 bean이어야 한다.
bean으로 등록하는 방법은 클래스명 위에 어노테이션으로 @Component를 적어주면 된다. bean으로 등록된 객체들은 Spring의 IoC Container 안에서 관리된다. bean으로 등록될 때는 클래스명과 동일한 이름이지만 첫 글자가 소문자인 상태로 등록된다. ( 예시 > MemoService의 bean은 memoService )
따라서 MemoService와 MemoRepository에 각각 @Component를 붙여주면 빨간 줄이 사라지는걸 확인할 수 있다.
MemoService.java
@Component
public class MemoService {
private final MemoRepository memoRepository;
@Autowired
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
}
또한 주입을 하고자 하는 생성자나 메소드, 필드 위에 어노테이션으로 @Autowired까지 붙여주어야 Spring이 인식을 할 수 있다. 지금 코드가 문제 없이 돌아가는 이유는 스프링의 버전이 4.3 이상이고, 생성자가 하나 밖에 없어서 생략이 가능하기 때문.
수동으로 bean 주입하기
public MemoService(ApplicationContext context) {
// 1. 'bean' 이름으로 가져오기
MemoRepository memoRepository = (MemoRepository) context.getBean("memoRepository");
// 2. 'bean' 클래스 형식으로 가져오기
MemoRepository memoRepository = context.getBean(MemoRepository.class);
this.memoRepository = memoRepository;
}
TIL 💭
그간 이걸 여기에 왜 사용하는지도 모르고 어노테이션을 썼는데 이렇게 알게 되어 매우 후련하다... 또한 이론으로만 알고 있던 스프링의 bean이나 DI 같은 디자인 패턴에 대해 코드로 직접 구현해볼 수 있어서 좋았다.
'Spring' 카테고리의 다른 글
Spring의 Filter 이해하기 및 구현하기 (0) | 2024.08.16 |
---|---|
Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지 (0) | 2024.08.16 |
Spring의 JPA와 Entity 이해하기 (0) | 2024.08.07 |
Spring을 3 Layer Architecture로 역할 분리하기 (0) | 2024.08.06 |
Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기 (0) | 2024.08.05 |