Container and Presentation 패턴

컴포넌트에서 로직과 프리젠테이션을 분리하여 조금 더 관리하기 쉽고 재사용하기 용이하게 만드는 패턴이다.

컨테이너란?

컨테이너란 컴포넌트의 로직에 관한 대부분의 것들을 다루며 특히 API 호출, 데이터 조작, 이벤트 처리 작업 등을 한다.

프리젠테이션이란?

여기서 말하는 프리젠테이션이란 UI에 표시할 요소를 만드는 부분이라고 보면된다(render). 달리 말해 UI가 정의되는 곳이며, 컨테이너에서 props 형태로 데이터를 받는다.

예제)

function Todo() {
  const [data, setData] = useState();
  
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(res => res.json())
      .then(json => setData(json))
  }, []);

  return <h1>{data?.title}</h1>
}

컨테이너 & 프리젠테이션 패턴

이 패턴을 적용하면, 컴포넌트는 두 개의 명확한 책임이 있는 전보다 작은 컴포넌트로 분리된다. 컨테이너컴포넌트는 로직에 대해 알고 있으며 위에서 언급한 API 호출, 데이터 조작, 이벤트 처리 등을 담당하고, 프리젠테이션 컴포넌트는 UI를 정의하며 컨테이터로 부터 props로 데이터를 받기 때문에 내부에는 로직이 없고 상태가 없는(stateless, 물론 최소한의 컴포넌트 동작을 위한 상태는 존재할 수도 있다) 컴포넌트가 된다.

이 패턴 적용하기

기존에 컨테이너와 프리젠테이션 컴포넌트가 같이 있었다면 이를 분리하며 컨테이너 & 프리젠테이션 패턴을 적용할 수 있다.

먼저 로직을 다루는 부분을 컨테이너로 분리하며, 해당 컴포넌트의 이름을 기존 컴포넌트 이름 뒤에 Container를 추가하면 된다. 이는 React 커뮤니티에서 사용되는 보편적인 방법이다.

그리고 render를 담당하고 있는 부분은 아래와 같이 구현을 변경했고 UI 부분을 제거했다.

function TodoContainer() {
  const [data, setData] = useState();
  
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(res => res.json())
      .then(json => setData(json))
  }, []);
  
  return <Todo title={title} />
}

Todo.jsx는 이미 만들어 놓은 UI만 반환하는 순수 함수가 되었으며, 동일한 데이터 구조를 표현하는 곳이라면 어디서든 재사용 가능한 컴포넌트가 되었다. 더미 데이터를 통해 화면을 먼저 예상하면서 임시 화면을 구성해 볼 수도 있고, 프리젠테이션 영역을 관리하는 사람과 컨테이너 영역을 관리하는 사람이 일을 병렬적으로 동시에 할 수도 있게 된다.

물론 개발 속도와 프로젝트의 유지 관리 가능성에 있어 이점이 있을 수 있지만, 아무런 이유 없이 이 패턴을 적용한다면 더 많은 파일과 컴포넌트를 만들게 되면서 버그가 발생할 수도 있고 코드베이스가 복잡해질 수도 있다. 위의 예는 극단적으로 간단하여 간단해 보일지라도 현실에서 마주하는 문제는 이보다 훨씬 복잡하고 거대하기 때문이다.

state 대신 props를 사용하면, 복잡한 논리를 무시하고 props를 전달할 수 있으며, 컴포넌트를 단순하게 만들 수 있다.

Last updated