본문 바로가기
▼ 코딩 공부하기/▼▼ 프론트엔드

[React] 아직도 컴포넌트 하나에 다 넣으세요? (Presentational & Container 패턴)

by mdeeno 2026. 1. 24.
반응형

React로 개발하다 보면 컴포넌트 하나에 데이터를 가져오는 로직(Fetch)화면을 그리는 코드(UI)가 뒤섞여 300~500줄이 넘어가는 경험, 다들 있으시죠?

이렇게 코드가 섞이면 수정하기도 어렵고, 다른 곳에서 재사용하기도 힘듭니다. 이 문제를 해결하기 위한 가장 클래식하고 강력한 해결책, 바로 Presentational & Container 패턴입니다. 오늘은 이 패턴을 통해 '똑똑한 녀석''보여주는 녀석'을 나누는 방법을 7살 눈높이 설명과 함께 알아봅니다.


목차

  1. Container 컴포넌트란? (똑똑한 녀석)
  2. Presentational 컴포넌트란? (보여주는 녀석)
  3. 7살도 이해하는 설명: 쉐프와 웨이터
  4. 실무 코드 예시 (Before & After)

#  0. 패턴의 핵심 개념 (The Concept)

이 패턴은 컴포넌트를 두 가지 역할로 철저하게 분리합니다.

 

1. Container 컴포넌트 (Logic)

"어떻게 동작하는가(How things work)"를 담당하는 컴포넌트입니다.
데이터를 가져오고, 상태(State)를 관리하며, 비즈니스 로직을 처리하는 '매니저' 역할을 합니다.

  • API 호출 (Fetch, Axios 등)을 담당합니다.
  • 데이터와 함수를 props로 Presentational 컴포넌트에게 넘겨줍니다.
  • 보통 div 같은 HTML 태그나 스타일(CSS)을 직접 갖지 않습니다.

2. Presentational 컴포넌트 (UI)

"어떻게 보여지는가(How things look)"를 담당하는 컴포넌트입니다.
복잡한 로직 없이 오직 props로 받은 데이터를 화면에 예쁘게 그려주는 '디자이너' 역할을 합니다.

  • State(상태)를 거의 갖지 않습니다. (가져도 UI용 state만 가짐)
  • API가 뭔지, 데이터가 어디서 왔는지 전혀 모릅니다.
  • HTML, CSS, 스타일링에 집중합니다.
  • 재사용성이 매우 높습니다.


3. 7살도 이해하는 설명: "쉐프와 웨이터"

👶 7살 버전 설명

"레스토랑에는 주방장(Container)웨이터(Presentational)가 있어.

1. 주방장(Container): 재료를 어디서 사올지, 어떻게 요리할지, 맛은 어떤지 다 알아서 결정해. 하지만 손님 앞에 직접 나가지는 않아.
2. 웨이터(Presentational): 요리법은 전혀 몰라. 주방장이 접시를 주면, 그걸 손님에게 예쁘게 서빙만 해.

만약 웨이터가 요리도 하고 서빙도 하면 너무 바빠서 실수하겠지? 그래서 일을 나누는 거야!"


4. 실무 코드 예시 (Separation)

하나로 뭉쳐있던 코드를 두 개로 분리하는 과정을 보세요. 훨씬 깔끔해집니다.

1) Presentational (보여주는 녀석)

오직 props를 받아 그리는 일만 합니다. 재사용하기 쉽습니다.

// UserList.jsx (UI만 담당)
const UserList = ({ users, isLoading }) => {
  if (isLoading) return <div>로딩 중...</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          <b>{user.name}</b> ({user.email})
        </li>
      ))}
    </ul>
  );
};
export default UserList;

2) Container (똑똑한 녀석)

데이터를 가져와서 UserList에게 넘겨주기만 합니다.

// UserListContainer.jsx (로직만 담당)
import { useState, useEffect } from 'react';
import UserList from './UserList';

const UserListContainer = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 데이터 Fetch 로직
    fetch('https://api.example.com/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  // UI 컴포넌트에 데이터(props) 주입
  return <UserList users={users} isLoading={loading} />;
};
export default UserListContainer;


🚀 요즘 트렌드는? (Hook의 등장)

최근에는 React Hooks (Custom Hooks)가 등장하면서 Container 컴포넌트의 역할을 useUserList() 같은 훅이 대체하기도 합니다.

하지만 "로직과 UI를 분리한다"는 이 패턴의 철학은 여전히 유효하며, 깨끗한 코드를 작성하는 제1원칙입니다.

반응형