Spring - IoC / DI

흑이·2022년 7월 6일
1
post-thumbnail

IoC(Inversion of Control)/DI(Dependency Injection)


IoC(Inversion of Control)란?

Library는 애플리케이션 흐름의 주도권이 개발자에게 있고
Framework은 애플리케이션 흐름의 주도권이 Framework에 있다

애플리케이션 흐름의 주도권이 뒤바뀐 것을 바로 IoC(Inversion of Control)라고 한다.


Java 콘솔 애플리케이션의 일반적인 제어권

예시 코드

public class Example2_10 {
    public static void main(String[] args) {
        System.out.println("Hello IoC!");
    }
}

main() 메서드가 호출되고 난 다음에 System 클래스를 통해서 static 멤버 변수인 outprintln()을 호출한다.

이렇게 개발자가 작성한 코드를 순차적으로 실행하는게 애플리케이션의 일반적인 제어 흐름이다.


Java 웹 애플리케이션에서 IoC가 적용되는 예

  • Java 콘솔 애플리케이션이 아니라 웹 상에서 돌아가는 Java 웹 애플리케이션의 경우

  • 서블릿 기반의 애플리케이션을 웹에서 실행하기 위한 서블릿 컨테이너

Java 콘솔 애플리케이션의 경우 main() 메서드가 종료되면 애플리케이션의 실행이 종료 된다.

하지만 웹에서 동작하는 애플리케이션의 경우 클라이언트가 외부에서 접속해서 사용하는 서비스이기 때문에 main() 메서드가 종료되지 않아야 한다.

그런데 서블릿 컨테이너에는 서블릿 사양에 맞게 작성된 서블릿 클래스만 존재하지 별도의 main() 메서드가 존재하지 않는다.

main() 메서드처럼 애플리케이션이 시작되는 지점을 엔트리 포인트(Entry Point)라고도 부른다.

main() 메서드가 없는데 어떻게 애플리케이션이 실행 되는 걸까?

서블릿 컨테이너의 경우 클라이언트의 요청이 들어올 때마다 서블릿 컨테이너 내의 컨테이너 로직(service()메서드)이 서블릿을 직접 실행시켜 주기 때문에 main() 메서드가 필요 없다.

서블릿 컨테이너가 서블릿을 제어하고 있기 때문에 애플리케이션의 주도권은 서블릿 컨테이너에 있다.

바로 서블릿과 웹 애플리케이션 간에 IoC(제어의 역전)의 개념이 적용되어 있는 것

Spring에는 이 IoC의 개념이 어떻게 적용되어 있나!? DI(Dependency Injection)로



DI(Dependency Injection)란?

Dependency는 의존성 주입 이라는 의미


의존성 주입이란?

예) A,B 두개의 클래스가 있고

A클래스에서 B클래스의 기능을 사용하기 위해 B클래스에 구현되어 있는 어떤 메서드를 호출
A클래스가 B클래스의 기능을 사용 할 때, A클래스는 B클래스에 의존한다.

MenuController클래스는 클라이언트의 요청을 받는 엔드포인트(Endpoint) 역할을 하고
MenuService클래스는 MenuController클래스가 전달 받은 클라이언트의 요청을 처리하는 역할을 합니다.

MenuController 클래스는 메뉴판에 표시되는 메뉴 목록을 조회하기 위해서 MenuService의 기능을 사용하고 있다.

MenuService 클래스의 객체를 생성한 후, 이 객체로 MenuServicegetMenuList() 메서드를 호출하고 있다.

이처럼 클래스 끼리는 사용하고자 하는 클래스의 객체를 생성해서 참조하게 되면 의존 관계가 성립하게 된다.

두 클래스 간에 의존 관계는 성립 되었지만 아직까지 의존성 주입은 이루어지지 않았습니다.

의존성 주입

MenuController 생성자로 MenuService의 객체를 전달 받고 있습니다.

이처럼 생성자를 통해서 어떤 클래스의 객체를 전달 받는 것을 '의존성 주입' 이라고 한다.

생성자의 파라미터로 객체를 전달하는 것을 외부에서 객체를 주입힌다 라고 표현

그렇다면 여기서 의미하는 객체를 주입해주는 ‘외부’는 무엇일까?

바로 CafeClient 클래스가 MenuController의 생성자 파라미터로 menuService를 전달하고 있기 때문에 객체를 주입해주는 외부가 된다.

클래스의 생성자로 객체를 전달 받는 코드가 있다면
객체를 외부에서 주입 받고 있다는 것, 의존성 주입이 이루어 지고 있다는걸 알 수 있다.



의존성 주입은 왜 필요한가?

의존성 주입을 사용할 때, 항상 염두에 두어야 하는 부분이 한가지
현재의 클래스 내부에서 외부 클래스의 객체를 생성하기 위한 new 키워드를 쓸지 말지 여부를 결정하는 것

애플리케이션 코드 내부에서 직접적으로 new 키워드를 사용할 경우 객체지향 설계의 관점에서 중요한 문제가 발생할 수 있다.


프론트와 협업 시 스텁(Stub)을 요청하게 된다면?

