Container & Presentational 패턴: React에서의 관심사의 분리

발행: (2026년 2월 4일 오후 02:23 GMT+9)
7 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please provide the content (or paste the article) that should be converted into Korean? Once I have the text, I’ll keep the source link at the top unchanged and translate the rest while preserving all formatting, markdown, and technical terms.

소개

React를 오래 쓰다 보면 “God Component”(갓 컴포넌트)를 만든 적이 있을 겁니다. 이 컴포넌트는 데이터를 가져오고, 수십 개의 상태 변수를 관리하며, 검증을 처리하고, 관련 없는 계산을 수행하고, UI까지 렌더링합니다. 그 결과는 얽히고 설킨, 유지보수가 어려운 컴포넌트가 됩니다.

여기에 Container / Presentational pattern(스마트 vs. 덤 컴포넌트라고도 함)을 도입합니다. 이 패턴은 UI가 어떻게 보이는지에 대한 what과 데이터가 어떻게 획득·관리되는지에 대한 how을 분리합니다.

패턴 설명

고급 레스토랑을 생각해 보세요:

  • Container (Chef / Manager) – 주방에서 일하고, 공급업체(APIs)와 협력하며, 재고(state)를 관리하고 비즈니스 로직을 적용합니다. 플레이팅에는 신경 쓰지 않습니다.
  • Presentational (Waiter / Plater) – 준비된 요리를 받아 아름답게 배열하고 고객에게 제공합니다. 요리가 어떻게 조리됐는지는 모릅니다.
+---------------------------+
|   CONTAINER COMPONENT     |
|   (Logic / Data)          |
+---------------------------+
| 1. Fetches data (API)     |
| 2. Manages state          |
| 3. Defines handlers       |
+-----------+---------------+
            |
   (Passes data via props)
            |
            v
+---------------------------+
| PRESENTATIONAL COMPONENT  |
|   (UI / Rendering)        |
+---------------------------+
| 1. Receives props         |
| 2. Renders JSX / CSS     |
| 3. Looks fabulous        |
+---------------------------+

클래식 예제: 암호화폐 목록

프레젠테이션 컴포넌트 (“예쁜” 버전)

// CryptoList.jsx
import React from 'react';

const CryptoList = ({ coins, isLoading, onRefresh }) => {
  if (isLoading) {
    return Loading your digital gold...;
  }

  return (
    
      

🚀 달까지!

{coins.map((coin) => (
  
  **{coin.name}**: ${coin.price.toFixed(2)}
  
))}
<button onClick={fetchCoinData}>Refresh Prices</button>

Pure UI: props를 통해 데이터를 받아 렌더링합니다. 데이터가 어디서 오는지에 대한 지식이 없습니다.

Container Component (The “Smart” One)

// CryptoListContainer.jsx
import React, { useState, useEffect } from 'react';
import CryptoList from './CryptoList';

const CryptoListContainer = () => {
  const [coins, setCoins] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchCoinData = async () => {
    setLoading(true);
    // Simulating an API call
    setTimeout(() => {
      setCoins([
        { id: 1, name: 'Bitcoin', price: 45000 },
        { id: 2, name: 'Ethereum', price: 3200 },
        { id: 3, name: 'Doge', price: 0.12 },
      ]);
      setLoading(false);
    }, 1000);
  };

  useEffect(() => {
    fetchCoinData();
  }, []);

  // No JSX here – just passing data down
  return (
    
  );
};

export default CryptoListContainer;

Logic‑only: 데이터 가져오기, 상태 관리, 부수 효과를 처리한 뒤 결과를 프레젠테이션 컴포넌트에 전달합니다.

혜택

재사용성

동일한 CryptoList UI는 스타일링 로직을 건드리지 않고도 (예: “Favorites” vs. “All Coins”)와 같은 어떤 소스에서든 데이터를 표시할 수 있습니다.

관심사의 분리

  • 스타일링 버그 → 프레젠테이션 파일을 편집합니다.
  • 데이터 로딩 이슈 → 컨테이너 파일을 편집합니다.

혼합된 로직 수백 줄을 스크롤할 필요가 없습니다.

디자이너/개발자 조화

디자이너는 프레젠테이션 파일(HTML/CSS)에서 무한 렌더 루프 위험 없이 안전하게 작업할 수 있고, 개발자는 컨테이너에서 데이터 처리를 집중할 수 있습니다.

훅 트위스트

React Hooks가 도입된 이후, 엄격한 클래스 기반 “컨테이너” 패턴은 덜 일반적이게 되었습니다. 이제 컴포넌트를 함수형으로 유지하면서도 커스텀 훅을 추출하여 관심사를 분리할 수 있습니다.

// useCoins.js (custom hook)
import { useState, useEffect } from 'react';

export const useCoins = () => {
  const [coins, setCoins] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchCoinData = async () => {
    setLoading(true);
    // Simulated API call
    setTimeout(() => {
      setCoins([
        { id: 1, name: 'Bitcoin', price: 45000 },
        { id: 2, name: 'Ethereum', price: 3200 },
        { id: 3, name: 'Doge', price: 0.12 },
      ]);
      setLoading(false);
    }, 1000);
  };

  useEffect(() => {
    fetchCoinData();
  }, []);

  return { coins, loading, fetchCoinData };
};

컨테이너 컴포넌트는 이제 이 훅을 간단히 사용하고 그 결과를 프레젠테이셔널 컴포넌트에 전달하는 얇은 래퍼가 될 수 있습니다.

함정

과도한 설계

사소한 컴포넌트(예: 간단한 제출 버튼)를 별도의 컨테이너와 프레젠테이션 파일로 나누지 마세요. 컴포넌트가 몇 줄에 불과하다면, 추가적인 추상화가 잡음을 만들 뿐입니다.

Prop‑Drilling 지옥

같은 prop을 여러 레이어(예: user={user}를 10개 이상) 통해 전달하고 있다면, 컨테이너를 깊게 중첩하기보다 Context나 상태 관리 솔루션을 도입할 신호입니다.

결론

Container / Presentational 패턴은 dumb UIsmart logic을 장려합니다. 렌더링 관점을 데이터 관점과 분리함으로써 재사용성, 명확한 책임, 디자이너와 개발자 간의 원활한 협업을 얻을 수 있습니다. 가치가 있을 때 사용하고, 불필요한 분리를 피하며, prop‑drilling을 완화하기 위해 hooks 또는 Context를 고려하세요. 🚀

Back to Blog

관련 글

더 보기 »

React에서 `useState` 이해하기

useState가 해결하는 문제는 무엇인가? React 이전에 화면의 무언가를 업데이트하려면: - HTML 요소 찾기 - 수동으로 업데이트하기 - 확인하기 …