FEInterview Prep

react · high priority

React 컴포넌트 패턴 — Compound · Headless · Hook 합성

재사용성의 단위는 *컴포넌트* 가 아니라 *행위* — 패턴 별 트레이드오프와 선택 기준

advanced 난이도4시간토스카카오네이버배민당근라인
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

복잡한 개념을 실생활 비유로 설명합니다.

레고 시스템

*Custom Hook* 은 *레고 안의 작은 메커니즘* — 톱니, 스프링, 모터. 혼자서는 모양이 없지만 다른 부품과 합치면 다양한 동작을 만듭니다. *Headless* 는 *모터 + 회로 묶음* — 차종이 무엇이든 동력은 같습니다. *Compound* 는 *레고 세트* — 자동차의 차체, 바퀴, 문이 한 박스에 들어 있고 사용자가 *순서와 위치* 만 자유롭게 조립. *Slot/asChild* 는 *모듈러 어댑터* — 같은 회로를 USB 도, 라이트닝도 결합 가능하게 해주는 어댑터입니다.

핵심 개념

Compound Components

부모가 *암묵적 상태* 를 들고, 자식들은 그 상태를 Context 로 구독해 공동 작업. <Tabs> 안에 <Tabs.List> <Tabs.Trigger> <Tabs.Content> 같은 구성. *prop 폭발* 의 표준 해독제.

Headless Components

*로직만* 제공하고 UI 는 사용자에게 맡김. 접근성·키보드·포커스·ARIA 등 어려운 부분은 라이브러리가, 마크업·스타일은 앱 코드가. Radix UI Primitives, Headless UI, React Aria 가 대표.

Custom Hooks

재사용 단위가 *상태 + 효과* 일 때, 컴포넌트가 아니라 훅으로 묶음. useToggle, useDisclosure, useDebouncedValue 등. UI 에 묶이지 않아 *가장 가벼운 재사용 단위*.

Slot / asChild 합성

컴포넌트가 *기본 트리 구조* 만 정하고, asChild 또는 slots 로 *내부 마크업/태그* 를 교체. Radix 의 <Slot> 이 대표적 — <Button asChild><a href> 처럼 사용.

재사용 단위 vs 결합도
패턴재사용 단위스타일 결합내부 상태 노출대표 라이브러리
CompoundUI + 로직 묶음강 (스타일까지 같이 옴)Context 로 암묵shadcn/ui (래퍼)
Headless로직만약 (마크업·스타일은 사용자)훅/렌더 함수로 명시Radix Primitives, Headless UI, React Aria
Custom Hooks상태/효과없음리턴 값use-* 라이브러리들
Slot/asChild트리 구조약~중주입된 child 로 위임Radix Slot, Vue slot

render props · HOC 는 어디로 갔나

*HOC* (withRouter 등) 는 hook 등장과 함께 거의 은퇴했고, *render props* 도 일반 UI 에서는 hook 으로 대체되었습니다. 그러나 *지속적 렌더 트리* 가 필요한 곳 — Downshift, React Spring, Formik 같은 라이브러리 — 에서는 render props 가 여전히 유효합니다. 면접에서 "왜 render props 가 줄었나" 는 결국 hook 의 등장으로 설명하면 됩니다.

실무 적용

어떤 상황에서 사용하는가

디자인 시스템 팀이 새 Modal/Toast/Combobox 컴포넌트를 만들어야 한다. 팀에는 시니어/주니어가 섞여 있고, 다양한 페이지에서 다른 디자인 변형이 필요.

어떻게 적용하는가

(1) 어려운 부분(포커스 트랩·Esc 처리·ARIA·keyboard nav)은 *Radix Primitives* 를 헤드리스 layer 로 채택. (2) 그 위에 회사 wrapper 를 *Compound* 형태로 (`<Modal.Root> <Modal.Header> <Modal.Body> <Modal.Actions>`) 노출. (3) `<Button asChild>` 로 Link/외부 라우터 결합. (4) 폼 제어 같은 로직은 *Custom Hook* 으로 (`useDisclosure`, `useFormState`). (5) wrapper 코드는 shadcn/ui 처럼 *복사 가능한 형태로 레포에 둔다* — 팀이 디자인 토큰을 직접 수정 가능. (6) Storybook 으로 변형을 시각화, 시각 회귀 테스트(Chromatic) 로 회귀 방지.

