🤔 < IoC ? >

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

-출처: Wikipidia - 

In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of control as compared to traditional control flow. In IoC, custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

 

번역하자면, 

"제어의 역전(Inversion of Control, IoC)"은 소프트웨어 엔지니어링의 한 프로그래밍 원칙입니다. 전통적인 제어 흐름에 비해 IoC는 제어 흐름을 역전시킵니다. IoC에서는 사용자가 작성한 컴퓨터 프로그램의 일부가 일반적인 프레임워크로부터 제어 흐름을 받습니다. 이런 디자인을 가진 소프트웨어 아키텍처는 전통적인 절차적 프로그래밍과 비교해 제어를 역전시킵니다: 전통적인 프로그래밍에서는 프로그램의 목적을 표현하는 사용자 정의 코드가 일반적인 작업을 처리하기 위해 재사용 가능한 라이브러리를 호출하는 반면, 제어의 역전에서는 프레임워크가 특정 작업에 대한 사용자 정의 코드를 호출합니다." 으로 번역 된다.

 

더 자세히 설명하자면, 일반적은 프로그래밍에서는 프로그램이 어떠한 작업을 실행할지 결정하고 그 작업을 수행하는 코드를 "직접" 호출한다. 예를 들어, 프로그램이 DataBase에서 정보를 가져오는 작업을 수행해야 한다면, 그 작업을 수행하는 코드를 "직접" 호출하고 실행 한다.

 

허나 IoC를 사용하는 경우, 이러한 '제어'가 역전된다. 프로그램 자체가 더 이상 어떤 코드가 언제 실행될지 결정하지 않고, Freamwork가 그 역할을 담당해버린다. 프로그램은 필요한 기능을 수행하는 코드를 작성하고, 실행될지는 FrameWork가 결정한다. 

 

이로 인해 코드의 결합도가 낮아져 코드의 재사용성이 높아지며, 개발자는 프로그램의 전반적 흐름과 동작에 대해 신경 쓸 필요 없이 특정 작업을 수행하는 코드에 집중할 수 있게 된다. 

 


💻 < 코드 설명 >

A Class에서 B를 Field로 가지고 있고

생성자 내부에서 직접 생성해 필드를 초기화 하고 있다.

이러한 코드를 객체 생명주기를 직접 제어하는 코드라고 말할 수 있다.

제어란, 관리라는 말로도 대신 할 수 있다.

 


🤔 < 역전 ?>

앞서 본 것과 같은 A 클래스가 있다.

하지만 이 코드에서는 외부로 부터 B를 인자로 받아 초기화하고 있다.

이렇게 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 "제어를 역전시켰다"고 말할 수 있다.


🌳 < IoC가 필요한 이유> 

간단한 예시를 들어보겠다

배고픈 윤태영은 점심으로 서브웨이를 먹으러 갔다.

이탈리안 비엠티를 주문하자 서브웨이는 기본 레시피대로 만들기 시작한다. 하지만 윤태영은 나오는 재료를 변경하고 싶었다고 말을 한다. 서브웨이는 이탈리안 비엠티 레시피가 아니라며 당황을 한다.

(02:11)

앞서 본 얘기를 코드로 살펴보면 빵,치즈,소스를 모두 ItalianBMT Code 내에서 생성해 초기화 하는 것을 알 수 있다.

즉, 각 재료들에 대한 제어권이 객체 내부에 있는 것이다.

만약 윤태영과 같은 변경이 생기게 되면 ItalianBMT 에도 큰 변경이 필요해 진다.

< 이런 문제를 어떻게 해결할까? >

🔥 < IOC가 적용된 예시 >

태영이 요구한 재료로 주문하였고, 요구한 재료로 구성된 ItalianBMT를 만들어 주었다.

(02:59)

 

위의 예시를 코드로 살펴보면

ItalianBMT는 화이트,파마산 오레가노 빵등 어떠한 빵 종류도 받을 수 있는 Bread라는 인터페이스를 인자로 받고 있다.

