인프런 커뮤니티 질문&답변

nsonestudy2님의 프로필 이미지
nsonestudy2

작성한 질문수

Java/Spring 주니어 개발자를 위한 오답노트

설계 (1) : 의존성이란 무엇인지? (DI vs DIP)

IoC != DI에 대한 질문입니다.

해결된 질문

작성

·

747

1

안녕하세요,선생님 습하고 더운 여름 몸 건강히 계시길 바랍니다.

강의를 듣는 도중 궁금한 부분이 생겼습니다.

DIP와 IoC가 다르다고 말씀하신 부분이,

DIP가 의존성 주입을 역전함으로써 제어의 흐름을 바꾸지만

IoC가 의존성을 주입해주는 것은 아니어서 그렇다고 이해되는데

제대로 이해한 게 맞을까요? ㅜㅜ

또, Open-Closed 법칙을 설명해주시는 1:32초 부분에 수정에는 열려있어야 한다는 말씀을 하셨는데

수정 시 많은 메소드 혹은 클래스를 수정해야 하는 경우가 수정에 열려있는 것이 아닌지요 ㅜㅜ...

제가 잘 모르다보니 헷갈려서 질문드립니다!

 

답변 2

5

김우근님의 프로필 이미지
김우근
지식공유자

안녕하세요.
어떻게 답변드릴지 고민하다가 아래와 같이 답변 남겨봅니다.

DIP vs IoC

우선 질문 내용 중 오류가 있어서 먼저 교정하려고 합니다.

“DIP가 의존성 주입을 역전함으로써 제어의 흐름을 바꾸지만”

여기에 틀린 말이 있어서 아래와 같이 교정드립니다.

  1. DIP가 의존성 주입을 역전하는 것은 아닙니다. DIP는 의존성을 역전하는 것입니다.

  2. DIP는 제어의 흐름을 역전하지 않습니다.

  3. DIP와 IoC는 그냥 완전히 다른 개념입니다. ‘아예 연관조차 짓지 않았으면 좋겠다.’라는 의미로 넣은 건데, 이런 질문이 올라오는 것을 보니 약간 괜히 넣었나 하는 생각도 드네요.

IoC를 이해하는 게 너무 어려우시면 IoC는 ‘스프링이 자동으로 의존성 주입을 해주는 것‘이라고 생각하고 넘어가셔도 괜찮습니다. 사실 IoC는 설계하면서 자주 보게 될 일 없는 개념입니다.

그리고 IoC(Inversion of Control)가 곧 제어의 흐름 역전입니다. 이걸 이해하려면 일단 제어의 흐름이 뭔질 알아야 하는데요. 아래와 같은 코드가 있다고 가정합시다.

class Bar {
}

class Foo { 

    private final Bar bar;
    public Foo(Bar bar) {
        this.bar = bar;
    }
}

이런 코드가 있을 때 Foo, Bar를 객체로 만들려면 아래와 같이 개발자가 직접 만들어줘야합니다.

Bar bar = new Bar();
Foo foo = new Foo(bar);

이를 보고 개발자가제어의 흐름을 갖고 있다고 말합니다. 이게 왜 제어의 흐름이냐면, Foo에 주입해주는 Bar 객체를 어떤 인스턴스를 주입해주냐에 따라 프로그램의 제어가 달라질 수 있기 때문입니다.

완전히 이해가 안되시면 객체 생성도 개발자가 직접하고 foo에 들어갈 bar도 개발자가 원하는 것을 명시적으로 선택해서 넣어주기 때문에 '제어의 흐름'이라는 것을 개발자가 쥐고 있다 정도로만 인식해주시면 됩니다. 반면 스프링에서는

@Component
class Bar {
}

@Component
class Foo { 

    private final Bar bar;
    public Foo(Bar bar) {
        this.bar = bar;
    }
}

이렇게 컴포넌트 어노테이션을 달아주는 것만으로도 객체가 알아서 생성되고 주입이 됩니다. 즉 스프링 프레임워크에서는 객체를 생성하고 주입하는 일을 스프링이 해줍니다. '제어의 흐름을' 스프링이 해줍니다. 그래서 이를 보고 '제어의 흐름이 스프링 프레임워크에 넘어갔다'라고 말합니다.

