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() {
// 물 끓이고 면 넣고...
}
}
이제는 전문 요리사에게 "라면 주세요"만 하면, 모든 걸 알아서 해줍니다.
우리는 요청만 하고, 실제 조리는 요리사(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) |
DI는 IoC를 구현하는 한 방법입니다.
객체가 의존하는 다른 객체를 직접 생성하지 않고, 외부에서 주입(inject)받는 방식입니다.
public class RamenCook {
private final Water water = new Water(); // 직접 생성
private final Noodle noodle = new Noodle();
public void cook() {
// 물과 면을 이용해 직접 조리
}
}
@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은 ApplicationContext라는 IoC 컨테이너를 통해 Bean 객체를 생성하고 관리합니다.
@Component, @Service, @Repository 등의 어노테이션 기반 스캔을 통해 Bean을 자동으로 등록합니다.
Component Scan으로 클래스 탐색
@Component, @Service, @Repository 등이 붙은 클래스를 스캔하여 Bean 후보로 지정
빈 등록 및 싱글톤 관리
스프링이 해당 클래스의 인스턴스를 Bean으로 생성하고 싱글톤으로 관리
의존성 주입 (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);
}
}
| 항목 | 설명 |
|---|---|
| 사용된 어노테이션 | @Service → Spring이 이 클래스를 Bean으로 등록 |
| 주입 대상 | PostRepository (인터페이스) |
| 주입 방식 | 생성자 주입 (Constructor Injection) |
| 장점 | - final 키워드로 불변성 보장- 테스트에 용이함 - 스프링 부트에서는 생성자가 1개면 @Autowired 생략 가능 |
👉 즉, Spring이 PostRepository를 자동으로 찾아서 주입해주는 구조이며,
이 방식은 실무에서도 가장 널리 사용되는 "생성자 주입 방식"입니다.

| 구성 요소 | 설명 |
|---|---|
| Spring IoC Container | Spring 애플리케이션의 핵심. Bean을 생성하고 관리 |
| PostRepository | JpaRepository를 상속한 인터페이스, 스프링이 구현체를 만들어 Bean으로 등록 |
| PostService | @Service로 등록된 Bean, 생성자 주입 방식으로 Repository를 주입받음 |
| Post(Entity) | DB와 매핑되는 순수 객체. Bean이 아님. Service나 Repository에서 직접 생성하여 사용 |
Service → Repository → Entitynew를 통해 직접 생성합니다.낮은 결합도 (Low Coupling)
객체 간의 의존성이 약해져서 변경에 유연합니다.
높은 응집도 (High Cohesion)
객체의 책임이 명확하고 집중됩니다.
테스트 용이성
Mock 객체를 쉽게 주입할 수 있어 단위 테스트가 쉽습니다.
유지보수 및 확장성 향상
새로운 기능 추가나 수정 시 다른 객체에 미치는 영향이 적습니다.
Spring 프레임워크는 IoC와 DI를 기반으로 동작하며, 개발자가 객체 생성과 의존성 관리에서 벗어나 비즈니스 로직 구현에 집중할 수 있도록 도와줍니다.