이와 같이 Chesses,Sauce도 종류에 상관없이 인자로 받아, 필드를 초기화 할 수 있도록 되어 있다.

 

결과적으로 객체 내부에서 재료의 종류를 제거해 변경에 자유롭지 못하다는 코드가 외부에서 제어를 받으면서 변경에 자유로워 진것을 알 수 있다.

 

IoC 관점에서 다시 요약하자면 재료에 대한 제어권을 기존에는 이탈리안비엠티가 가졌었지만

변경된 코드에서는 태영이 가지게 된 것.

 

IoC 프로그램 모델은 "역할과 책임의 분리"라는 내용과 관련이 있다.

(03:42)

이렇게 역할과 책임을 분리해 객체의 응집도를 높이고

객체간 결합도를 낮추면서 앞서 본 예시와 같이 변경에 유연한 코드를 작성할 수 있는 구조가 될 수 있다.

IoC 설명을 찾아보면 할리우드 법칙을 본 적이 있을텐데

 

(04:09)

Don't call us, we'll call you

즉, "우리에게 연락하지 마시오,알아서 불러 줄 테니" 라는 말이다.

우리가 어떤 것을 주도하여 호출하는 것이 아니라, 주도권은 빼앗기고 호출 당하기를 기다리는 모습이 IoC와 비슷해서 Gof 디자인 패턴에서는 Holywood principle이라고 불리기도 한다.

 

할리우드 원칙에서는 DIP에 대한 설명도 있는데

실제로 DIP와 IOC는 함께 사용되길 권장 받는 관계 이기도 하다. 그만큼 헷갈리기 쉬울 수도 있어

 

DIP에 대해서도 이야기를 해보겠다.

 

🔥 < DIP (Dependency Inversion Principle) >

(04:56)

DIP란 Dependency Inversion Principle의 약자로서 번역을 하면 "의존 역전 원칙" 이라는 뜻을 가지고 있다.

DIP는 상위 레벨의 모듈은 절대 하위 레벨 모듈에 의존하지 않는다. 둘 다 추상화에 의존해야 한다.

🤔 그렇다면 상위 레벨 모듈은 무엇이며 하위 레벨 모듈은 무엇이며 추상화에 의존한다는 말은 무엇일까?

쉬운 예시를 들어보겠다.

앞서 본 코드에서 WhiteBread라는 Field를 포함하는 ItalianBTM Class를 High Level Module, Field로 포함되는 WhiteBread를 Low Level Module이라고 볼 수 있다.

ItalianBTM에서는 기본 빵이 WhiteBread이다.

ItalianBTM라는 고수준 모듈에서 저수준 모듈인 WhiteBread에 의존하고 있었던 것이다.

하지만 태영이 파마산오레가노로 변경해달라고 한다면 화이트는 의존하고 있던 이탈리안비엠티 코드에서도 연쇄적으로 영향이 일어난다. 이 문제를 DI(Dependency Injection)를 이용해 추상화로 해결해 보겠다.

 


 

 

(06:44)

위의 그림을 살펴보면, High Level Module인  ItalianBTM는 더 이상 Low Level Module인 WhiteBread에 의존하고 있지 않고, Bread Interface에 의존하고 있다.

Low Level Module인 Bread 종류들도 이 Bread Interface에 의존하고 있다.

즉,모두 추상화에 의존하고 있는 것이다.

여기서 "Bread"는 High Level Module인 ItalianBMT 입장에서 만들어졌고

따라서 상황이 역전되어 Low Level Module이 High Level Module에 의존하게 된 것을 알 수 있다.

 

이렇게 의존 방향이 역전되는 것을 의존 역전(DI) 이라고 할 수 있다.

 

위의 코드도 High Level Module인 ItalianBMT가 Low Level Module인 재료의 종류에 의존하지 않으면서

Cheese, Bread, Sauce Interface 재료들에 의존하며 이미 DI가 적용되어 있던 것이다. 

 

IoC와 DIP는 Class간 결합을 느슨히 하고자 하는 같은 목적이 있다. 

