[Spring 5] 제어의 역전(IoC), 의존성 주입(DI)

2026. 2. 24. 19:28·프로젝트/Spring

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

일반적인 객체지향 프로그래밍 방식에서는 개발자가 직접 객체를 사용하는 시점에 객체를 생성해서 사용한다. 즉 객체에 대한 제어권이 개발자에게 있었다. 하지만 소스코드 상에서 하드코딩한 객체의 생성 구현은 객체간의 의존성을 높이게 된다. 직접적인 객체 생성으로 인해 결합도가 높아지게 된다.

public class ClubServiceLogic implements ClubService {
    private ClubStore clubStore;

    public ClubServiceLogic() {
        this.clubStore = new ClubMapStore();
    }
}

위 코드가 전통적인 방식이다. 개발자가 생성자에서 다른 객체를 사용하게 위해 직접적으로 `new`로 객체를 생성한다. 이러한 방식은 만약에 `ClubMapStore` 클래스를 직접 접근하는 것으로 객체간 결합도를 높이게 된다. 클래스가 많아지게 되면 어떤 클래스에서 해당 인스턴스가 몇 개가 어느 시점에 생성되고 소멸되는지 관리가 매우 어려워진다. 이러한 문제를 해결하기 위해 IoC, 제어의 역전이 도입되었다.

IoC (Inversion of Control)는 통제 방향의 변경을 의미한다. 객체의 생성과 소멸에 대한 생명주기 즉 제어권을 기존의 개발자에서 IoC 컨테이너에게 넘기는 것이다. IoC 방식에서는 객체의 생성, 의존성 설정, 소멸과정을 IoC 컨테이너가 담당하고, 개발자는 단지 객체의 생성을 신경 쓸 필요 없다. IoC의 구현 방식에는 DL(Dependency Lookup) 방식과 DI(Dependency Injection) 방식이 있다. 이 중 DL 방식은 컨테이너 API에 대한 의존성을 높이는 문제로 인해 사용하지 않고, DI 방식만 사용한다.

 

 

* IoC 컨테이너 (IoC Container)

스프링에서 IoC와 DI를 담당하는 컨테이너이다. 스프링 컨테이너 또는 ApplicationContext라고 불린다. 

  • Bean: 스프링 컨테이너가 관리하는 실제 객체이다. 관리되는 객체(Managed Object)라고도 한다.
  • IoC Container: IoC 컨테이너, 스프링 컨테이너, applicatonContext, Bean Factory 등으로 불린다.(엄밀하게는 각각이 의미하는 바가 약간씩 다르지만 비슷한 맥락에서 통칭해도 크게 문제 없다.) 빈을 생성하고 조회하고 돌려주는 역할을 한다. 
  • 작동 원리:
    1. 설정 정보(Java Config, XML(`applicationContext.xml`), 어노테이션)를 읽어 Bean 리스트를 저장한다.
    2. 객체를 생성하고 의존 관계를 연결(injection)한다.
    3. 개발자가 요청할 때 준비된 Bean을 제공한다.

 

[사용 예시: applicationContext.xml]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

        <context:component-scan base-package="io.uicheol.travelclub.spring" />
</beans>

applicationContext.xml 파일에서 `<context:component-scan base-package="~~" />`를 beans 태그로 감싸주면 자바 클래스를 전부 훑으면서 어노테이션으로 개발자가 지정해준 클래스들을 bean으로 관리하게 된다.


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

DI는 IoC의 개념을 구현하는 구체적인 방법이다. 한 객체가 다른 객체를 사용할 때, 이를 직접 생성하는 것이 아니라 외부 스프링 컨테이너로부터 주입받는 방식이다. 이러한 방식은 객체가 인터페이스만 알고 있으므로 느슨한 결합 (loose coupling)을 가능하게 한다. DI에는 3가지 주요 방식이 있다. 생성자 주입(constructor injection), 수정자 주입(setter injection), 메소드 주입(method injection)이다. 이 중 생성자 주입 방식만 거의 대부분 사용한다. 객체의 불변성을 보장하는 방식이기 때문이다.

 

// Bean 어노테이션 표시
@Repository("clubStore")
public class ClubMapStore implements ClubStore {
    private Map<String, TravelClub> clubMap;

    public ClubMapStore() {
        this.clubMap = new LinkedHashMap<>();
    }
    ...
}

// Bean 어노테이션 표시
@Service("clubService")
public class ClubServiceLogic implements ClubService {
    private ClubStore clubStore;

    // 생성자에서 의존성 주입
    public ClubServiceLogic(ClubStore clubStore) {
        this.clubStore = clubStore;
    }
    ...
}

Bean으로 관리할 객체는 빈 특성에 따라 `@Repository`, `@Service`, `@Controller`, `@Component` 어노테이션을 표시해준다. 빈의 id를 지정하지 않으면 클래스 이름으로 기본 등록된다. 지정할 때는 보통 첫글자만 소문자로 바꿔서 쓴다. 각각에 대해서는 자세히 후술하겠다.

 

