프론트엔드에서 사용하기 좋은 Design Pattern - Container Presenter Pattern

Kychann·2025년 4월 29일
post-thumbnail

📦 Container-Presenter Pattern

Smart-Dumb Compnent Pattern으로 불리기도 한다.

개념

  • Container: 데이터 관리, 로직 담당 (API 호출, 상태 관리)
    - “데이터와 로직”을 담당한다.
    - 외부 API 호출, 상태 관리(useState, useEffect), 비즈니스 로직, Redux 연결 같은 걸 여기서 처리.
  • Presenter: UI만 담당 (props로 데이터 받아서 단순 렌더링)
    - “순수 UI”만 담당한다.
    • props를 받아서 어떻게 화면에 보일지만 신경 쓴다.
    • 상태나 로직을 모른다.

Container는 “배달 기사” (데이터를 들고 와서 전달해줌)
Presenter는 “요리사” (받은 재료로 요리만 함, 재료 수급은 신경 안 씀)


왜 사용할까?

  1. 관심사 분리 (Separation of Concerns)
  • Logic/State/Rendering을 깔끔하게 구분
  • 한 컴포넌트가 너무 복잡해지는 것을 막아줌
  1. 재사용성 증가
  • Presenter는 어디서든 쓸 수 있음 (다른 데이터에 대해서도)
  1. 테스트 편리
  • Presenter는 순수 함수형 컴포넌트라 UI 테스트만 하면 됨
  • Container는 비즈니스 로직 테스트만 집중하면 됨
  1. 가독성 향상
  • 이 컴포넌트는 뭐하는지 명확하게 알 수 있음

예시

// Container: 상태 관리 + 데이터 로딩
import { useState, useEffect } from "react";
import UserCard from "./UserCard";

function UserContainer({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);

  if (!user) return <div>Loading...</div>;

  return <UserCard user={user} />;
}

export default UserContainer;
// Presenter: 오직 화면만
function UserCard({ user }) {
  return (
    <div className="card">
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

export default UserCard;

구조 그림으로 살펴보자

[Container 컴포넌트]
 └─ useState, useEffect
 └─ API 요청
 └─ 상태 관리
 └─ 데이터 가공
 └─ 렌더링: <Presenter {...props} />

[Presenter 컴포넌트]
 └─ props만 받아서
 └─ 화면 그리기 (return JSX)
 └─ 내부 상태 없음 (또는 최소)

주의할 점

  • Container가 비대해지지 않게 조심
    - fetch, 상태관리만 하고 로직이 너무 많으면 또 복잡해짐 -> Custom Hook으로 분리하면 좋음
  • Presenter가 로직을 가져가지 않도록 주의
    - Presenter는 화면만 그려야 한다. 로직을 가져가기 시작하면 이 패턴이 깨진다.
profile
어,, 저 아닌데요?

0개의 댓글