Spring Boot에서 @RequiredArgsConstructor로 깔끔한 의존성 주입하기
Spring Boot 개발을 하다 보면 의존성 주입(Dependency Injection)을 위해 생성자를 작성하는 일이 매우 빈번합니다.

특히 여러 개의 의존성을 주입받는 클래스에서는 생성자 코드가 길어지고 반복적인 작업이 될 수 있습니다. 이런 문제를 해결해주는 것이 바로 Lombok의 @RequiredArgsConstructor 어노테이션입니다.
@RequiredArgsConstructor란?
@RequiredArgsConstructor는 Lombok에서 제공하는 어노테이션으로, final 필드나 @NonNull 어노테이션이 붙은 필드에 대해서만 생성자를 자동으로 생성해주는 기능입니다.
Spring Boot에서는 주로 final 필드와 함께 사용하여 불변(immutable) 의존성 주입을 구현하는 데 활용됩니다.
기본 사용법
Before: 전통적인 생성자 방식
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final ValidationService validationService;
// 수동으로 생성자를 작성해야 함
public UserService(UserRepository userRepository,
EmailService emailService,
ValidationService validationService) {
this.userRepository = userRepository;
this.emailService = emailService;
this.validationService = validationService;
}
// 비즈니스 로직...
}
After: @RequiredArgsConstructor 사용
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final ValidationService validationService;
// 생성자가 자동으로 생성됨!
// 비즈니스 로직...
}
핵심 특징
1. final 필드만 대상
@RequiredArgsConstructor는 오직 final 필드만을 생성자 매개변수로 포함합니다.
@RequiredArgsConstructor
public class ExampleService {
private final UserRepository userRepository; // 생성자에 포함됨
private final EmailService emailService; // 생성자에 포함됨
private String tempData; // 생성자에 포함되지 않음
private int counter = 0; // 생성자에 포함되지 않음
}
2. @NonNull 필드도 포함
@NonNull 어노테이션이 붙은 필드도 생성자에 포함됩니다.
@RequiredArgsConstructor
public class MixedService {
private final UserRepository userRepository; // 포함됨
@NonNull
private String configValue; // 포함됨
private String optionalValue; // 포함되지 않음
}
실무 활용 예제
Controller에서의 활용
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final UserMapper userMapper;
// 컨트롤러 로직...
}
Repository 계층에서의 활용
@Repository
@RequiredArgsConstructor
public class CustomUserRepositoryImpl {
private final EntityManager entityManager;
private final JPAQueryFactory queryFactory;
// 리포지토리 로직...
}
테스트에서의 활용
@RequiredArgsConstructor를 사용한 클래스는 Mockito의 @InjectMocks와 함께 사용할 수 있습니다.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService; // @RequiredArgsConstructor가 생성한 생성자를 통해 Mock 주입
@Test
void testUserService() {
// given
User mockUser = new User("test@example.com", "테스트");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
// when
Optional<User> result = userService.findById(1L);
// then
assertThat(result).isPresent();
assertThat(result.get().getEmail()).isEqualTo("test@example.com");
}
}
또는 수동으로 생성자를 호출하여 테스트할 수도 있습니다.
class UserServiceTest {
private UserRepository mockUserRepository;
private EmailService mockEmailService;
private UserService userService;
@BeforeEach
void setUp() {
mockUserRepository = mock(UserRepository.class);
mockEmailService = mock(EmailService.class);
// @RequiredArgsConstructor가 생성한 생성자 직접 호출
userService = new UserService(mockUserRepository, mockEmailService);
}
@Test
void testUserCreation() {
// 테스트 로직...
}
}
Configuration 클래스에서의 활용
Configuration 클래스에서 Properties를 주입받을 때 매우 유용합니다.
@Configuration
@RequiredArgsConstructor
public class DatabaseConfig {
private final DatabaseProperties databaseProperties; // application.yml에서 주입
@Bean
public DataSource dataSource() {
return DataSourceBuilder.create()
.url(databaseProperties.getUrl())
.username(databaseProperties.getUsername())
.password(databaseProperties.getPassword())
.build();
}
}
// Properties 클래스
@ConfigurationProperties(prefix = "database")
@Data
public class DatabaseProperties {
private String url;
private String username;
private String password;
}
여러 개의 Properties를 주입받는 경우:
@Configuration
@RequiredArgsConstructor
public class AppConfig {
private final DatabaseProperties databaseProperties;
private final RedisProperties redisProperties;
private final EmailProperties emailProperties;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(redisProperties.getHost());
factory.setPort(redisProperties.getPort());
return factory;
}
}
베스트 프랙티스
1. 항상 final 키워드 사용
의존성 주입받는 필드는 final로 선언하여 불변성을 보장하세요.
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository; // 올바른 방법
private UserRepository userRepository2; // 잘못된 방법
}
2. 너무 많은 의존성 주입 피하기
한 클래스에 너무 많은 의존성이 있다면 클래스 분리를 고려하세요.
// X 너무 많은 의존성 (Single Responsibility Principle 위반)
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final SmsService smsService;
private final PaymentService paymentService;
private final LogService logService;
private final FileService fileService;
// ... 더 많은 의존성들
}
// O 책임을 분리한 적절한 의존성
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final UserNotificationService notificationService; // 알림 관련 로직 분리
}
주의사항
순환 의존성 주의
// 잘못된 예: 순환 의존성 발생 가능
@Service
@RequiredArgsConstructor
public class UserService {
private final OrderService orderService; // OrderService도 UserService를 의존한다면 문제
}
다른 방식과의 비교
@Autowired vs @RequiredArgsConstructor
// X 필드 주입 (권장하지 않음)
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // 필드 주입, 테스트하기 어려움
}
// O 생성자 주입 (@RequiredArgsConstructor 사용)
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository; // 생성자 주입, 불변성 보장
}
@AllArgsConstructor와의 차이점
- @AllArgsConstructor: 모든 필드를 포함하는 생성자 생성
- @RequiredArgsConstructor: final이나 @NonNull 필드만 포함하는 생성자 생성
@AllArgsConstructor // 모든 필드가 생성자에 포함됨
public class ExampleService {
private final UserRepository userRepository;
private String optionalField; // 이것도 생성자에 포함됨
}
@RequiredArgsConstructor // final 필드만 생성자에 포함됨
public class ExampleService {
private final UserRepository userRepository;
private String optionalField; // 이것은 생성자에 포함되지 않음
}
@RequiredArgsConstructor는 Spring Boot에서 깔끔하고 안전한 의존성 주입을 구현하는 데 매우 유용한 도구입니다. 특히 다음과 같은 장점들을 제공합니다:
- 코드 간소화: 반복적인 생성자 작성 불필요
- 불변성 보장: final 필드를 통한 안전한 의존성 관리
- 테스트 용이성: 생성자 주입을 통한 쉬운 목 객체 주입
- 컴파일 타임 안전성: 누락된 의존성에 대한 컴파일 에러
Spring Boot 프로젝트에서 의존성 주입이 필요한 클래스라면 @RequiredArgsConstructor를 적극 활용해보세요. 코드가 훨씬 깔끔해지고 유지보수하기 좋은 구조를 만들 수 있습니다.
Spring Boot 아이콘 출처: Icons8 (https://icons8.com)