본문 바로가기
카테고리 없음

의존성 주입(DI)이란? - Spring 컨테이너와 Bean 자동 연결

by code study 2026. 5. 15.

들어가며

Spring을 배우면서 가장 먼저 마주치는 개념 중 하나가 의존성 주입(DI, Dependency Injection) 입니다.
"의존성을 주입한다"는 말이 처음엔 어렵게 느껴질 수 있습니다.
이 글에서는 의존성 주입이 무엇인지, Spring 컨테이너가 어떻게 Bean을 만들고 연결해주는지 정리해보겠습니다.


의존성(Dependency)이란?

의존성이란 한 클래스가 다른 클래스를 필요로 하는 관계입니다.

public class MemberService {
    private MemberRepository memberRepository; // MemberService는 MemberRepository에 의존
}

MemberService가 동작하려면 MemberRepository가 반드시 필요합니다.
이때 MemberServiceMemberRepository의존한다고 합니다.


의존성 주입이 없다면?

의존성 주입 없이 개발자가 직접 객체를 생성하는 경우입니다.

public class MemberService {
    // 개발자가 직접 생성 - 의존성 주입 아님
    private MemberRepository memberRepository = new MemberRepository();
}

이 방식의 문제점은 아래와 같습니다.

문제 1. 강한 결합 - MemberService가 MemberRepository를 직접 생성
         → MemberRepository가 바뀌면 MemberService도 수정해야 함

문제 2. 테스트 어려움 - 가짜 객체(Mock)로 교체 불가

문제 3. 객체 관리 부담 - 개발자가 모든 객체의 생성과 소멸을 직접 관리

의존성 주입이란?

의존성 주입은 객체가 필요로 하는 다른 객체를 직접 생성하지 않고, 외부(Spring 컨테이너)에서 만들어서 연결해주는 것입니다.

// 의존성 주입 - 직접 생성하지 않고 외부에서 받음
@Service
public class MemberService {
    private final MemberRepository memberRepository;

    // Spring이 MemberRepository를 만들어서 여기에 주입
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

MemberServiceMemberRepository를 직접 만들지 않고, Spring 컨테이너가 만들어서 전달해줍니다.


Spring 컨테이너란?

Spring 컨테이너는 Bean을 생성하고, 관리하고, 필요한 곳에 주입해주는 공간입니다.
개발자 대신 객체의 생성과 소멸, 의존성 연결을 모두 처리합니다.

[Spring 컨테이너]
┌────────────────────────────────────┐
│                                    │
│  MemberController ──▶ MemberService│
│                       ↑            │
│  MemberService ──▶ MemberRepository│
│                       ↑            │
│  MemberRepository (DB 접근)        │
│                                    │
│  (객체 생성 및 의존성 연결 자동 처리) │
└────────────────────────────────────┘

@Component 계열 어노테이션 - Bean 등록 신호

Spring 컨테이너는 아래 어노테이션이 붙은 클래스를 자동으로 찾아 Bean으로 등록합니다.

어노테이션 역할 계층
@Component 일반 Bean 등록 공통
@Controller 웹 요청 처리 Presentation
@Service 비즈니스 로직 Business
@Repository DB 접근 처리 Data

@Controller, @Service, @Repository는 내부적으로 모두 @Component를 포함하고 있습니다.

// @Service 어노테이션 내부 구조
@Component  // @Component를 포함
@Target(ElementType.TYPE)
public @interface Service { ... }

컴포넌트 스캔 - Bean 자동 등록 과정

Spring Boot 애플리케이션이 시작되면 컴포넌트 스캔(Component Scan) 이 동작합니다.
지정된 패키지를 탐색하며 어노테이션이 붙은 클래스를 자동으로 Bean으로 등록합니다.

[애플리케이션 시작 과정]

@SpringBootApplication 실행
        ↓
컴포넌트 스캔 시작 (패키지 탐색)
        ↓
@Component, @Service, @Repository, @Controller 클래스 탐색
        ↓
발견된 클래스를 Bean으로 생성
  → MemberRepository Bean 생성
  → MemberService Bean 생성 (MemberRepository 주입)
  → MemberController Bean 생성 (MemberService 주입)
        ↓
의존성 자동 연결 완료
        ↓
애플리케이션 실행

코드로 전체 흐름 이해하기

// 1. Repository - DB 접근
@Repository
public class MemberRepository {
    public Member findById(Long id) {
        // DB 조회 로직
        return new Member(id, "홍길동", 25);
    }
}

// 2. Service - 비즈니스 로직
@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository; // Spring이 주입

    public Member getMember(Long id) {
        return memberRepository.findById(id);
    }
}

// 3. Controller - 웹 요청 처리
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberController {
    private final MemberService memberService; // Spring이 주입

    @GetMapping("/{id}")
    public Member getMember(@PathVariable Long id) {
        return memberService.getMember(id);
    }
}
[요청 흐름]

클라이언트 GET /api/members/1
        ↓
MemberController.getMember(1)
        ↓  (Spring이 주입한 MemberService 사용)
MemberService.getMember(1)
        ↓  (Spring이 주입한 MemberRepository 사용)
MemberRepository.findById(1)
        ↓
{ "id": 1, "name": "홍길동", "age": 25 } 응답

의존성 주입 전후 비교

// 주입 전 - 개발자가 직접 생성, 강한 결합
public class MemberController {
    private MemberService memberService = new MemberService(
        new MemberRepository() // 연쇄적으로 직접 생성
    );
}

// 주입 후 - Spring이 자동 연결, 느슨한 결합
@RestController
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService; // Spring이 알아서 주입
}
항목 직접 생성 의존성 주입
객체 생성 개발자가 직접 new Spring 컨테이너가 대신 생성
결합도 강한 결합 느슨한 결합
테스트 Mock 교체 어려움 Mock 교체 용이
유지보수 변경 시 영향 범위 큼 변경 시 영향 범위 작음
객체 관리 개발자가 직접 관리 Spring이 생명주기 관리

정리

  • 의존성 주입은 객체가 필요로 하는 다른 객체를 직접 생성하지 않고, Spring 컨테이너가 만들어서 자동으로 연결해주는 것이다.
  • @Component, @Service, @Repository, @Controller 어노테이션이 붙은 클래스는 Spring 컨테이너에 Bean으로 자동 등록된다.
  • 애플리케이션 시작 시 컴포넌트 스캔으로 Bean을 탐색하고, 의존성을 자동으로 연결한다.
  • 의존성 주입을 사용하면 강한 결합이 느슨한 결합으로 바뀌어 유지보수성과 테스트 용이성이 높아진다.
  • @RequiredArgsConstructor를 함께 사용하면 생성자 코드 없이 간결하게 의존성 주입을 구현할 수 있다.