React

Composition

컴포넌트를 조합하여 복잡한 UI를 구성하는 방법과 원칙을 설명합니다.

as Prop

as prop은 React 컴포넌트가 렌더링하는 기본 HTML 요소를 변경하는 데 사용됩니다. 컴포넌트의 기능은 유지하면서 기본 요소를 변경할 수 있는 간단한 방법을 제공합니다.

<Text as="p">단락 텍스트</Text>
<Text as="span">인라인 텍스트</Text>
<Text as="h3">제목 텍스트</Text>

asChild Prop

asChild prop은 컴포넌트의 기능을 자식 요소에 합성하는 데 사용됩니다. 이를 활용해 HTML 태그를 변경하거나, 다른 컴포넌트에 기능을 주입할 수 있습니다. Radix UI에서 제공하는 asChild 패턴과 동일한 기능을 제공합니다.

요소 타입 변경하기

// ActionButton은 기본적으로 button 태그를 사용하지만, asChild를 사용해 링크로 변경할 수 있습니다.
<ActionButton asChild>
  <a href="/create">글쓰기</a>
</ActionButton>

기본 요소 타입을 변경하는 경우, 해당 요소가 접근성을 유지하고 올바르게 작동하는지 확인하는 것은 개발자의 책임입니다. 예를 들어, 버튼을 div로 변경하면 더 이상 키보드로 접근할 수 없게 될 수 있습니다.

React 컴포넌트와 조합하기

// 라우터 라이브러리에서 제공하는 Link 컴포넌트에 기능을 합성할 수 있습니다.
<Button asChild>
  <Link to="/create">
    <Icon svg={<IconPlusLine />} />
  </Link>
</Button>

asChild를 사용하여 컴포넌트를 조합할 때는 세 가지 원칙을 따라야 합니다.

1. 컴포넌트는 props를 전개해야 합니다

asChild는 기능을 제공하고 접근성을 유지하기 위한 고유의 props와 이벤트 핸들러를 전달합니다. 컴포넌트가 이러한 props를 지원하지 않으면 제대로 작동하지 않습니다.

// 잘못된 방법
const MyButton = () => <button />;

// 올바른 방법
const MyButton = (props) => <button {...props} />;

asChild의 자식 컴포넌트에 대해서는 항상 모든 props를 전개(spread)하는 것이 좋은 방법입니다.

2. 컴포넌트는 ref를 전달해야 합니다

컴포넌트에 ref를 전달해야 할 수 있습니다(예: 요소 크기 측정). 컴포넌트가 ref를 받지 않으면 제대로 작동하지 않을 수 있습니다.

// 잘못된 방법
const MyButton = (props) => <button {...props} />;

// 올바른 방법
const MyButton = React.forwardRef((props, forwardedRef) => (
  <button {...props} ref={forwardedRef} />
));

모든 경우에 필수는 아니지만, 항상 React.forwardRef를 사용하는 것이 가장 안전합니다.

3. 컴포넌트는 받은 children을 렌더해야 합니다

asChild는 컴포넌트의 바깥 요소만 교체합니다. 일부 컴포넌트는 그 안에 쓴 children을 SEED가 주입하는 내부 구조로 감싸서 자식 컴포넌트에 전달합니다. 예를 들어 Content Scale을 사용하는 컴포넌트는 children을 ContentScale 박스로 감쌉니다.

<Menu.Item asChild>
  <a href="/settings">설정</a>
</Menu.Item>

// 렌더 결과
<a href="/settings" class="seed-menu-item__root …">
  <span class="seed-content-scale">설정</span>
</a>

내부 구조가 children으로 전달되므로, 자식 컴포넌트는 받은 children을 그대로 렌더해야 합니다. children 없이 props로 내용을 직접 그리는 컴포넌트를 넘기면 화면은 그려지지만 내부 구조가 빠집니다. 이 경우 children을 렌더하도록 바꾸거나, 컴포넌트 안에 ContentScale을 직접 두세요.

// 잘못된 방법: 받은 children을 버리고 props로 내용을 그립니다
const MyRow = React.forwardRef(({ title, children, ...props }, ref) => (
  <a {...props} ref={ref}>
    {title}
  </a>
));

// 올바른 방법
const MyRow = React.forwardRef((props, ref) => <a {...props} ref={ref} />);

Last updated on

목차