도림.로그

Tags

Series

About

[Spring 공부] Spring 핵심 원리 - 2

2022. 02. 24.

Spring Study

▶ Expand post list

    Spring에 대해 처음 공부하는 사람이 정리를 목적으로 작성한 글입니다. 오개념 등 잘못된 부분이 있을 경우 댓글로 가감없이 지적해주세요! 확인하는 대로 정확한 정보를 기반해 빠르게 수정할 수 있도록 하겠습니다.

    개요

    이번 Spring Boot 스터디는 배달의 민족의 김영한님께서 인프런에 올려주시는 자바 스프링 완전 정복 시리즈를 기반으로 진행한다. 그 시리즈의 가장 첫 강의인 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술을 끝내고, 다음 강의인 스프링 핵심 원리 - 기본편을 정리해보려 한다.

    이번 포스트에서는 IoC, DI, 그리고 컨테이너에 대한 이야기를 써보도록 하겠다.

    문제 인식

    전 포스트에서 다형성만으로는 SOLID를 지킬 수 없다는 사실을 확인했다. 특히, 그 중에서 OCP와 DIP를 지킬 수 없음을 알 수 있었다. 다시 한번 더 간단히 예제를 가져와 보면 다음과 같다.

    public class DriverA implements Driver {
        private Car driverACar = new Sonata();
    }

    운전자 역할을 구현하는 DriverA 클래스에서, 자동차 역할을 구현하는 Sonata 를 사용하고 있다. 현재 OCP와 DIP를 위반하는 가장 큰 원흉은 DriverA 내부에서 Sonata 를 직접 사용하고 있음에 있다. 이를 해결해야만 SOLID를 만족 할 수 있을 것이다.

    해결

    일단 DIP부터 지켜보자

    DIP 위반을 먼저 해결해보자. DIP를 만족하려면 일단 구체적 구현이 아니라 추상에만 의존하도록 변경해야 한다. 즉, 구체 클래스에 의존하는 것이 아니라 인터페이스에만 의존해야 한다. 그러면 코드가 다음과 같이 바뀔 것이다.

    public class DriverA implements Driver {
        private Car driverACar;
    }

    어라? 하지만 이 코드는 당연히 실행할 수 없다. 구현체가 없기 때문이다. 마치 운전하는 자세로 DriverA 혼자서 스쿼트를 하고 있는 것과 같달까. 이를 어떻게 해결할 수 있을까?

    흠… 누군가 이 DriverA 에게 Sonata 를 갖다주면 어떨까?

    관심사의 분리

    어플리케이션을 해리 포터 영화라고 생각해보자. 그리고 각각의 배역(해리, 론, 헤르미온느 등)을 각각의 인터페이스라고 생각하자. 그럼 이 해리라는 배역을 다니엘 래드클리프에게, 론은 루퍼트 그린트에게, 헤르미온느는 엠마 왓슨에게 배정하는 사람은 누구일까? 물론, 정말 많은 분들이 고심을 하시겠지만 그 분들을 통틀어서 기획자 정도로 정리할 수 있을 것이다.

    그렇다면 우리의 애플리케이션에도 이러한 구체적인 구현을 배정해주는 기획자를 만들어서 구체 클래스와 배정자의 관심사를 확실히 분리하면 어떨까?

    예를 들어, 위의 자동차 클래스를 기획 혹은 구성하기 위해 다음과 같이 DriveConfig 라는 설정 클래스를 만든다고 가정해보자.

    public class DriveConfig {
        public DriverA driverA() {
            return new DriverA(new Sonata());
        }
    }

    그리고, DriverA 는 어떤 자동차든 받을 수 있게 생성자를 조금 바꿔주자.

    public class DriverA implements Driver {
        public DriverA(Car car) {
            this.car = car;
        }
        private Car car;
    }

    자, 어떤가? DriverA 는 완벽히 Car 라는 인터페이스에만 의존하고 있고, 어떤 차(Sonata , TeslaModel3 등)가 들어올지 알 수 없다. 따라서 DriverA 는 의존 관계에 대한 고민을 DriveConfig 라는 구성 클래스에 맡기고, 자신의 구현과 실행에만 집중할 수 있다. 심지어, 인터페이스가 정해져 있기 때문에 Car 를 올바르게 구현한 클래스라면 무엇이든지 DriverA 가 사용할 수 있다.

    DIP를 해결한 것이다.

    그리고, 동시에 OCP도 해결되었다. 이제 새로운 자동차 G90 을 개발하더라도, DriverA 의 코드는 변경하지 않고 그대로 사용할 수 있다. 물론, 구성을 하는 DriveConfig 는 변경이 되겠지만, 실제로 자동차를 사용하는 사용 영역인 DriverA 의 코드는 전혀 변하지 않는다.

    이로써, 다형성을 통해 SOLID를 달성했다.

    아니 DI 말한다며

    너무 자연스러워서 눈치를 채지 못하신 분들도, 아니면 애진작에 눈치를 채신 분들도 있을 것이다. 자, 위의 코드를 다시 보자.

    public class DriverA implements Driver {
        public DriverA(Car car) {
            this.car = car;
        }
        private Car car;
    }
    
    public class DriveConfig {
        public DriverA driverA() {
            return new DriverA(new Sonata());
        }
    }

    지금 DriveConfig 에서 DriverA 에게 Sonata 를 생성해서 넣어주고, 다른 말로 주입하고 있다.

    자, 다시, DriverA 가 의존하는 Car 의 구현체인 Sonata주입하고 있다. 이렇게, 필요한 의존 관계에 있는 구체 클래스를 주입해준다고 해서 이를 의존 관계 주입, 의존성 주입, 그리고 영어로는 Dependency Injection 다시 줄여서 DI라고 부른다.

    엄청 어려운 개념이라고 생각해서 겁을 잔뜩 먹었었는데, 생각보다 이해할만 했고, 그 동기도 크게 이해가 되었다.

    IoC, DI, 그리고 컨테이너

    제어의 역전

    Java나 C 언어로 프로그래밍이나 코딩을 처음 배우면, 대개 main 함수라는 것을 통해서 프로그램을 실행함을 알 수 있다. 그리고 거기서 우리가 필요한 객체를 생성하기도 하고, 사용하기도 하며 조건문과 반복문을 통해서 프로그램의 흐름을 우리, 즉 개발자가 직접 제어했다.

    당연한 것이다. 프로그램은 내가 짠 대로 움직이니까.

    하지만, 위의 DriveConfig 등의 구성 정보를 보면 이제 구현 객체인 DriverASonata 는 자신의 로직을 실행하는 역할만 담당할 뿐, 실제로 주도적으로 프로그램의 제어 흐름을 가지지 않는다는 사실을 알 수 있다. 마치, DriverACar 라는 인터페이스는 알고 있지만, 도대체 어떤 자동차가 실행될 지는 모르는 것처럼 말이다.

    프로그램의 제어 흐름에 대한 권한을 모두 DriveConfig 가 가지고 있다. 심지어, DriverA 의 인스턴스 또한 DriveConfig 가 생성한다. 또한 이 생성 시에 Sonata 가 넘어오는지, TeslaModel3 가 넘어오는지 이 DriverA 는 전혀 인지하지 못한다. 그저, 운전을 할(자신의 로직을 수행할) 뿐이다.

    이렇게, 프로그램의 제어 흐름을 구현 객체에서 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(Inversion Of Control, IoC)이라 한다.

    컨테이너

    이런 DriveConfig 처럼, 객체를 생성하고 관리하면서 의존관계를 연결해주는 것, 또는 이 기술을 IoC 컨테이너 또는 DI 컨테이너라 한다. 최근에는 의존 관계 주입에 포커스가 맞추어져 있어서 주로 DI 컨테이너라는 명칭으로 더 자주 부르곤 한다.

    또 다른 명칭으로는, 객체를 조립한다고 해서 어셈블러, 혹은 객체를 생성한다고 해서 오브젝트 팩토리 등으로 부르기도 한다. Spring 에서도 BeanFactory, ApplicationContext가 이 역할을 담당하고 있다.

    다음은

    이 내용을 강의에서 배우면서, 정말 가슴이 두근거리는(?) 경험을 했다. 이렇게 스토리텔링 탄탄하게 객체 지향 프로그래밍을 배워본 경험이 없어서 더 그랬던 것 같다.

    다음 포스트에서는 Spring 컨테이너와 Spring 빈에 대한 내용을 정리해보도록 하겠다.

    끝.

    #java#spring#inflearn#OOP#SOLID#DI#IoC

    © 2024, Built with Gatsby