두 원칙은 하나의 Class의 변경이 다른 Class들에게 전이되는 것을 최소화함으로써, App을 지속 가능하게 하며,확장성 있게 만들고자 한다. 그래서 시너지가 높은 DIP,IoC를 함께 사용하도록 강력하게 권장하고 있다.

 

 

 

 

 

참고: https://www.youtube.com/watch?v=8lp_nHicYd4

'스프링부트' 카테고리의 다른 글

Member class 고밀도 상세 분석  (0) 2023.04.06
ApplicationException code analysis  (0) 2023.04.05
Container in springboot  (0) 2023.04.04
ResponseEntity 고찰  (0) 2023.04.04
Model이라는 데이터 전달자  (0) 2023.04.04

Container in springboot

* 영상 출처: https://www.inflearn.com/course/%ED%86%A0%EB%B9%84-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%9D%B4%ED%95%B4%EC%99%80%EC%9B%90%EB%A6%AC/unit/136735?tab=curriculum

(01:30)

'Container' 하면 무엇이 떠오르는가?

Docker Container 혹은 Spring에서 Ioc container가 떠오를 것이다.

우리가 Web program을 개발한다는 것은 Server에서 동작하며 무언가 기능을 제공해 주는 여러가지 Component를 만드는 것이다.

에를 들어 Component 하나를 만들었다고 가정한다면

회원 가입 이런 기능을 담당하는 Web Component를 만들었다고 한다면

(01:56)

Component는 서버에 존재하게 될 텐데 혼자서 일을 하지 못한다.

Component는 뭐가 필요하냐면 앞에 항상 'Web Client'가 필요하다.

(02:38)

Web Client가 뭔가 요청을 주면 그때 Component가 일을 한다.

Component는 여러 가지 기능이 있겠지만 핵심적인 목표는 Dynamic Content를 만드는 것이다.

만약에 Dynamic 하게 매번 바뀌는 Content가 아닌,항상 고정되어 있는 똑같은 페이지를 띄운다면 Static한 Resource를 올려놓고 항상 똑같은 화면을 보세요. 이렇게 응답을 주면 되는 것이다.

Component를 만든다 혹은 코드를 작성한다는 것은 Dynamic content를 만들어서 다시 Client한테 Request 한다는 것이다.

(02:58)

요청과 응답 쌍으로 돌아가게 되어 있고

그 요청을 처리하는 하나의,하나 이상을 웹컴포넌트가 존재하고, 그 웹컴포넌트 하는 중요한 일은 다이나믹 컨텐트를 만드는 일 이다.

근데 웹 컴포넌트는 항상 웹 컨테이너 안에 들어가 있어야 한다.

(03:48)

웹 컨테이너는 1차적으로 웹 컴포넌트를 관리하는,컴포넌트가 서버 쪽에서 떠 있는 거니 누군가가 시작할 시켜 줘야 할 것 아닌가?

Component를 Memory에 올리고, New 해서 진입해 주는 그런 Class를 Internet으로 생성을 해주고, Service가 되는 동안에 계속 메모리 위에서 동작하도록 관리해 주는 작업이 필요하다. 그런 Memory에서 관리를 해주는, 흔히 말하는 lifecycle을 관리해 주는 그런 역할을 한다.

그리고

일반적으로 하나의 Container는 하나의 Component만 가지고 있지 않다.

(03:56)

이 안은 많은 종류의 하나의 웹컨테이너가 제공하는 서비스를 담당하는 컴포넌트들을 여러 개를 가지고 있을 수 있다.

회원 가입도 해야 되고, 로그인도 해야 되고, 조회도 해야 되고, 주문도 나와야 될 것 같은 그런 등 등의 기능을 가진 여러 개의 컴포넌트들 컨테이너가 다 관리를 해줘야 된다.

또 해줘야 하는 중요한 것은

클라이언트로부터 들어온 요청을 어느 컴포넌트가 담당 할지 결정해서 연결해주는 작업을 해 줘야 된다. 그래서 웹 요청이 들어오면 컨테이너는 정해진 어떤 룰에 따라서 '어느 컴포넌트가 요청을 처리해줘' 라고 넘겨준다.

