리엑트 애플리케이션을 제작할 때 아주 유용한 간단한 디자인 패턴이 있다. 오랫동안 리엑트를 사용해 본 사람이라면, 이미 알고 있을 것이다. 해당 포스팅에서 아주 잘 설명하고 있긴 하지만 몇가지 좀 더 설명하고자 한다.
컴포넌트를 두개의 카테고리로 나눈다면 컴포넌트를 재사용하고 이해하는 데에 아주 쉬워진다는 사실을 알 것이다. 필자는 이를 Container-Presentational이라고 부르는데 Fat-Skinny, Smart-Dumb, Stateful-Pure, Screens-Components라고도 불리기도 한다. 정확히 같은 개념은 아니지만 비슷한 개념을 공유하는 말이다.
필자가 정의하는 presentational 컴포넌트란:
- 어떻게 보일지를 관여한다.
- presentational과 container 컴포넌트를 내부에 가지며 보통은 DOM 마크업과 스타일을 가진다.
- this.props.children를 통해 내부를 봉쇄하기도 한다.
- Flux actions나 stores와 같이 어플의 다른 부분에 의존성이 없다.
- 데이터가 어떻게 로딩되고 변형되는지 관여하지 않는다.
- props를 통해 데이터와 콜백을 독자적으로 받게 된다.
- 자신의 state를 드물게 가진다.(만약 state를 가진다면 데이터 때문이 아닌 UI state 때문이다.)
- state, lifecycle hooks나 성능최적화가 필요한 것이 아니라면 함수형 컴포넌트로 작성된다.
- 예: page, sidebar, story, userinfo, list
필자가 정의하는 container 컴포넌트란:
- 어떻게 동작하는지를 관여한다.
- presentational과 container 컴포넌트를 내부에 가지며 div 래핑을 위한 게 아니라면 DOM 마크업을 가지지 않는다. 래핑을 할 때도 스타일을 가져선 안된다.
- presentaional 혹은 다른 container 컴포넌트에 데이터와 행위를 제공한다.
- Flux action을 호출하고 콜백을 presentaional 컴포넌트에 전달한다.
- stateful하며 데이터 소스를 저장한다.
- 손으로 직접 작성하기보다는 보통은 상위 컴포넌트, 예를 들어 React Redux의 connect(), Relay의 createContainer(), 혹은 Flux Utils의 Container.create()를 통해 생성한다.
- 예: userpage, followersidebar, storycontainer, followeduserlist
필자는 각 컴포넌트 카테고리를 다른 폴더에 넣어 차이를 명확히 볼 수 있도록 한다.
이러한 접근법의 장점
관심사의 분리. 이런 방식으로 컴포넌트를 작성함으로써 어플과 UI를 더 잘 이해할 수 있다.
재사용성. 동일한 presentational 컴포넌트를 다른 state 소스를 이용해 재사용 가능하다. 그래서 별도의 container 컴포넌트가 추후에 재사용 가능할 수 있도록 한다.
presentational 컴포넌트는 애플리케이션의 팔레트 역할을 한다. presentational 컴포넌트를 하나의 페이지에 놓고 디자이너가 어플의 로직을 건들이지 않고 갖가지 변수들을 뒤틀 수 있도록 한다.
이는 Sidebar, Page, ContextMenu와 같은 레이아웃 컴포넌트를 추출하고 동일한 마크업과 레이아웃을 여러 container 컴포넌트에 반복하는 것이 아닌 this.props.children을 이용할 수 있도록 한다.
명심하라, 컴포넌트는 DOM을 반환할 필요가 없다. UI에 따른 구성 바운더리만 제공해도 된다.
이점을 최대한 활용하자.
그렇다면 언제 container 컴포넌트를 작성해야할까?
우선은 presentational 컴포넌트만으로 어플제작을 시작하길 권한다. 결국에는 중간 컴포넌트에 너무 많은 props를 전달한다는 사실을 깨닫게 될 것이다. 어떤 컴포넌트는 props를 사용하지 않고 그저 전달만 할 뿐이고 자식 컴포넌트가 데이터를 필요로 할 때 모든 중간 컴포넌트에 props 전달을 작성해야 할 때, 이는 container 컴포넌트를 작성할 좋은 시기이다. 이런 방식으로 나무의 줄기 부분 컴포넌트에 부담을 주지 않고도 데이터와 행위 props를 잎사귀(마지막) 컴포넌트까지 전달할 수 있다.
이는 지속적으로 리펙토링을 해야하는 부분이므로 처음부터 완벽하게 하려고 하지 않아도 된다. 이 패턴에 익숙해지면, 언제 container를 추출해야 할지를 알게 될 것이다. 마치 함수를 추출할 때를 아는 것과 마찬가지다.
이외의 이분법
presentational 컴포넌트와 container가 기술적인 분류가 아님을 이해하는 것은 중요하다. 이는 오히려 목적에 따른 분류이다.
반대로 아래 분류는 관련된 기술적 분류이다(같은 개념은 아니다!):
- Stateful and Stateless: 어떤 컴포넌트는 React setState() 메서드를 사용하고 어떤 컴포넌트는 사용하지 않는다. Container 컴포넌트는 stateful하지만 presentational 컴포넌트는 stateless 하려는 경향이 있다. 하지만 그렇게 강제적인 규칙은 아니다. Presentational 컴포넌트는 stateful 하고 container는 stateless 할 수도 있다.
- Classes and Functions: React 0.14 이후, 컴포넌트는 클래스와 함수 둘 다 선언이 가능해 졌다. 함수형 컴포넌트는 정의하기가 쉽지만 클래스 컴포넌트에 비하면 몇몇 기능이 부족한 상태다. 추후에는 그런 부족한 부분이 없어지겠지만 현재로서는 그렇다. 함수형 컴포넌트는 이해하기가 쉽기 때문에 글을 쓰는 현재 클래스 컴포넌트에서만 사용 가능한 state, lifecycle hook, 혹은 성능 최적화를 위한 것이 아니라면 함수형 컴포넌트 사용을 추천한다.
- Pure and Impure: 어떤 사람들은 같은 props와 state를 주었을 때 항상 같은 결과를 반환하는 컴포넌트를 순수 컴포넌트라고 부른다. 순수 컴포넌트는 클래스나 함수형 모두 정의될 수 있고 stateful 하거나 stateless할 수 있다. 순수 컴포넌트의 또 다른 중요한 특징은 props나 state의 깊은 변형에 의존하지 않는다는 것이다. 따라서 렌더링 성능이 shouldComponentUpdate() 훅의 얕은 비교로 최적화될 수 있다. 현재로서는 클래스만이 shouldComponentUpdate()를 사용할 수 있지만 미래에는 바뀔지도 모른다.
Presentational 컴포넌트나 container는 위 카테고리에 모두 해당될 수 있다. 필자의 경험상, presentational 컴포넌트는 stateless하고 순수 함수인 경향이 있고 container의 경우 stateful 하고 순수 클래스인 경향이 있다. 하지만 이는 규칙이 아닌 그런 경우가 많다는 것이지 상황에 따라 완전히 정반대의 케이스도 본 적이 있다.
Presentational이나 container 분리를 마치 신념처럼 받아들이지 말라. 어떨 때는 상관이 없을 때도 있고 어떨 때는 분리하기가 어려운 경우도 있다. 만약 어떤 컴포넌트가 presentational인지 container인지 명확히 확신이 들지 않는다면 지금 결정하기 너무 이른 경우일 수 있다. 진땀 빼지 말자!
출처: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
Presentational and Container Components
You’ll find your components much easier to reuse and reason about if you divide them into two categories.
medium.com
'Random' 카테고리의 다른 글
프론트엔드에서 페이지 로딩 속도 문제 해결하기 (2) | 2022.09.19 |
---|---|
DP PX 변환 (0) | 2022.08.21 |
Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'.ts(2345) (0) | 2022.05.14 |
DB에 쿼리하기 VS 클라이언트에서 데이터 분류하기 (0) | 2022.05.04 |
CSS와 JS 애니메이션 성능향상 팁 (0) | 2022.04.25 |