코드에서 볼 수 있듯이 생성자 파라미터로 clubStore를 받아서 자신의 필드에 저장하는 방식을 사용한다. 생성자에서 `new`로 새로운 객체를 생성하는 것이 아니다. 스프링 컨테이너에서 관리하는 clubStore 객체를 받아서 사용할 뿐이다. 이렇게 되면  clubMapStore 구체적인 객체를 직접 접근하는 것이 아니고 인터페이스를 통해서만 메소드 호출을 하게 된다. 따라서 느슨한 결합도를 유지할 수 있게 된다. DI는 이런 방식으로 구현된다.


3. 어노테이션을 이용한 Bean 관리

위에서 본 것 처럼 applicationContext.xml 파일에서 `<context:component-scan base-package="~~" />`를 beans 태그로 감싸주면 자바 클래스를 전부 훑으면서 어노테이션으로 개발자가 지정해준 클래스들을 bean으로 관리하게 된다. 스프링에서는 bean의 특성이나 종류에 따라 아래와 같은 스테레오타입 어노테이션을 사용한다.

어노테이션 설명 역할
`@Repository` 데이터 액세스 계층 DB 접근계층의 로직을 처리하며, DB 예외를 스프링 예외로 변환하는 기능을 포함한다.
`@Service` 서비스 계층 비즈니스 로직을 수행하는 클래스임을 명시한다.
`@Controller` 프레젠테이션 계층 스프링 웹 서블릿에 의해 웹 요청(HTTP)을 처리하는 컨트롤러 빈으로 선정한다.
`@Component` 기본 타입 위의 계층 구조를 적용하기 어려운 일반적인 경우에 사용한다.

 

왜 이렇게 어노테이션을 클래스 역할에 따라 나눠놓았는가 하면 첫번째는 가독성과 의미부여의 역할이 있다. 이를 통해 협업과 유지보수에 도움이 된다. 두번째로 계층별 특수기능을 제공한다. 예를 들어 @Repository는 DB에서 발생하는 예외 SQLException 등을 스프링의 공통 예외 DataAccessException으로 자동변환한다. @Controller는 Spring MVC에서 해당 클래스를 웹 요청 핸들러로 인식하게 하는 역할이 있다. 


4. 스프링 Bean Scope

스프링에서 bean 객체는 기본적으로 싱글톤으로 관리된다. 즉 하나의 인스턴스만 생성해서 관리하는 것이 디폴트이다. 그러나 개발자가 원한다면 prototype, request, session 등으로 다르게 인스턴스를 관리할 수도 있다. 각각은 아래와 같다.

Scope 설명
singleton 스프링 컨테이너 당 하나의 인스턴스만 생성함 (디폴트)
prototype 컨테이너에 빈을 요청할 때마다 새로운 인스턴스를 생성함
request HTTP 요청 별 새로운 인스턴스 생성함
session HTTP 세션 별 새로운 인스턴스 생성함

 

이렇게 다른 스코프를 지정해주고 싶다면 역시 어노테이션을 활용해서 쉽게 지정할 수 있다.

@Repository
@Scope("prototype")
public class JdbcRecipeDAO implements RecipeDAO {
    ...
}

 

하지만 대부분의 경우는 디폴트인 싱글톤으로 관리하는 것이 일반적이다. 

'프로젝트 > Spring' 카테고리의 다른 글

[Spring 5] Spring MVC, DispatcherServlet  (0) 2026.02.28
[Spring 5] REST API, RESTful API 이름 짓기  (0) 2026.02.26
[Spring 5] 프로젝트 관리 도구 - Maven  (0) 2026.02.25
'프로젝트/Spring' 카테고리의 다른 글
  • [Spring 5] Spring MVC, DispatcherServlet
  • [Spring 5] REST API, RESTful API 이름 짓기
  • [Spring 5] 프로젝트 관리 도구 - Maven
sophon
sophon
sophon 님의 블로그 입니다.
  • sophon
    sophon 님의 블로그
    sophon
    • 카테고리 (172) N
      • 컴퓨터공학 (36)
        • 데이터베이스 (19)
        • 네트워크 (15)
        • 기타 이슈 (2)
      • 프로젝트 (16)
        • Java (8)
        • Spring (4)
        • Docker (4)
      • 코딩테스트 (95) N
        • BOJ (74)
        • 프로그래머스 (7)
        • 프로그래머스 SQL (12) N
        • PS Snippets (2)
      • 🌱 잡담 (22)
        • 자격증 (7)
        • 좋은 시 모음 (12)
        • 책과 영화 (3)
        • 기록 (0)
  • 전체
    오늘
    어제
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
sophon
[Spring 5] 제어의 역전(IoC), 의존성 주입(DI)
상단으로

티스토리툴바