어떤 컴포넌트가 요청을 처리할지 결정하는 것, 그리고 그거를 선택하는 작업을 우리가 흔히 라우팅 또는 맵핑이라고 얘기한다. 스프링에서 HandlerMapping 이라고 하는 이런 것들이 웹 요청을 담당하는 컴포넌트를 결정 해주는 것이다.

우리가 웹컨테이너 웹컴포넌트라고 얘기를 했는데 이거를 이제 자바 용어로 한번 바꿔 보자. 자바에서는 Web component를 뭐라고 부를까? 바로 Servlet이라고 부른다.

우리가 잘 알고 있는 Servlet이 바로

'Web component'다. 그리고 Servlet을 관리해주는

Container가 'Servlet Container'라고 불린다.

유명한 Servlet Container는 'Tomcat' 같은 것이 있다.

(05:23)

위의 사진을 보면, Tomcat에 여러 개의 SERVLET들을 여러 개 띄어놓고, 그 다음에

Mapping information를 넣으면 Request에 따라서 특정 SERVELT이 해당 Request를 처리할 수 있도록, 그 응답을 Response해서, 다시 Client, Browser라든지 아니면 API call한 Client한테 return해주는 이런 작업을 담당하는 Servelt Container가 존재 한다.

이 것이 전통적인, 90년대 후반부터 시작되는 Java Web programming basic Component가 되는 것이다.

그런데 Servlet에 대해서 불만을 가진, 너무 제한적이고, 더 나은 방법으로 개발을 할 수 있으면 좋겠다 라고 생각했던 사람들이 만든 것 중에 하나가 바로, Spring Framework이다.

Spring도 Container라고 말 했었다.Spring Container는 그럼 무엇일까?

Servlet container를 대신 하는 건가? 라고 생각 할 수 있지만, 그것은 아니다.

Spring container는 뒤에 존재 한다.

(06:13)

Servlet container 뒤쪽에 있으면서도 Spring container도 똑같이 어떤 기능을 담당하는 BEAN을 (spring container에 들어가는 component들은 BEAN이라고 부른다.

JAVA BEAN에서 그 용어를 따 온것)

여러 개의 BEAN을 가지고 있는 Spring container가 Servlet container 뒤쪽에서

Servlet을 통해서 외부로 들어온 Request를 받아가지고 Spring container한테 다시 넘겨 주는 것이다.

그럼 Spring container가 이거는 어느 BEAN이 Request를 처리해라.

그럼, 하나의 BEAN이 이 것은 내가 작업 하는 동안에 이런 기능이 필요하니까 네가 이 작업을 해라. 라고 다른 BEAN을 호출 하기도 하고. Spring container안에서 우리가 만든

Application이 동작을 하는 것이다.

그럼 이러한 의문이 들 것이다. 그럼 Spring Container가 아예 Servlet container를 대체 하면 안될까? 근데 안된다.

기본적으로 자바의 표준 웹 기술을 사용하려면 어쨌건 Servlet container가 존재 해야 한다.

그런데 문제는 Spring Application을 개발하는 거에만 집중을 해서 저 Bean들은 구성은

어떻게 할 것이고 이거를 Spring container에 어떻게 담을 것이며 이런 것만 고민하고 개발을 하면 좋겠는데 이거를 동작 시키려면 가장 간단한 'Hello world' 서버 프로그램 하나라도 띄우려면 Servlet container가 반드시 무조건 떠야 한다.

Servlet container가 필요하다는 것이다.

근데 이 Servlet container를 띄우는 것이 간단하지가 않다. 굉장히 오래된 기술이기도 하고 많은 것을 고려 하려고 다양한 설정 방법들 이런 것들을 요구 하고 있다.

(07:52)

아마 연차가 있는 개발자는 Web.xml이라는 file을 보았을 것이다.

아마 꽤 많은 Spring framework 책에 이것이 등장 할 것이다.

'일단 Spring project를 만들려면 project에 web.xml을 다음과 같이 작성해 주세요.'

