new로 직접 객체를 만들지 않고, 스프링한테 만들어 달라고 하는 걸까요?

제어의 역전(IoC)과 의존성 주입(DI)

스프링부트(Spring Boot)를 사용할 때 반드시 이해해야 하는 핵심 개념 중 하나가 바로 제어의 역전(IoC) 과 의존성 주입(DI) 입니다.

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

IoC란 객체의 생성과 생명주기 관리의 주도권을 개발자가 아닌 프레임워크(스프링)에게 넘기는 것을 의미합니다. 기존에는 개발자가 직접 객체를 생성하고 관리했지만, IoC를 적용하면 스프링 컨테이너가 객체를 대신 생성하고 관리합니다. 이를 통해 코드의 결합도를 낮추고, 더 유연하고 확장성 있는 구조를 만들 수 있습니다.

핵심 포인트: IoC를 적용하면 객체 간의 의존 관계를 코드가 아닌 프레임워크가 결정하므로, 구현체를 교체하거나 테스트 환경을 구성할 때 기존 코드를 수정하지 않아도 됩니다.

  • 기존 객체 생성 방식 예시:

    JAVA
    public class A {
        private B b;
    
        public A(B b) {
            this.b = b;
        }
    }
    
    public class B {}
    
    public class Main {
        public static void main(String[] args) {
            B b = new B();
            A a = new A(b);
        }
    }
    

의존성 주입(DI: Dependency Injection)

DI는 IoC의 대표적인 구현 방법입니다. 객체가 필요로 하는 의존성(다른 객체)을 직접 생성하지 않고, 외부(스프링 컨테이너)에서 주입받는 방식을 말합니다. 이렇게 하면 객체 간의 관계를 코드 내부가 아니라 설정이나 어노테이션을 통해 정의할 수 있습니다. DI를 적용하면 테스트가 쉬워지고, 코드의 재사용성과 유지보수성이 크게 향상됩니다.

  • 의존성 주입 예시:

    JAVA
    // 의존성 주입이 없는 경우
    public class UserService {
        private UserRepository userRepository = new UserRepository();
    }
    
    // 의존성 주입이 적용된 경우
    public class UserService {
        private final UserRepository userRepository;
    
        // 생성자 주입 방식
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    

스프링에서는 @Autowired, @Component, @Service와 같은 어노테이션을 활용해 DI를 손쉽게 적용할 수 있습니다.

** 실무 팁:** 스프링 공식 문서에서는 생성자 주입(Constructor Injection) 방식을 권장합니다. 생성자 주입을 사용하면 의존성이 final로 선언되어 불변성이 보장되고, 필수 의존성 누락 시 컴파일 시점에 오류를 발견할 수 있습니다.

주의할 점

1. 필드 주입(@Autowired)은 테스트에서 의존성을 넣을 수 없다

@Autowired private UserRepository repo;로 필드 주입을 하면, 스프링 컨테이너 없이는 의존성을 주입할 방법이 없습니다. 단위 테스트에서 Mock 객체를 넣으려면 리플렉션을 사용해야 하므로, 테스트 코드가 불필요하게 복잡해집니다. 생성자 주입이면 new UserService(mockRepo)로 간단히 해결됩니다.

2. 순환 참조를 필드 주입으로 우회하면 런타임에 더 큰 문제가 된다

A가 B를, B가 A를 의존하는 순환 참조가 있을 때, 필드 주입은 빈 생성 후에 주입하므로 에러 없이 시작됩니다. 하지만 런타임에 @PostConstruct에서 상대 빈을 사용하려 하면 초기화되지 않은 객체에 접근하게 됩니다. 생성자 주입은 시작 시점에 즉시 에러를 발생시켜 순환 참조를 조기에 발견할 수 있습니다.

댓글 로딩 중...