IoC와 DI란 무엇이며, Spring에서는 어떻게 동작할까?

Chaaehyun·2025년 4월 25일

스프링 스터디

목록 보기
5/10
post-thumbnail

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

정의

IoC는 객체의 생성, 초기화, 의존 관계 설정 등 제어 흐름을 개발자가 아닌 프레임워크(예: Spring) 가 담당하게 만드는 디자인 원칙입니다.


🍜 라면 요리 예시로 쉽게 이해해볼게요!

✅ 기존 방식 (직접 요리하기)

우리가 라면을 끓일 때 직접 물을 끓이고, 면을 넣고, 스프도 직접 넣고 타이머까지 맞춰야 합니다.

이건 모든 과정을 내가 제어 한다고 생각하면 됩니다. → 전통적인 방식 (개발자가 모든 객체 생성과 조립을 직접)

public class RamenCook {
    private final Water water;
    private final Noodle noodle;

    public RamenCook() {
        this.water = new Water();     // 직접 생성
        this.noodle = new Noodle();   // 직접 생성
    }

    public void cook() {
        // 물 끓이고 면 넣고...
    }
}

✅ IoC 방식 (요리사에게 맡기기)

이제는 전문 요리사에게 "라면 주세요"만 하면, 모든 걸 알아서 해줍니다.
우리는 요청만 하고, 실제 조리는 요리사(Spring)가 담당합니다.

이게 바로 IoC입니다!
→ Spring이 객체 생성과 조립을 모두 담당하고, 우리는 필요한 것만 요청하는 것이죠!

@Component
public class RamenCook {
    private final Water water;
    private final Noodle noodle;

    @Autowired
    public RamenCook(Water water, Noodle noodle) {
        this.water = water;
        this.noodle = noodle;
    }

    public void cook() {
        // 라면 조리 시작
    }
}

🔁 요약하자면

구분직접 제어 (전통 방식)IoC (Spring 방식)
객체 생성개발자가 new로 직접 생성Spring이 대신 생성
객체 조립개발자가 직접 연결Spring이 자동 연결
책임 주체개발자프레임워크(Spring)

2. DI (Dependency Injection) - 의존성 주입

정의

DI는 IoC를 구현하는 한 방법입니다.
객체가 의존하는 다른 객체를 직접 생성하지 않고, 외부에서 주입(inject)받는 방식입니다.


DI의 세 가지 방식

  1. 생성자 주입 (가장 권장)
  2. Setter 주입
  3. 필드 주입 (테스트 어려움 등으로 권장하지 않음)

🍜 계속해서 라면 요리 예시로 설명해볼게요!

❌ 직접 재료 준비 (의존성 직접 생성)

public class RamenCook {
    private final Water water = new Water();   // 직접 생성
    private final Noodle noodle = new Noodle();

    public void cook() {
        // 물과 면을 이용해 직접 조리
    }
}
  • RamenCook 클래스가 Water, Noodle에 직접 의존하게 됩니다.
  • 그렇게 되면, 두 재료가 바뀌면 이 클래스도 수정해야 하므로 유지보수가 어렵습니다.

✅ DI 방식 (재료는 외부에서 주입)

@Component
public class RamenCook {
    private final Ingredient1 ingredient1;
    private final Ingredient2 ingredient2;

    @Autowired
    public RamenCook(Ingredient1 one, Ingredient2 two) {
        this.one = one;
        this.two = two;
    }

    public void cook() {
        // 외부에서 주입된 재료와 도구로 요리!
    }
}
  • 필요한 재료를 직접 만들지 않고, 외부(Spring)로부터 주입받는 구조 입니다.
  • 재료가 바뀌어도 코드 수정은 안해도 되겠죠?

3. Spring에서의 IoC/DI 동작 방식

🧠 Spring IoC 컨테이너

Spring은 ApplicationContext라는 IoC 컨테이너를 통해 Bean 객체를 생성하고 관리합니다.
@Component, @Service, @Repository 등의 어노테이션 기반 스캔을 통해 Bean을 자동으로 등록합니다.


