[React] Container Component 그리고 HOC
공부내용 공유하기

[React] Container Component 그리고 HOC

2015년. 리액트의 아부지 Dan Abramov는 Container / Presentational 두 가지의 구분을 통해 컴포넌트를 작성하는 패턴에 대해 글을 작성했다.

 

4년 후인 2019년. 그는 사람들이 자신이 소개한 패턴에 얽매여 맹목적으로 사용하는 것을 보며 깊은 현타가 왔고, hooks를 사용하면 렌더링과 상태 관리에 대한 분리가 가능하기 때문에 더 이상 이 패턴을 권장하지 않는다는 글을 남겼다.

 

그가 이야기한 컨테이너와 프레젠테이션 컴포넌트의 구분은 사실 세부 구현보다도 목적성에 의해 잘 드러난다.

 

프레젠테이션 컴포넌트

 

UI를 표현하는 것을 목적으로 함

상태가 없거나 극히 제한적

순수 컴포넌트(사이드 이펙트가 없는 순수 함수)를 지향

 

컨테이너 컴포넌트

 

로직을 관리하는 것을 목적으로 함

상태와 상태 관리 메소드가 있음

사이드 이펙트가 있을 수 있음

 

 

결국은 관심사 분리를 하고 레이어를 나누는 것이 핵심인데, 리액트에는 이러한 역할을 하기 위해 존재하는 패턴이 더 있다.

그중 대표적인 패턴이 바로 HOC이다.

 

 

Higher-Order Components – React

A JavaScript library for building user interfaces

reactjs.org

 

HOC 패턴을 소개하는 글 말귀에서, 아래와 같은 글을 발견할 수 있다.

 

You may have noticed similarities between HOCs and a pattern called container components. Container components are part of a strategy of separating responsibility between high-level and low-level concerns. Containers manage things like subscriptions and state, and pass props to components that handle things like rendering UI. HOCs use containers as part of their implementation. You can think of HOCs as parameterized container component definitions.

 

오우;; 무슨 이야기인지 잘 모르겠다.

휴... 이럴 땐 리액트 공식문서가 한글화가 되어있어 다행이야. 한글 문서를 보자

 

고차 컴포넌트와 컨테이너 컴포넌트라 불리는 패턴이 유사하다고 느낄 수 있습니다. 컨테이너 컴포넌트는 high-level과 low-level 관심사를 분리하는 전략 중 하나입니다. 컨테이너는 구독 및 state 같은 것을 관리하고 UI 렌더링 같은 것을 처리하는 컴포넌트에 props를 전달합니다. 고차 컴포넌트는 컨테이너를 그 구현체 중 일부에 사용하고 있습니다. 고차 컴포넌트는 매개변수화된 컨테이너 컴포넌트 정의로 생각할 수 있습니다.

 

고차 컴포넌트는 매개변수화된 컨테이너 컴포넌트 정의로 생각할 수 있습니다.

 

예...? 대체 무슨 소리죠? 매개변수화(parameterize)란 대체 무엇인가...

칼 마르크스, 목재의 도난과 노동자 계급 구성: 현재 논쟁에 대한 기여

 

문득 대학교 신입생 때 교양 수업이 생각났다.

그 당시 마르크스가 라인 신문에 기고한 아티클의 번역본을 읽으면서 '이게 정녕 한국어인가' 고뇌하던...

 

 

내가 나름대로 정리한 결론은 다음과 같다.


컨테이너 컴포넌트의 경우, 대개 childern으로 받은 요소에 컨테이너 컴포넌트의 상태 혹은 함수를 전달해주는 방식을 하고 있다.

 

<LoginHandler>
 {({ isLogin }) => <button disabled={isLogin}/> }}
</LoginHandler>


반면 HOC의 경우, HOC를 호출하는 단계에서 컴포넌트를 인자로 받아 컴포넌트를 반환하는 형태를 하고 있다.

const ButtonWithLogin = withLogin(Button)


따라서 매개변수화된 컨테이너 컴포넌트의 정의라는 말은 무엇이냐.

컨테이너 컴포넌트의 기능을 동일하게 하게 하면서 매개변수화를 통해 컴포넌트를 재정의 했다는 말이다.

말이 좀 어려운데, 원문을 보면 'You can think of HOCs as parameterized container component definitions.'라고 되어있다.

parameterized는 수학 용어로, 하나의 표현식에 대해 다른 파라미터를 사용하여 다르게 표현하는 것을 말한다.

Parameterization을 우리나라 말로 굳이 표현하자면 "매개변수화"로, 하나의 표현식에 대해 다른 parameter를 사용하여 다시 표현하는 과정을 뜻한다. 이 과정에서 보통 parameter의 개수를 표현 식의 차수보다 적은 수로 선택(ex. 3차 표현식 --> 2개 parameter 사용)하므로, 낮은 차수로의 apping 함수(ex. 3D --> 2D)가 생성 된다. Parameterization은 많은 수학적 배경 지식과 높은 이해력이 요구되는 분야 중 하나이다. 파라미터화의 기본 개념을 위해 2차원 원의 표현식부터 출발하자. 중심이 (a, b)이고 반지름이 r인 원의 implicit한 표현은 (x-a)2+(y-b)2=r2 이다. 이 2차원 도형을 1차 파라미터 u 로 표현하면 아래 그림과 같다. (u의 범위는 0~1 사이로 정한다.)

출처: http://kucg.korea.ac.kr/research/MeshandTextureProcessing/PointBased/Parameterization/index.shtml

하나의 표현식에 대해 다른 parameter를 사용하여 다시 표현하는 과정 === 컴포넌트를 다른 파라미터를 사용하여 재선언 하는 과정

(이라고 멋대로 해석해버리기~)

 

여기서의 파라미터를 함수의 파라미터(컴포넌트의 props)라고 해석하자면, 컨테이너 컴포넌트는 본인의 로직을 표현하기 위해 받아야 하는 props를 본인이 사용되는 맥락에 따라 동적으로 변경할 수 없는 형태이다.

 

오해의 소지가 있어 다시 정확히 말하자면, 받아야 하는 props의 내용물은 상황에 따라 바꿔서 받을 수 있지만! 받아야 하는 props의 형태 즉, props의 인터페이스(타입) 자체는 동적으로 변경할 수 없다는 이야기이다.


그러나 HOC의 경우 어떤 컴포넌트를 통해 HOC를 생성하느냐에 따라(!) 본인이 받아야 하는 매개변수(props)의 형태를 동적으로 받을 수 있다. 감싸고 있는 컴포넌트의 타입이 곧 HOC를 통해 반환한 컴포넌트의 타입이 되기 때문이다. 이러한 측면에서 한 기능을 하는 컨테이너 컴포넌트를 다른 props를 받을 수 있게 '재정의'했다고 말할 수 있다는 뜻인 것이다!!!

 

 

 

글 참 어렵다 어려워...

 

 

 

참고자료

 

파라미터화에 대한 개념이 정리되어있는 고려대 어딘가의 자료였는데, 지금은 링크가 깨진 모양인지 열리지 않는다 ㅠㅠ

http://kucg.korea.ac.kr/research/MeshandTextureProcessing/PointBased/Parameterization/index.shtml