라고 전형적으로 나오는 그런 것이다.

Root context는 무엇이며 dispatcherservlet을 등록하는데 설정은 뭐고..

여기에 Servlet Mapping은 어디다, 이런 내용이 들어가 있고 위에도 ContextLoaderListener이런 것들을 설정 해줘야 한다.

이런게 들어가야지만 Servlet container가 뜨고, 그 Servlet container를 통해서 Spring container가 Call 되는 것이다.

근데 이것을 Spring develop하려면 이것을 다 기억 해야 하는 복잡함이 있다.

(10:14)

이런 많은 정보들을 우리가 단순한 스프링컨테이너 들어간 애플리케이션을 만든다고 하더라도 항상 이거를 설치하고 그다음에 실행하도록 하기 위해서는

꼭 필요하지만 그렇게 중요하지 않은,우리가 개발한 동안은 계속 신경 써야 되는게 아닌 이런 내용들을 알고 있어야 되는 번거로움이 있다.

저런 설정들을 모르면 기존 프로젝트에 복사를 해서 붙여 놨는데 하다가 오타 하나 생기면은 에러가 나서, '아 이건 어떻게 해결 해야 되나요?' 물어보고 다녀야 되고 굉장히 번거로운 일이 아닐 수 없다.

더 큰 문제는 뭐냐면 Servlet Container는 표준 스펙이고 그거를 구현한 제품을 갔다가 여러분이 사용해야 되는데,

우리가 잘 알고 있는 Tomcat 같은게 있긴 하지만

프로젝트를 하러 어떤 새로운 환경에 들어갔다고 가정 한다면.

프로젝트에서 '우리는 Tomcat 쓰지 않습니다. 다른 종류의 서블릿컨테이너 없습니다.'

그러면 그 Servlet container에는 web.xml file형식 말고 그 외의 설치부터 해서 배포방식, server, 여러가지 설정 방법 이런 것을 새로 배워야 한다. 또 tomcat마다 조금씩 달라지는 설정 까지도...

그래서 '서블릿컨테이너'가 없는 'Containerless web architecture' 만들어 줬으면 좋겠다.

이 얘기는 뭐냐면 서블릿컨테이너 아예 안 쓰고 어떻게 스프링 컨테이너가 웹 요청을 바로 받을 수 있을까 뭐 이런 얘기는 아니고 서블릿컨테이너라는게 필요 하지만, 이 동작을 해야 되지만 이거를 설치하고 관리하고 신경 쓰기 위해서 개발자가 뭔가를 시간을 들이고 지식을 학습을 하고 적용하는 이런 위에 수고들 없이 할 수 있으면 좋겠다.

제거해 줬으면 좋겠다. 라는 것.

Containerless가 적용이 되면 어떻게 될까?

(12:10)

실제로는 Servlet container가 동작을 하고 있다. 거기 web.xml 이나 혹은 요즘에 그것에 대응되는 java code를 작성하는방법. 아무튼 이런 것들이 다 알아서 Spring boot를 통해서 제공이 된다.그렇게 설정들이 다 Setting 되니까, 거기에 대한 설정을 우리가 하나도 신경 쓰지 않고 개발을 시작하고 서버를 띄우고 동작을 시킬 수 있다.

실제로 코드를 만들어 보면 스프링부트에서 항상 메인메소드가 등장 한다. 거기서 뭔가 이게 처음 시작이 되는구나라는 걸 볼 수가 있다.

근데 그것만으로도 서블릿컨테이너 와 관련된 모든 필요한 것들이 다 쓰레기 되는 것이다. 이것이 컨테이너 리스 개발 방법이다.

'스프링부트' 카테고리의 다른 글

ApplicationException code analysis  (0) 2023.04.05
IoC(Inversion of Control) 제어의 역전  (0) 2023.04.04
ResponseEntity 고찰  (0) 2023.04.04
Model이라는 데이터 전달자  (0) 2023.04.04
@DataTimeFormat 고찰  (0) 2023.04.04

+ Recent posts