그리고 일반적으로 생각하면 이렇게 객체를 생성하고 주입해주는 것은 원래 개발자가 직접 챙겨줘야 하는 일일 겁니다. 맞죠? 그런데 스프링 같은 프레임워크를 사용하니 프레임워크가 이걸 대신해주고 있습니다. 개발자가 프로그램을 제어해야 하는 데 프로그램이 제어하고 있으니 ‘역전’이 됬다고 표현하는 것입니다. 그래서 스프링을 IoC 컨테이너라고 부르는 거고요.

  • ‘제어 흐름이 프로그램에게 넘어가는 건 안 좋은 거 아니야? 개발자가 통제할 수 있어야 좋은 거 아닌가?’라는 생각이 드실 수도 있을 것 같습니다. 그런데 이건 그냥 표현이 그런 것일 뿐입니다. 제어 흐름이라고 하면 뭔가 대단한 것 같지만 실제로는 귀찮은 작업을 프로그램에 넘긴 것에 가깝습니다. 객체를 생성하는 것 자체가 하드 코딩이라서, new 키워드 자체가 약간 꺼려지고 귀찮은 일인데, 프레임워크가 관리해 준다고 하니까 냅다 넘겨준 거에요. 게다가 프레임워크가 의존 관계를 추적해서 컴포넌트 생성 순서를 고려해 컴포넌트를 만들어 준다고 하니까 더 편리한 면도 있고요. (ex. Foo 생성자에 Bar를 필요로 하므로, Foo는 Bar보다 먼저 생성될 수 없다.)

  • “제어가 넘어간다”라는 표현이 좀 오해스러운 면이 있어서 IoC는 할리우드 원칙이라는 이름으로 불리기도 합니다.

DIP / DI / IoC 이 셋은 어딘가 비슷한 부분이 있어서 뭔가 관통하는 하나로 엮고 싶게 만드는 개념들인데요. 그런데 하나로 엮으려고 하면 오히려 더 헷갈리기 시작합니다. 각각에 대해서만 정의가 완벽하면 됩니다!

 

OCP

"Open-Closed 법칙을 설명해주시는 1:32초 부분에 수정에는 열려있어야 한다는 말씀을 하셨는데"

확인해보니 강의 내용중 대본에 오류가 있었네요. 앞에서는 '수정에는 닫혀있어야 한다'라고 잘 말해놓고 이야기해주신 대사를 보니 말실수 한 게 맞습니다. 😭 "수정에는 닫혀있어야 한다"가 맞습니다. 오해를 하게 만들어 너무 죄송합니다. 이해하신 게 맞습니다. 확장에는 열려있어야하고 확장할 때 다른 메서드나 클래스를 수정해서는 안되므로 수정에는 닫혀있어야 합니다.

이야기해주신 부분은 조만간 교체해서 올리도록 하겠습니다. 교정해주셔서 감사합니다.

0

안녕하세요, 인프런 AI 인턴이에요.

DIP와 IoC가 다르다는 것은 맞습니다. DIP는 의존성 주입을 통해 제어의 흐름을 역전시켜 객체 간의 결합도를 낮추고 유연한 코드를 작성할 수 있도록 도와주는 원칙입니다. 하지만 IoC는 DIP와는 조금 다르게, 객체를 생성하고 관리하는 작업을 자신이 하지 않고, 외부에서 객체의 생성 및 관리를 제어하는 것입니다.

Open-Closed 원칙은 기존 코드를 수정하지 않으면서 새로운 기능을 추가할 수 있도록 하는 것을 말합니다. 수정에 열려 있다는 것은 기존 코드를 수정하지 않고도 새로운 기능을 추가하거나 기능을 변경할 수 있도록 코드를 구성하는 것을 의미합니다. 따라서 만약 수정 시 많은 메소드나 클래스를 수정해야 한다면 수정에 닫혀있는 것입니다.

이해가 어려운 부분이 있다면 추가적으로 질문해주세요. 감사합니다.

nsonestudy2님의 프로필 이미지
nsonestudy2

작성한 질문수

질문하기