프레임워크/Spring Boot

의존성 주입(Dependency Injection, DI)

코드몬스터 2025. 3. 27. 09:25
728x90

 

의존성 주입은 원칙을 설계하기 위한 구체적인 기법이다.

 

목차

  1. "의존"이란
  2. 의존성 생성
  3. 의존성 주입 방법
  4. 불변 VS 가변
  5. 불변 vs 상수

"의존" 이란?

A 클래스가 다른 클래스의 기능이 필요해서 다른 클래스를 호출하여 사용하는 경우, A 클래스는 그 클래스에 "의존"한다고 말한다.

 

의존성 생성

어쨌든, A 클래스는 다른 클래스의 기능이 필요하기 때문에 의존을 할 수 밖에 없다.

그렇다면 어떠한 방법으로 의존성을 생성할 수 있을까?

 

1. 의존성의 명시적 생성

개발자가 직접 의존성을 생성한다는 의미에서 이렇게도 부른다.

이러한 경우, A 클래스는 다른 클래스의 생성에 민감하기 때문에 강한 의존성 또는 강한 결합을 의미한다. 

  • 다른 클래스가 바뀌면 A 클래스도 수정해야 할 가능성이 높다.
  • 테스트하려고 해도 다른 클래스를 대체(Mocking)하기 어렵다.
UserService userService = new UserService(); // 직접 생성

 

2. 의존성 주입

반대로 외부에서 의존성을 대신 주입해달라고 한다.

이러한 경우, A 클래스는 다른 클래스의 생성에는 관심이 없고 오직 필요한 기능(동작)만 기대할 뿐이다.

  • 인터페이스 클래스로의존성을 주입하면 더 느슨해짐
    ⇒ 개방-폐쇄 원칙(Open-Closed Principle, OCP)와 관련이 있다.
    ⇒ 구현 클래스를 유연하게 변경할 수 있기 때문이다.
// 생성자 주입 예시
private final B b;

public A(B b) { // 외부에서 주입
    this.b = b;
}

 

의존성 주입 방법

의존성을 주입하는 방법에는 아래와 같이 세 가지 방법이 있다.

 

1. 생성자 주입

  • 가장 권장되는 방식
  • 불변성(immutable) 보장 final 키워드 사용 가능
  • 테스트 용이성 높음
public class A {
    private final OtherClass b;

    public A(OtherClass b) {
        this.b = b;
    }

    public void doSomething() {
        b.run();
    }
}

 

2. 필드 주입

클래스 내부에 선언된 변수(멤버 변수)를 필드라고 한다.

  • 코드가 간결함
  • 테스트 어려움, Mock 주입 불가
  • final 사용 불가 ⇒ 불변 객체 만들기 어려움
public class A {

    @Autowired
    private OtherClass b;

    public void doSomething() {
        b.pay();
    }
}

 

3. setter 주입

  • 선택적 의존성에 유리 (null 허용 가능)
  • 객체 생성 후 의존성 주입
  • 테스트 시 Mock 객체 주입에 유리
public class A {

    private otherClass b;

    @Autowired
    public void setOtherClass(otherClass b) {
        this.b = b;
    }
}

 

불변 VS 가변

의존성 주입 방법에서 불변(Immutable)과 가변(Mutable)에 대해 자주 언급이 된다.

두 성질(불변과 가변)이 붙은 객체에 대해 알아보자.

 

불변 객체

객체가 생성된 이후, 내부 상태(필드 값)가 바뀌지 않는 객체

 

가변 객체

객체가 생성된 이후에도, 내부 상태를 바꿀 수 있는 객체

 

불변(final) vs 상수(static final)

자바에서는 변수를 불변으로 만들기 위해 final 을 사용한다.

그리고 final이 붙으면 상수라고 생각할 수 있는데 상수와 final은 약간 다르다.

 

final

변수에 final을 붙이면 한 번만 값이 할당될 수 있도록 제한한다.

즉, 값을 변경할 수 없게 만드는 키워드이다.

final int x = 10;
x = 20; // ❌ 컴파일 에러 – final 변수는 재할당 불가

 

상수(static final)

클래스 차원의 상수 (공통값 + 불변성) ⇒ 상수라고 부름

public class Constants {
    public static final int MAX_USER = 100; // 진짜 상수
}

 

불변을 사용하면 장점

  1. 예측 가능성 증가
    ⇒  생성 후 객체의 상태가 변하지 않으니 **사이드 이펙트(side effect)**를 걱정할 필요 없음.
  2. 테스트 용이
    ⇒  테스트 시 객체가 바뀌지 않으니 신뢰성 높은 테스트가 가능.
  3. 스레드 안전성(Thread Safety)
    ⇒ 멀티스레드 환경에서도 동기화 없이 안전하게 사용할 수 있음.
  4. 버그 방지
    ⇒ 필드가 나중에 갑자기 null로 바뀌는 등의 예외 상황이 원천 차단됨.