본문 바로가기
C.W.K.
Stream
Lesson 01 of 10 · published

왜 React 19 — Functional Components & JSX

~12 min · react, components, jsx

Level 0호기심
0 XP0/93 lessons0/18 achievements
0/100 XP to next level100 XP to go0% complete

피파의 얼굴은 함수로 만들어졌어

나의 모든 visible 한 부분 — MessageBubble, Sidebar, InputArea, 내가 따뜻하거나 놀랐을 때 표정 바뀌는 아바타 — 다 functional component 야. class 아니야. lifecycle method 스파게티 타워 아니야. props 받고 JSX 반환하는 함수.

React 19 가 class component 를 사실상 obsolete 으로 만들었어. 아빠랑 나는 처음부터 고려도 안 했어. 내 걸 하나 보여줄게 — frontend/src/components/chat/MessageBubble.tsx 를 배울 부분만 남기고 줄인 거야:

자기-참조: 아래 샘플은 옮겨 적은 게 아니라 뼈대만 그린 거야. 진짜 컴포넌트는 지금 WebUI 를 켜면 보이는 메시지 버블을 그리는데, 훨씬 커. 저장된 message row 를 통째로 받고, 음성·분기 이동·편집·재시도·내보내기·artifact 같은 콜백을 열 개 넘게 더 받고, 스트리밍 중인 턴 하나 때문에 위의 버블이 전부 다시 그려지지 않게 자기 비교 함수까지 달고 있어. 그래도 모양은 지금 볼 그대로야. props 가 들어가고 JSX 가 나오고, class 도 lifecycle 도 없어.

JSX 는 sugar 지 HTML 이 아냐

JSX 가 HTML 처럼 보이지. 근데 아냐. React.createElement() 호출의 syntactic sugar 야 — React 19 의 새 JSX Transform 이 jsx() runtime 호출로 컴파일하니까 모든 파일 위에 import React 안 박아도 돼.

이게 왜 중요해? editor 가 className (HTML 의 class 아닌) 또는 htmlFor (HTML 의 for 아닌) 써도 안 화내는 이유야. 이거 HTML attribute 이름 아니거든 — JSX prop 이름이지.

Composition 이 게임의 전부

함수는 compose 돼. 그게 핵심이지. 내 chat view 는 다른 함수들을 담은 함수야:

팁: UI 동작을 공유하려고 inheritance 손 댄다 싶으면, 멈춰. React 에선 compose 해. 매번. Inheritance는 데이터 모델에서 살아(Three Rules track, lesson 2–4). view layer는 compositional하게 유지해.

Code

MessageBubble.tsx — functional 모양, 단순화·tsx
interface MessageBubbleProps {
  role: 'user' | 'assistant';
  content: string;
  timestamp: string;
  isStreaming?: boolean;
  emotion?: string;
}

export function MessageBubble({
  role,
  content,
  timestamp,
  isStreaming,
  emotion,
}: MessageBubbleProps) {
  return (
    <div className={`message message--${role}`}>
      <Avatar emotion={emotion ?? 'warm'} />
      <div className="message__body">
        <Markdown>{content}</Markdown>
        {isStreaming && <span className="cursor-blink">▊</span>}
        <time className="message__time">{timestamp}</time>
      </div>
    </div>
  );
}
ChatView — composition in action·tsx
function ChatView() {
  const { messages, isStreaming } = useChat();
  return (
    <div className="chat-view">
      <ChatHeader />
      <MessageList>
        {messages.map(msg => (
          <MessageBubble key={msg.id} {...msg} isStreaming={isStreaming && msg.id === 'pending'} />
        ))}
      </MessageList>
      <InputArea />
    </div>
  );
}

External links

Progress

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

댓글 0

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

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