카테고리 없음

IoC · DI · 빈 등록 정리

Gani053 2026. 3. 31. 10:54

오늘 IoC랑 DI 개념 정리했다.

사실 그동안 그냥 @Service 붙이면 되는 거 아닌가 하고 넘겼는데 제대로 짚고 가야 할 것 같아서 정리한다.


IoC (Inversion of Control) - 제어의 역전

직역하면 제어의 역전인데 처음엔 이게 무슨 말인지 몰랐다.

원래는 이렇게 쓴다.

public class UserService {
    private UserRepository userRepository = new UserRepository();
}

내가 직접 new로 객체를 만드는 거다. 근데 Spring 쓰면 이걸 내가 안 해도 된다.

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Spring이 알아서 객체 만들고 넣어준다. 이게 IoC다. 객체를 내가 제어하는 게 아니라 Spring이 제어하니까 "제어가 역전됐다"는 거였다. 이름이 어렵게 느껴지는데 개념 자체는 별거 없었다.


Bean (빈)

Spring이 관리하는 객체를 빈이라고 부른다. 그리고 그 빈들을 담아두는 공간이 IoC 컨테이너 (ApplicationContext).

빈 등록하는 방법

1. 어노테이션으로 등록

@Component
@Service
@Repository
@Controller

이거 붙이면 Spring이 알아서 빈으로 등록해준다.

@Service, @Repository, @Controller 다 내부적으로 @Component 포함하고 있음. 역할 구분하려고 나눠놓은 거라고 한다.

@Service
public class ParkingService {
    // 그냥 이렇게만 해도 빈으로 등록됨
}

 

2. @Configuration + @Bean으로 직접 등록

외부 라이브러리처럼 내가 소스코드를 못 건드리는 경우에 쓴다.

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

BCryptPasswordEncoder는 외부 클래스라서 @Component 못 붙이니까 이렇게 수동으로 등록하는 거다.

프로젝트에서 시큐리티 설정할 때 이 방식 썼는데 그때는 왜 이렇게 하는지 몰랐는데 이제 이해된다.


DI (Dependency Injection) - 의존성 주입

IoC를 구현하는 방법이다. Spring이 빈끼리 연결해주는 방식.

주입 방법이 3가지가 있다.

 

1. 생성자 주입 (권장)

@Service
public class VehicleService {

    private final VehicleRepository vehicleRepository;

    public VehicleService(VehicleRepository vehicleRepository) {
        this.vehicleRepository = vehicleRepository;
    }
}

final 쓸 수 있어서 불변성 보장되고, 순환 참조도 컴파일 시점에 잡힌다. Spring 공식 권장 방식.

 

2. 필드 주입 (비권장)

@Service
public class VehicleService {

    @Autowired
    private VehicleRepository vehicleRepository;
}

코드가 짧아서 처음엔 이게 편해 보였는데 final 못 쓰고 테스트할 때 문제가 생긴다.

 

3. setter 주입

@Service
public class VehicleService {

    private VehicleRepository vehicleRepository;

    @Autowired
    public void setVehicleRepository(VehicleRepository vehicleRepository) {
        this.vehicleRepository = vehicleRepository;
    }
}

처음엔 IoC, DI, 빈이 다 따로따로인 줄 알았는데 정리하고 보니까 결국 하나로 이어지는 개념이었다.

선택적 의존성 있을 때 쓰는 거라는데 솔직히 아직 이걸 쓸 상황이 잘 안 떠오른다.


셋의 관계 정리
IoC
 └── IoC 컨테이너가 Bean 생성·관리
          └── DI로 Bean 간 의존 관계 연결


느낀 점

솔직히 처음에 @Autowired 붙이면 되는 거 아닌가 하고 이해도 없이 쓰고 있었다.

근데 프로젝트하다가 순환 참조 오류 터지고 나서야 왜 생성자 주입 써야 하는지 몸으로 깨달았다.

필드 주입이 코드 짧아서 편해 보이는데 테스트 짤 때 의존성 못 넣어줘서 결국 다 생성자 주입으로 바꿨다.

그때부터 그냥 동작하니까 쓰는 거랑 이유 알고 쓰는 거 차이를 느꼈다.

Spring 처음 배울 때 제일 헷갈렸던 부분인데 이번에 제대로 정리하고 나니까 코드 읽는 게 좀 더 편해진 것 같다.