스텁(Stub)은 메서드가 호출되면 미리 준비된 데이터를 응답하는 것
즉, 고정된 데이터이기 때문에 몇 번을 호출해도 동일한 데이터를 리턴한다.
몇번을 호출해도 동일한 데이터를 리턴하는 것을 전문 용어로 멱등성(idempotent)을 가진다 라고 합니다.

MenuServiceStub 클래스를 보면 getMenuList()에 Stub 데이터로 채워져 있다.
MenuServiceStub 클래스를 사용하려고 보니, 이 MenuService 클래스를 의존하고 있는 CafeClientMenuController에서 MenuServiceMenuServiceStub 클래스로 불가피하게 변경해야 되는 상황이 발생


결국 new 키워드를 사용해서 객체를 생성하게 되면 참조 할 클래스가 바뀌게 될 경우, 이 클래스를 사용하는 모든 클래스들을 수정할 수 밖에 없습니다.

이처럼 new 키워드를 사용해서 의존 객체를 생성할 때, 클래스들 간에 강하게 결합(Tight Coupling) 되어 있다라고 한다.

의존성 주입을 하더라도 의존성 주입의 혜택을 보기 위해서는 클래스들 간의 강한 결합은 피하는 것이 좋다
느슨한 결합(Loose Coupling)이 필요하다.



느슨한 의존성 주입은 어떻게?

Java에서 클래스들 간의 관계를 느슨하게 만드는 대표적인 방법은 바로 인터페이스(Interface)를 사용하는 것

MenuControllerMenuService라는 클래스를 직접적으로 의존하는게 아니라 클래스 이름은 같지만 인터페이스를 의존하고 있다.

MenuControllerMenuService를 의존하고 있지만 MenuService의 구현체는 MenuServiceImpl인지 MenuServiceStub인지 알지 못 한다.

MenuController 입장에서는 그저 메뉴 목록 데이터를 조회할 수 만 있으면 된다.

MenuController가 생성자로 주입 받은 대상이 MenuService 인터페이스이기 때문에 MenuService 인터페이스의 구현 클래스이면 어떤 클래스도 전부 주입을 받을 수 있다.

(1)을 자세히 보시면 newMenuServiceStub 클래스의 객체를 생성해서 MenuService 인터페이스에 할당합니다.

이처럼 인터페이스 타입의 변수에 그 인터페이스의 구현 객체를 할당할 수 있는데 이를 업캐스팅(Upcasting)이라고 한다.

업캐스팅을 통한 의존성 주입으로 인해 MenuControllerMenuService느슨한 결합 관계를 유지하게 되었다.


그러나

클래스들 간의 관계를 느슨하게 만들기 위해서는 new 키워드를 사용하지 않아야 되는데, CafeClient 클래스의 (1)을 보면 MenuServiceStub 의 객체와 MenuController 객체를 생성하기위해 여전히 new를 사용하고 있습니다.

new 키워드는 어떻게하면 제거하고 의존 관계를 느슨하게 만들 수 있을까

  • Spring이 대신 해결

Spring 기반 애플리케이션에서는 의존성 주입을 누가 해주나!?

new 키워드를 어떻게 없앨 수 있었나!?
Config라는 클래스를 사용해서

Config 클래스에서 (3)과 같이 MenuController 객체 생성을 정의해 두면 (1)을 이용해서 이 객체를 애플리케이션 코드에서 사용하게 된다.

Config 클래스에 정의해둔 MenuController 객체를 Spring의 도움을 받아서 CafeClient클래스에게 제공을 하고 있는 것

Config 클래스는 단순한 클래스가 아니라 Spring Framework의 영역에 해당하는 것이고
이 Config 클래스가 실제 애플리케이션의 핵심 로직에 관여하지 않고 있다.
온전히 Spring Framework의 영역





정리

  • 애플리케이션 흐름의 주도권이 사용자에게 있지 않고, Framework이나 서블릿 컨테이너 등 외부에 있는 것 즉, 흐름의 주도권이 뒤바뀐 것을 IoC(Inversion of Control)라고 한다.

  • DI(Dependency Injection)는 IoC 개념을 조금 구체화 시킨 것으로 객체 간의 관계를 느슨하게 해준다.

  • 클래스 내부에서 다른 클래스의 객체를 생성하게 되면 두 클래스 간에 의존 관계가 성립하게 된다.

  • 클래스 내부에서 new를 사용해 참조할 클래스의 객체를 직접 생성하지 않고, 생성자 등을 통해 외부에서 다른
    클래스의 객체를 전달 받고 있다면 의존성 주입이 이루어 지고 있는 것이다.

  • new 키워드를 사용하여 객체를 생성할 때, 클래스 간에 강하게 결합(Tight Coupling)되어 있다고 한다.

  • 어떤 클래스가 인터페이스 같이 일반화 된 구성 요소에 의존하고 있을 때, 클래스들 간에 느슨하게 결합(Loose Coupling)되어 있다고 한다.

  • 객체들 간의 느슨한 결합은 요구 사항의 변경에 유연하게 대처할 수 있도록 해준다.

  • 의존성 주입(DI)은 클래스들 간의 강한 결합을 느슨한 결합으로 만들어준다.

  • Spring에서는 애플리케이션 코드에서 이루어지는 의존성 주입(DI)을 Spring에서 대신 해준다.


0개의 댓글