지난글: https://cooookieeee.tistory.com/38
의존성 주입이란?
공공 알리미 프로젝트를 하며, 어노테이션(@)을 공부하다가,의존성 주입의 용도로 쓰이는 @Autowired와 @RequiredArgsConstructor 를 클래스 구성 시 자주 사용함을 확인했다.궁금해서 확인 시 의존성 주
cooookieeee.tistory.com
지난글에 의존성 주입의 필요성을 다뤘으니,
의존성을 좀 더 쉽게 주입하는 방법을 다뤄보자.
스프링으로 프로젝트를 진행하다보면
@Autowired와 @RequiredArgsConstructor 이 어노테이션을 많이 봤을것이다.
이 어노테이션은 의존성 주입을 목적으로 더 쉽게 주입할 수 있게 도움을 준다.
먼저 Autowired 어노테이션의 설명을 보도록하자.
@Autowired
- Spring 컨테이너가 관리하는 Bean을 자동으로 주입하는 역할을 합니다.
- 생성자, 필드, 세터 메서드, 일반 메서드에 사용할 수 있습니다.
@Autowired는 Spring의 의존성 주입(Dependency Injection) 중 자동 주입(Auto-wiring)을 지원합니다.
@Autowired를 사용할 수 있는 위치
@Component
public class MyService {
@Autowired
private UserRepository userRepository; // UserRepository는 Bean으로 등록되어 있어야 함
public void doSomething() {
userRepository.save(new User());
}
}
``
음 확실히 생성자나 세터로 의존성을 주입하는 메서드가 없이 바로 필드로 의존성을 주입하는 것 같다.
그렇다면 어떤 원리로 주입하는걸까?
Spring 컨테이너가 관리하는 Bean을 자동으로 주입하는 역할을 한다고 했는데 이건 무슨 뜻일까?
다음 설명을 읽어보자.
Spring은 IoC(Inversion of Control, 제어의 역전) 컨테이너를 통해 객체의 생성과 관리를 책임집니다.
- Spring은 @Component로 등록된 클래스를 자동으로 스캔하여 Bean으로 관리합니다.
- @Autowired는 Spring에게 "해당 타입의 Bean을 찾아서 주입하라"고 요청합니다.
- UserService는 객체를 생성하지 않고, 필요한 객체를 Spring이 제공받습니다.
정리하자면 이렇다.
컨테이너에 Bean을 등록하고, 필요할 시 컨테이너에서 타입과 맞는 Bean을 꺼내서 주입한다
Bean으로 등록하려면 클래스에 @Component를 붙여야 등록이된다.
즉 @Component를 Bean에 객체를 넣고 싶은 클래스에 붙이고,
다른 클래스에서 의존할때 @Autowired를 통해 객체를 가져온다.
사실 이정도만 알아도 Spring에서 제공하는 의존성 주입 기능의 90퍼를 이해했다고 생각한다.
그렇다면 @RequiredArgsConstructor는 @Autowired와 뭐가 다른거지?
또 다시 차이점을 찾아보았다.
@RequiredArgsConstructor 방식과 @Autowired 방식의 차이점
@RequiredArgsConstructor @Autowired
필드 주입이 아닌 생성자 주입 | 기본적으로 필드 주입 |
생성자를 통해 주입되므로 불변성 보장 | 필드에 직접 주입되므로 코드가 덜 명확 |
테스트 코드 작성 용이 | 테스트 시 Mock 주입이 불편할 수 있음 |
Lombok으로 생성자 관리 자동화 | 코드가 더 장황해질 수 있음 |
무슨 말이지?
그래서 하나씩 알아보기로 하자.
1. 주입방식의 차이
@RequiredArgsConstructor는 클래스에 붙는다. 그리고 기본적으로 생성자 method를 생성할 필요가 없다.
알아서 주입해준다. 그래서 간단하게 의존하는 클래스 객체를 선언하기만하면 알아서 클래스 객체로 초기화가된다.
@Autowired는 기본적으로 필드와 메서드에 붙는다.
메서드에 붙으면? 의존하는 클래스 객체를 매개 변수로 줄 필요가 없다.
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
이런 클래스가 있을때, 메서드에 Autowired 어노테이션이 붙으면 매개 변수로 줘야하는
UserRepository 타입의 클래스 객체를 생략하고, Bean에있는 클래스 객체를 전달한다.
즉, UserService().method() 이런식으로 매개 변수를 생략하고 호출이 가능하다.
필드에 붙으면?
public class UserService {
@Autowired
private UserRepository userRepository;
public void createUser(String name) {
userRepository.save(name);
}
}
이거는 생성자도 생성하지않는다.
userRepository의 값에 바로 똑같은 타입의 클래스 객체를 제공한다.
2. 특징의 차이
1.코드의 명확성 차이
@RequiredArgsConstructor는 기본적으로 규칙이 있다.
선언하는 클래스 객체는 앞에 무조건 final이 붙어야한다. 이는 불변성을 보장한다.
@Autowired는 필드에 직접 주입된다. 또한 가변성이다.
기본적으로 생성자에 주입하지 않기 때문에, 의존하는 클래스 객체가 필수적으로 필요하더라도 쉽게 파악이 어렵다.
즉, 필수 의존성인지 파악이 어렵다. 생성자가 없기 때문에 의존성이 명시적으로 드러나지 않는다.
쉽게 말해, 생성자는 클래스 객체를 만들때 무조건적으로 실행되므로 필수로 실행되어야하는 코드를 넣는다.
그렇다면, 필드로 넣었을 때는, 이게 생성자로 넣어야하는지 setter메소드로 넣어도 되는 객체인지 파악이 안된다는거다.
2.테스트 용이성의 차이
이것은 어려울수도 있으니 간단하게 보고 넘기자.
필드에 직접 주입 시
@Service
public class MyService {
@Autowired
private UserRepository userRepository;
public void doSomething() {
userRepository.save(new User());
}
}
// 테스트 시
@Test
public void testDoSomething() {
MyService service = new MyService(); // 생성
UserRepository mockRepository = mock(UserRepository.class);
// 리플렉션 또는 도구를 사용해 주입
ReflectionTestUtils.setField(service, "userRepository", mockRepository);
// 테스트 진행
service.doSomething();
verify(mockRepository).save(any(User.class));
}
생성자로 넣을 시
@Service
@RequiredArgsConstructor
public class MyService {
private final UserRepository userRepository;
public void doSomething() {
userRepository.save(new User());
}
}
// 테스트 시
@Test
public void testDoSomething() {
UserRepository mockRepository = mock(UserRepository.class);
MyService service = new MyService(mockRepository); // 생성자 주입으로 Mock 객체 주입
// 테스트 진행
service.doSomething();
verify(mockRepository).save(any(User.class));
}
간단하게 설명하자면, 테스트를 할때는 mock 객체를 통해 클래스를 구동해 DB의 연결없이 호출여부와 숫자만 파악한다.
이때, Service의 생성자로 넣을 시 매개변수로 mock 객체를 받을 수 있다.
하지만 필드로 받는다면, 생성자를 명시하지 않았으니, mock 객체를 매개변수로 받을 수 없다.
이는 MyService라는 클래스 객체를 생성할때, 필수 의존성인 UserRepository를 명시하지 못하니 테스트가 힘들다
3. 결론
@Autowired든 @RequiredArgsConstructor든 의존성 주입을 목적으로 하고, 약간의 차이점이 있다.
@RequiredArgsConstructor는 생성자를 자동생성하여 의존성을 주입한다.
@Autowired는 생성자, setterMethod, 필드에 붙여서 사용한다.
즉 @RequiredArgsConstructor든 @Autowired든 생성자를 통해 의존성을 주입할 수 있다는 소리다.
메서드에 붙이냐 클래스에붙이냐의 간단한 차이만 있을 뿐이다.
그렇다면 우리는 상황에 맞게 두 어노테이션을 사용하면 될 것이다.
보통은 생성자 주입에 하는 것이 이점이 많아서 @RequiredArgsConstructor를 자주 볼 것이다.
마지막으로 컨테이너에 객체를 Bean으로 등록하는 방법을 다루고 글을 마치도록 하겠다.
컨테이너에 Bean으로 객체를 등록하는 방법
컨테이너에 Bean으로 객체를 등록하는 방법은 크게 두 가지다.
- @Bean을 통해 매서드단위로 return의 타입 클래스 객체를 Bean으로 등록
- @Component를 통해 클래스단위로 객체를 Bean으로 등록
이는 전의 @Autowired와 @RequiredArgsConstructor의 단위 (매서드,클래스) 의 차이점과 같다.
그렇다면 바로 차이점을 더욱 알아보도록 하자
@Component와 @Bean의 차이
- @Component:
- 구현체 클래스 자체에 붙임
- Spring은 클래스의 어노테이션(@Component)을 스캔하여 Bean으로 등록.
- 세부적으로 @Service, @Controller 같은 클래스 명시 어노테이션도 구현체 클래스 자체에 붙여 Bean 등록 가능
@Component
public class MyService {
@Autowired
private UserRepository userRepository; // UserRepository는 Bean으로 등록되어 있어야 함
public void doSomething() {
userRepository.save(new User());
}
}
``
- @Bean:
- 특정 메서드의 반환값을 Bean으로 등록합니다.
- 컨트롤이 더 세부적이다. (return 타입만 바꾸면 쉽게 의존성 주입에 필요한 클래스를 바꿀 수 있다.)
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new FileRepository(); // 변경 시 여기만 수정
}
}
우리 모두 상황에 따라 맞는 Spring 의존성 주입 기능을 이용해보자
'공부 > Java' 카테고리의 다른 글
#JPA 1# JPA와 트랜잭션 (0) | 2024.11.26 |
---|---|
#API# Spring의 API 생성 방식과 Model (0) | 2024.11.26 |
#의존성 1#의존성 주입이란? (0) | 2024.11.25 |
#개인 프로젝트 3# DB와 프로젝트 연결 및 JPA로 쿼리 수행하기 (0) | 2024.11.23 |
#개인 프로젝트 2# 프로젝트 생성,MYSQL과 연결하기, Controller-View 연결 (0) | 2024.11.22 |