Spring 입문주차 - 3

이진일·2026년 4월 8일
post-thumbnail

3 Layer Architecture

이전 글에서 Spring이 DispatcherServlet으로 반복되고 귀찮은 일을 처리해주기 때문에 Controller만 잘 만들면 된다고 했다.

하지만 Controller클래스 하나로 비즈니스로직과 DB연결된 작업 모두 처리하면 기능이 추가될수록 코드가 복잡해지고 유지보수가 어려워진다.

이러한 문제를 해결하기 위해 Controller, Service, Repository 3개로 분리하여 3 Layer Architecture를 도입할 수 있다.

Controller

  • 클라이언트의 요청을 받는다.
  • 요청에 대한 로직 처리는 Service에게 전담한다.
    - 이 때 Request 데이터가 있다면 Service에 같이 전달한다.
  • Service에서 처리 완료된 결과를 클라이언트에게 응답한다.

Service

  • 사용자의 요구사항을 처리(비즈니스 로직)를 담당한다.
  • DB 저장 및 조회 처리는 Repository에게 요청한다.

Repository

  • DB 관리(연결, 해제, 자원 관리) 담당한다.
  • DB CRUD 작업을 처리한다.

업로드중..
위와 같은 구조로 연결된다고 볼 수 있다.


IoC와 DI

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

IoC는 말 그대로 "제어권이 뒤바뀌었다"는 뜻이다.

프레임워크가 없을 때 (일반적인 제어 흐름)
프레임워크를 사용하지 않는 자바 프로그램에서는 개발자가 직접 객체를 생성하고, 실행하고, 소멸시키는 등 객체의 생명주기를 관리합니다. 외부 라이브러리를 가져다 쓰더라도, 언제 호출할지는 개발자가 직접 결정한다. 즉, 개발자가 코드의 주인이다.

프레임워크를 사용할 때 (제어의 역전)
Spring을 사용하면 상황이 달라진다. 우리는 Controller, Service, Repository 같은 비즈니스 로직만 구현해 놓을 뿐, 이 객체들이 정확히 언제 생성되고 호출되는지는 신경 쓰지 않는다. 대신 Spring 컨테이너가 적절한 시점에 객체를 생성하고 로직을 실행한다.

이처럼 객체의 생명주기 관리와 흐름 제어권이 개발자에서 프레임워크로 넘어간 것을 제어의 역전(IoC)이라고 부른다.

IoC의 장점

  • 관심사의 분리: 개발자는 비즈니스 로직(기능 구현)에만 집중할 수 있다.
  • 유연성 향상: 구체적인 구현체와 프로그램의 흐름을 분리하여 유지보수가 쉬워진다.
  • 결합도 감소: 객체 간의 의존성이 낮아져 코드 변경 시 영향이 줄어든다.

DI (Dependency Injection): 의존성 주입

DI는 IoC라는 거대한 원칙을 실현하기 위한 디자인 패턴 중 하나이다. 많은 분이 IoC와 DI를 동일하게 생각하지만, 엄밀히 말하면 DI는 IoC를 달성하기 위한 구체적인 방법론이다.

  • 의존성(Dependency): 한 객체가 작동하기 위해 다른 객체를 필요로 하는 상태 (예: A가 B를 사용함)
  • 주입(Injection): 필요한 객체를 외부에서 직접 넣어주는 행위

즉, 객체가 스스로 필요한 객체를 만드는 것이 아니라, 외부(Spring)에서 만들어진 객체를 넣어주는 것이 DI의 핵심이다.

의존성 주입(DI)의 3가지 방법

Spring에서 의존성을 주입하는 방식은 크게 세 가지가 있다.

생성자 주입
클래스의 생성자를 통해 의존성을 주입받는 방식입니다. Spring에서 가장 권장하는 방식이다.

public class A {
    private final B b; // final 키워드 사용 가능
    
    // 생성자를 통해 B 객체를 주입받음
    public A(B b) {
        this.b = b;
    }
}

장점: 객체 생성 시점에 의존성이 주입되므로 불변성을 보장하며, 테스트 코드 작성 시에도 용이하다.

메서드(세터) 주입
Setter 메서드를 통해 의존성을 주입받는 방식이다.

public class A {
    private B b;
    
    public void setB(B b) {
        this.b = b;
    }
}

특징: 주입받는 객체가 변경될 가능성이 있는 경우 사용하지만, 실무에서는 드물다.

필드 주입
변수(필드)에 바로 @Autowired를 붙여 주입받는 방식입니다.

public class A {
    @Autowired
    private B b;
}

특징: 코드가 매우 간결해 보이지만, 외부에서 변경이 불가능하고 테스트하기 어렵다는 단점이 있어 권장되지 않는다.


IoC Container와 Bean

위에서 제어의 역전(IoC)과 의존성 주입(DI)의 개념을 알아봤는데 그렇다면 스프링에서는 대체 '어디서' 이 객체들을 관리하고, '무엇'을 관리하는 걸까? 그 정답인 IoC 컨테이너와 Bean에 대해 핵심만 콕콕 집어 정리해 보자.

스프링 컨테이너 (IoC Container)

스프링 컨테이너는 자바 객체의 생명주기를 관리하며, 생성된 객체들에게 추가적인 기능을 제공하는 공간이다.

  • 역할: 객체(Bean)의 생성, 배정, 설정, 그리고 소멸까지의 전 과정을 책임진다.
  • 대표적인 인터페이스: BeanFactory: 스프링 설정 파일에 등록된 Bean을 생성하고 관리하는 가장 기본적인 컨테이너이다.

스프링 빈(Bean)

Bean은 스프링 컨테이너에 의해 관리되는 자바 객체를 의미한다. 우리가 new 키워드로 직접 생성한 객체가 아니라, 스프링이 대신 만들어 컨테이너에 담아둔 객체들이다.

Bean 등록 방법

스프링에게 "이 객체는 네가 관리해줘!"라고 알리는 방법은 크게 두 가지이다.

  • 컴포넌트 스캔 (Component Scan): 클래스 위에 @Component 어노테이션(또는 이를 포함하는 @Service, @Repository, @Controller)을 붙여주면 스프링이 자동으로 찾아 Bean으로 등록한다.
  • 자바 설정 클래스: @Configuration이 붙은 클래스 내에서 메서드에 @Bean을 붙여 직접 등록한다.

IoC 컨테이너와 Bean의 동작 원리

  • 컨테이너 생성: 스프링 부트가 실행되면 컨테이너가 먼저 생성된다.
  • Bean 등록: 설정 파일이나 어노테이션을 훑어(스캔) 필요한 객체들을 생성하고 이름을 붙여 컨테이너 안에 보관한다.
  • 의존 관계 주입: 생성된 Bean들끼리 서로 필요한 의존성을 연결해 준다.
  • 애플리케이션 실행: 이제 필요한 곳에서 컨테이너로부터 Bean을 꺼내 사용한다.

0개의 댓글