stack-analysis
CHAPTER 9 / 90
읽기 약 2분
FUNCTION
컴포넌트 패턴: Compound + Render Props
핵심 개념
Compound Component·Render Props·HOC·Hooks 비교 — 드롭다운·모달·탭.
본문
Compound Component
// 부모 + 자식이 함께 동작하는 컴포넌트 묶음
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext<{
active: string;
setActive: (v: string) => void;
} | null>(null);
function Tabs({ children, defaultValue }: { children: React.ReactNode; defaultValue: string }) {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ value, children }: { value: string; children: React.ReactNode }) {
const ctx = useContext(TabsContext)!;
return (
<button
className={ctx.active === value ? 'active' : ''}
onClick={() => ctx.setActive(value)}
>
{children}
</button>
);
}
function TabPanel({ value, children }: { value: string; children: React.ReactNode }) {
const ctx = useContext(TabsContext)!;
if (ctx.active !== value) return null;
return <div className="tab-panel">{children}</div>;
}
// 묶기
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panel = TabPanel;
export { Tabs };
// 사용 — 직관적·유연
<Tabs defaultValue="profile">
<Tabs.List>
<Tabs.Tab value="profile">프로필</Tabs.Tab>
<Tabs.Tab value="settings">설정</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="profile">프로필 내용</Tabs.Panel>
<Tabs.Panel value="settings">설정 내용</Tabs.Panel>
</Tabs>Render Props (이제는 Hooks로 대체)
// 옛날 패턴 — 함수를 자식으로
function MouseTracker({ children }: {
children: (pos: { x: number; y: number }) => React.ReactNode
}) {
const [pos, setPos] = useState({ x: 0, y: 0 });
// ...
return <div onMouseMove={...}>{children(pos)}</div>;
}
<MouseTracker>
{({ x, y }) => <p>{x}, {y}</p>}
</MouseTracker>
// 모던 — Custom Hook
function useMousePosition() {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const handler = (e: MouseEvent) => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener('mousemove', handler);
return () => window.removeEventListener('mousemove', handler);
}, []);
return pos;
}
function App() {
const { x, y } = useMousePosition();
return <p>{x}, {y}</p>;
}HOC (Higher-Order Component)
// 옛날 패턴 — 컴포넌트를 감싸는 함수
function withAuth<P>(Component: React.ComponentType<P>) {
return function AuthWrapped(props: P & { user?: User }) {
const user = useAuthUser();
if (!user) return <div>로그인 필요</div>;
return <Component {...props} user={user} />;
};
}
const ProtectedPage = withAuth(MyPage);
// 모던 — Custom Hook
function useRequireAuth() {
const user = useAuthUser();
const router = useRouter();
useEffect(() => {
if (!user) router.push('/login');
}, [user]);
return user;
}
function MyPage() {
const user = useRequireAuth();
if (!user) return null;
return <div>Welcome, {user.name}</div>;
}모달 — Compound Pattern
function Modal({ isOpen, onClose, children }: {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}) {
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>
);
}
Modal.Header = ({ children }: { children: React.ReactNode }) => (
<div className="modal-header">{children}</div>
);
Modal.Body = ({ children }: { children: React.ReactNode }) => (
<div className="modal-body">{children}</div>
);
Modal.Footer = ({ children }: { children: React.ReactNode }) => (
<div className="modal-footer">{children}</div>
);
// 사용
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
<Modal.Header>알림</Modal.Header>
<Modal.Body>정말 삭제하시겠습니까?</Modal.Body>
<Modal.Footer>
<button onClick={() => setIsOpen(false)}>취소</button>
<button onClick={handleDelete}>확인</button>
</Modal.Footer>
</Modal>다음 챕터
CH.10 "스타일링 비교" — Tailwind vs CSS Modules vs styled.
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 컴포넌트 패턴 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
컴포넌트 패턴 관련 인기 라이브러리/패턴 5개를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 컴포넌트 패턴 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 프론트엔드 시장의 컴포넌트 패턴 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
컴포넌트 패턴: Compound + Render Props는 이 3가지만 확실히 잡으세요
1.Compound Component는 직관적 + 유연 — 부모/자식 묶음으로 동작
2.모던 React는 Render Props/HOC 대신 Custom Hook 우선
3.다음 챕터 CH.10에서 스타일링 — 3가지 접근법 비교
공유하기
진행도 9 / 90