흔한 실수와 안티패턴

  • "모든 변형을 props 로" — 결국 30개 prop, 변형 추가가 두려워짐.
  • "무조건 Headless" — 팀이 작아 ARIA/키보드 직접 구현하느라 시간만 낭비.
  • Compound 자식을 Root 밖에서 사용해도 에러가 안 나서 디버깅 어려움 — useContext null 검사 누락.
  • asChild 를 자식이 React.forwardRef 미지원 컴포넌트에 적용해 ref 전달 실패.
  • Custom Hook 추상화를 1회 사용에도 적용해 *읽기 비용* 만 늘림.

흔한 오해

오해

"Compound 와 Headless 는 같은 개념이다."

교정

다르다. Compound 는 *부모-자식 묶음* 의 합성 방식, Headless 는 *로직만 제공하는 layer* 다. Headless 가 Compound 형태의 API 를 가질 수도 있다.

왜 중요

Radix 는 Headless 인 동시에 Compound 형태의 API 를 가진다 — 둘은 직교 축이다.

오해

"render props 와 HOC 는 죽었다."

교정

일반 UI 에선 hook 으로 대체됐지만, *지속적 렌더 트리* 가 필요한 라이브러리(Downshift, Spring) 에선 render props 가 여전히 유효.

왜 중요

hook 은 호출 위치 제약(Rules of Hooks) 이 있어 *조건부 트리 렌더* 가 어렵다.

오해

"Slot/asChild 는 가독성을 해치니 피해야 한다."

교정

오히려 *prop 폭발* 을 막아 가독성을 살린다. Radix·shadcn 의 표준이 된 이유.

왜 중요

`<Button as="a" href>` 식 polymorphic prop 보다 *자식 태그를 그대로 두는 asChild* 가 타입과 의미를 더 잘 보존한다.

오해

"shadcn/ui 도 라이브러리니까 그냥 npm 으로 받아 쓰면 된다."

교정

shadcn/ui 는 *복사 가능한 코드 모음* 이지 npm 패키지가 아니다 — 코드를 *내 레포로 복사* 해 자유롭게 수정하는 게 의도된 사용법.

왜 중요

디자인 시스템 wrapper 는 *변경되는 자산* 이라, 라이브러리 lock-in 보다 코드 복사가 합리적.

면접 질문

중급토스배민당근

답변 방향 힌트

"부모 상태 + 자식 구독" 구조.

반드시 언급할 키워드

  • 부모 컴포넌트가 *암묵 상태* 보유 (`activeValue`)
  • Context Provider 로 자식이 구독
  • 자식들은 마크업이 자유롭게 조합 가능
  • Trigger/Content 가 같은 value 로 매칭
  • `useTabs` 훅이 Root 밖 사용을 throw

예상 꼬리 질문

  • Trigger 컴포넌트의 ARIA 속성이 무엇이 되어야 하는지, 키보드 내비는 어떻게 처리하시겠습니까?
  • Compound 의 단점 한 가지와 그 완화책을 답해주세요.

자기 점검

Compound Components 와 Headless Components 의 *축* 이 어떻게 다른가?

기대 키워드

합성 방식layer 성격직교 축Radix 는 둘 다

자주 하는 오해

둘이 같은 개념이라고 생각하기 쉽지만, Compound 는 *합성 방식*, Headless 는 *layer 의 성격* 으로 직교한다.

asChild 패턴이 polymorphic `as` prop 보다 *낫다* 는 주장의 근거 두 가지를 답하라.

기대 키워드

타입 보존prop 폭발 회피Slot 합성자식 태그 그대로

자주 하는 오해

"as 도 똑같은 거 아닌가" — `as` 는 generic type explosion 과 구문상 가독성 문제가 크다.

한 번만 쓸 로직을 *Custom Hook 으로 추출* 하는 것의 단점은?

기대 키워드

추상화 비용읽기 비용추적 어려움3회 이상 반복부터

자주 하는 오해

"hook 추출은 항상 좋다" — 1회용 hook 은 오히려 가독성을 해친다.

학습 자료