PromleeBlog
sitemap
aboutMe

posting thumbnail
의존성 주입(DI)과 제어의 역전(IoC) 완벽 이해
A Complete Guide to Dependency Injection (DI) and IoC

📅

🚀

들어가기 전에 🔗

이번 시간에는 좋은 소프트웨어 설계를 위한 핵심 원칙 중 하나이자, Spring 프레임워크의 근간을 이루는
의존성 주입(Dependency Injection, DI)
에 대해 알아보겠습니다.
DI는 코드를 유연하고, 재사용하기 쉽고, 테스트하기 좋게 만들어주는 매우 강력한 디자인 패턴입니다.

"객체를 직접 만들지 말고, 외부에서 넣어달라고 하세요"라는 이 단순한 아이디어가 어떻게 거대한 프레임워크의 핵심 철학이 되었는지, 그리고 Spring Boot가 이 개념을 어떻게 구현하는지 그 원리를 함께 탐구해 보겠습니다.

🚀

의존성 주입이란? 🔗

먼저, '의존성'이 무엇인지부터 생각해 봅시다.
객체 지향 프로그래밍에서 한 객체가 다른 객체의 기능을 사용할 때,
두 객체는 서로 의존 관계에 있다
고 말합니다.

의존성 주입
을 이해하기 위해, 고급 레스토랑의 셰프를 상상해 보겠습니다.
A 클래스 셰프가 요리를 하려면 B 클래스 식재료가 필요합니다.
즉, 셰프식재료에 의존합니다.

이처럼, 객체가 필요한 다른 객체(의존성)를
내부에서 직접 생성하는 것이 아니라, 외부(컨테이너)로부터 전달받는 것
이 바로 의존성 주입의 핵심입니다.

제어의 역전 (Inversion of Control, IoC) 🔗

의존성 주입은 더 큰 개념인
제어의 역전(IoC)
을 구현하는 방법 중 하나입니다.
과거에는 개발자가 직접 코드의 흐름(객체의 생성과 사용)을 제어했습니다.
하지만 이제는 그 '제어 권한'을 프레임워크(주방 시스템, Spring 컨테이너)에 '역전'시켰다는 의미입니다.
개발자는 어떤 객체가 필요한지만 알려주면, 프레임워크가 알아서 객체를 만들고, 연결하고, 관리해 줍니다.

🚀

스프링 부트와 DI 🔗

Spring Boot에서는
IoC 컨테이너(또는 ApplicationContext)
가 바로 그 '주방 시스템' 역할을 합니다.
  1. Bean 등록
    개발자는 @Component, @Service, @Repository 등의 어노테이션을 클래스에 붙여, "이 클래스로 만든 객체는 Spring 컨테이너가 관리해 주세요"라고 알려줍니다.
    이렇게 컨테이너가 관리하는 객체를
    Bean(빈)
    이라고 부릅니다.
  2. 의존성 주입
    한 객체가 다른 Bean을 필요로 할 때, Spring 컨테이너는 자신이 관리하고 있는 Bean들 중에서 적절한 것을 찾아 자동으로 '주입'해 줍니다.

예제: 생성자 주입 (Constructor Injection) 🔗

Spring에서 의존성을 주입하는 방법은 여러 가지가 있지만, 현재는
생성자 주입
방식이 가장 권장됩니다.
나쁜 예: 의존성을 직접 생성
@Service
public class MemberService {
    // MemberService가 MemberRepository를 직접 생성합니다. (강한 결합)
    private final MemberRepository memberRepository = new MemberRepository();
 
    public void join(Member member) {
        memberRepository.save(member);
    }
}
좋은 예: 생성자 주입
// 1. MemberRepository를 Bean으로 등록합니다.
@Repository
public class MemberRepository {
    // ... 데이터 저장 로직 ...
}
 
// 2. MemberService가 생성될 때, 생성자를 통해 MemberRepository를 주입받습니다.
@Service
public class MemberService {
    private final MemberRepository memberRepository;
 
    // Spring 컨테이너가 이 생성자를 보고, 자신이 관리하는 MemberRepository Bean을 자동으로 넣어줍니다.
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    // ...
}

🚀

주요 면접 예상 질문 🔗

의존성 주입은 객체 지향 설계와 프레임워크 이해도를 동시에 보여주는 질문 소재입니다.

1. 의존성 주입을 사용하는 이유는 무엇인가요? 🔗

DI의 근본적인 목표를 설명해야 합니다.
의존성 주입을 사용하는 가장 큰 이유는
느슨한 결합(Loose Coupling)
을 통해 코드의 유연성과 재사용성을 높이기 위함입니다.
객체가 의존성을 직접 생성하면, 그 의존성이 변경될 때마다 해당 객체의 코드를 직접 수정해야 합니다.
하지만 DI를 통해 외부에서 의존성을 주입받으면, 다른 구현체로 손쉽게 교체할 수 있어 변화에 유연하게 대처할 수 있습니다.
또한, 실제 객체 대신 가짜(Mock) 객체를 주입하기 쉬워져
단위 테스트(Unit Test)
가 매우 용이해집니다.

2. Spring에서 의존성을 주입하는 방법들과, 그중 생성자 주입을 권장하는 이유는 무엇인가요? 🔗

각 방식의 특징과 장단점을 비교하여 설명해야 합니다.
Spring에는 생성자 주입, 세터(Setter) 주입, 필드(Field) 주입 방식이 있습니다.
이 중
생성자 주입
이 가장 권장되는 이유는 다음과 같습니다.
첫째, 생성 시점에 모든 의존성이 주입되므로
객체의 불변성(Immutability)
을 확보할 수 있습니다.
둘째, 의존성을 final 키워드로 선언할 수 있어, 런타임에 의존성이 변경될 위험을 막을 수 있습니다.
셋째, 생성자의 인자가 많아진다는 것은 해당 객체가 너무 많은 책임을 지고 있다는 신호이므로,
객체의 책임(SRP)
을 다시 한번 생각해 볼 기회를 줍니다.
마지막으로, 순환 참조 문제를 컴파일 시점에 발견할 수 있습니다.

🚀

결론 🔗

오늘은 좋은 설계의 핵심 원칙인 의존성 주입(DI)에 대해 알아보았습니다.

참고 🔗