C.W.K.
Stream
Lesson 01 of 07 · published

컴포넌트는 함수, JSX 는 sugar

~14 min · components, jsx, fundamentals

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
딴 거 다 잊어도: React 컴포넌트 = element 반환하는 함수. JSX 는 그 element 를 nested 함수 호출 대신 HTML 모양 expression 으로 묘사하게 해주는 편의.

함수 in, element out

함수 컴포넌트는 인자 하나 — props object — 받고 React element (또는 fragment, null, element 배열) 반환. 계약은 그게 전부. 클래스 상속 없음, override 할 라이프사이클 메서드 없음, this 없음.

React 가 본인 컴포넌트가 지금 뭘 렌더할지 알아야 할 때마다 함수 호출. 출력은 UI 묘사이지 UI 자체가 아냐. React 의 reconciler 가 묘사들 비교해서 어떤 DOM mutation 할지 결정.

JSX 는 함수 호출로 desugar 됨

JSX 는 JavaScript 의 일부가 아냐. Vite + React 플러그인 (안에서 Babel 또는 SWC) 이 JSX 를 react/jsx-runtimejsx()/jsxs() 호출로 transform. 그 호출 직접 쓸 일 거의 없지만, 존재한다는 걸 알면 그것들을 언급하는 에러 메시지가 덜 무서워.

Fragment 가 뭐

<>...</> (빈 태그 단축) 가 fragment. DOM wrapper 추가 없이 자식 묶음. 컴포넌트가 형제 element 여러 개 반환해야 하고 부모 <div> 가 레이아웃 오염시킬 때 사용.

조건 패턴

idiom 세 개가 조건 렌더링의 95% 커버:

  • {condition && <Foo />} — condition truthy 일 때만 Foo 렌더.
  • {condition ? <A /> : <B />} — 하나 고름.
  • {items.map(item => <Row key={item.id} {...item} />)} — 리스트 렌더.

리스트엔 key 필요. key 가 React reconciler 한테 '이 element 가 그 이전 element 에 대응' 이라고 알려서 remount 대신 move/update.

리스트가 static 이 아니면 array index 를 key 로 쓰지 마. 아이템이 재정렬되거나 삭제되거나 중간에 insert 되면 index key 가 React 한테 잘못된 DOM node 를 잘못된 아이템과 짝지어 — 한 row 의 input state 가 다른 row 로 새는 거 보게 돼. 데이터에서 안정적 id 써.

0/false 렌더링 함정

count === 0 일 때 {count && <Badge />} 는 DOM 에 literal 숫자 0 을 렌더해 (이유: 0 이 falsy → short-circuit 가 0 반환 → React 가 숫자를 텍스트로 렌더). 대신 {count > 0 && <Badge />} 또는 {Boolean(count) && <Badge />}.

Code

같은 컴포넌트의 JSX 형태와 desugar 된 jsx() 호출 형태·tsx
// 본인이 쓰는 것
function Hello({ name }: { name: string }) {
  return (
    <div className="greeting">
      <h1>Hello, {name}</h1>
      <p>Welcome</p>
    </div>
  );
}

// Vite 플러그인이 emit 하는 것 (대략)
import { jsx, jsxs } from "react/jsx-runtime";

function Hello({ name }: { name: string }) {
  return jsxs("div", {
    className: "greeting",
    children: [
      jsx("h1", { children: ["Hello, ", name] }),
      jsx("p", { children: "Welcome" }),
    ],
  });
}
세 조건 패턴이 한 컴포넌트에·tsx
type Item = { id: string; label: string };

function List({ items, loading, error }: {
  items: Item[];
  loading: boolean;
  error: string | null;
}) {
  // Ternary: 두 뷰 중 선택
  if (loading) return <p>Loading…</p>;

  // && short-circuit: truthy 일 때만 렌더
  return (
    <>
      {error && <p className="text-red-500">{error}</p>}
      {items.length > 0 ? (
        <ul>
          {items.map((item) => (
            // map + 안정적 key
            <li key={item.id}>{item.label}</li>
          ))}
        </ul>
      ) : (
        <p>No items yet.</p>
      )}
    </>
  );
}

External links

Exercise

{ total: number; pending: number; errors: number } 받는 Stats 컴포넌트 작성. Fragment 로 <p> 형제 셋 반환. 각각에 count 가 0 이면 그 줄 숨기는 조건 사용. <Stats total={5} pending={0} errors={2} /> 렌더해서 totalerrors 줄만 보이는지 확인.
Hint
0-렌더링 함정 주의. {pending && <p>...</p>} 는 pending 이 0 일 때 literal 0 렌더. {pending > 0 && <p>...</p>} 써.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.