⚙️ 동작 과정

  1. Component Scan으로 클래스 탐색
    @Component, @Service, @Repository 등이 붙은 클래스를 스캔하여 Bean 후보로 지정

  2. 빈 등록 및 싱글톤 관리
    스프링이 해당 클래스의 인스턴스를 Bean으로 생성하고 싱글톤으로 관리

  3. 의존성 주입 (Dependency Injection)
    다른 Bean이 필요한 경우, @Autowired 또는 생성자 등을 통해 자동 주입


✅ 실제 예시

이는 제가 실제로 작성한 코드 중 일부입니다.

package org.sopt.service;

import org.sopt.domain.Post;
import org.sopt.global.exception.CustomException;
import org.sopt.global.response.enums.ErrorCode;
import org.sopt.global.util.PostValidator;
import org.sopt.repository.PostRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
public class PostService {
    private final PostRepository postRepository;

    public PostService(final PostRepository postRepository){
        this.postRepository = postRepository;
    }

    @Transactional
    public void createPost(final String title) {
        PostValidator.validateTitleFormat(title);
        validateDuplicateTitle(title);
        validatePostTime();

        Post post = new Post(title);
        postRepository.save(post);
    }
}

💡 이건 어떤 DI 방식일까?

항목설명
사용된 어노테이션@Service → Spring이 이 클래스를 Bean으로 등록
주입 대상PostRepository (인터페이스)
주입 방식생성자 주입 (Constructor Injection)
장점- final 키워드로 불변성 보장
- 테스트에 용이함
- 스프링 부트에서는 생성자가 1개면 @Autowired 생략 가능

👉 즉, Spring이 PostRepository를 자동으로 찾아서 주입해주는 구조이며,
이 방식은 실무에서도 가장 널리 사용되는 "생성자 주입 방식"입니다.


💡 Spring IoC 컨테이너 관점에서의 의존성 주입(Dependency Injection) 흐름

📌 구성 요소 설명

구성 요소설명
Spring IoC ContainerSpring 애플리케이션의 핵심. Bean을 생성하고 관리
PostRepositoryJpaRepository를 상속한 인터페이스, 스프링이 구현체를 만들어 Bean으로 등록
PostService@Service로 등록된 Bean, 생성자 주입 방식으로 Repository를 주입받음
Post(Entity)DB와 매핑되는 순수 객체. Bean이 아님. Service나 Repository에서 직접 생성하여 사용

  • 이 흐름은 Spring이 어떻게 의존 객체를 생성하고 주입하는지를 설명합니다.
  • PostServicePostRepository를 의존하고, PostService 내부에서 비즈니스 로직을 수행하며 Post(Entity) 를 다룹니다.
  • 의존 방향 : Service → Repository → Entity
  • Entity(Post) 는 Spring이 관리하는 Bean이 아니며, new를 통해 직접 생성합니다.

✅ 장점

  • 낮은 결합도 (Low Coupling)
    객체 간의 의존성이 약해져서 변경에 유연합니다.

  • 높은 응집도 (High Cohesion)
    객체의 책임이 명확하고 집중됩니다.

  • 테스트 용이성
    Mock 객체를 쉽게 주입할 수 있어 단위 테스트가 쉽습니다.

  • 유지보수 및 확장성 향상
    새로운 기능 추가나 수정 시 다른 객체에 미치는 영향이 적습니다.


💡 결론

Spring 프레임워크는 IoCDI를 기반으로 동작하며, 개발자가 객체 생성과 의존성 관리에서 벗어나 비즈니스 로직 구현에 집중할 수 있도록 도와줍니다.

  • IoC(Inversion of Control)"누가 제어할 것인가?" 에 대한 개념
  • DI(Dependency Injection)"어떻게 의존성을 주입할 것인가?" 에 대한 구체적인 구현 방식이라고 이해하면 됩니다.
profile
세상을 사랑하는 컴퓨터학부생입니다 :>

0개의 댓글