ReactReact 기초 · 2기초

Props와 State — 데이터 전달과 상태 관리

ReactpropsstateuseState리렌더링

Props: 데이터 전달

Props는 부모 컴포넌트가 자식에게 전달하는 데이터입니다.

// 자식 컴포넌트
interface UserCardProps {
    name: string;
    email: string;
    avatar?: string;
    isAdmin?: boolean;
}

function UserCard({ name, email, avatar, isAdmin = false }: UserCardProps) {
    return (
        <div className="card">
            {avatar && <img src={avatar} alt={name} />}
            <h3>{name}</h3>
            <p>{email}</p>
            {isAdmin && <span className="badge">관리자</span>}
        </div>
    );
}

// 부모 컴포넌트
function App() {
    return (
        <div>
            <UserCard name="철수" email="kim@example.com" isAdmin />
            <UserCard name="영희" email="lee@example.com" />
        </div>
    );
}

Props는 읽기 전용

function BadComponent({ count }: { count: number }) {
    // count = count + 1;  // ❌ props 직접 수정 금지
    return <div>{count}</div>;
}

children props

interface CardProps {
    title: string;
    children: React.ReactNode;  // JSX를 받는 타입
}

function Card({ title, children }: CardProps) {
    return (
        <div className="card">
            <h2>{title}</h2>
            <div className="content">{children}</div>
        </div>
    );
}

// 사용
<Card title="프로필">
    <p>이름: 철수</p>
    <p>이메일: kim@example.com</p>
</Card>

useState: 상태 관리

import { useState } from "react";

function Counter() {
    // [현재값, 변경함수] = useState(초기값)
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>카운트: {count}</p>
            <button onClick={() => setCount(count + 1)}>+</button>
            <button onClick={() => setCount(count - 1)}>-</button>
            <button onClick={() => setCount(0)}>초기화</button>
        </div>
    );
}

상태 업데이트 원리

flowchart LR
    STATE["state 변경\nsetCount(n)"]
    RE["리렌더링\n컴포넌트 함수 재실행"]
    DOM["DOM 업데이트\n변경된 부분만"]

    STATE --> RE --> DOM
// 함수형 업데이트 (이전 값 기반)
setCount(prevCount => prevCount + 1);

// 배치 업데이트 (React 18)
// 여러 setState가 하나의 리렌더링으로 처리됨
function handleClick() {
    setCount(c => c + 1);  // 이 두 개가
    setName("새 이름");    // 하나의 리렌더링으로 처리
}

객체 상태: 불변성

interface UserForm {
    name: string;
    email: string;
    age: number;
}

function UserFormComponent() {
    const [form, setForm] = useState<UserForm>({
        name: "",
        email: "",
        age: 0,
    });

    function handleChange(field: keyof UserForm, value: string | number) {
        // ❌ 직접 수정 — React가 변경을 감지하지 못함
        // form.name = "새 이름";

        // ✅ 스프레드로 새 객체 생성
        setForm(prev => ({ ...prev, [field]: value }));
    }

    return (
        <form>
            <input
                value={form.name}
                onChange={e => handleChange("name", e.target.value)}
            />
        </form>
    );
}

배열 상태: 불변성

function TodoList() {
    const [todos, setTodos] = useState<string[]>([]);
    const [input, setInput] = useState("");

    function addTodo() {
        if (!input.trim()) return;
        // ✅ 새 배열 생성
        setTodos(prev => [...prev, input]);
        setInput("");
    }

    function removeTodo(index: number) {
        // ✅ filter로 새 배열
        setTodos(prev => prev.filter((_, i) => i !== index));
    }

    return (
        <div>
            <input value={input} onChange={e => setInput(e.target.value)} />
            <button onClick={addTodo}>추가</button>
            <ul>
                {todos.map((todo, i) => (
                    <li key={i}>
                        {todo}
                        <button onClick={() => removeTodo(i)}>삭제</button>
                    </li>
                ))}
            </ul>
        </div>
    );
}

State 끌어올리기 (Lifting State Up)

두 컴포넌트가 같은 상태를 공유해야 할 때, 공통 부모로 상태를 올립니다.

function TemperatureConverter() {
    const [celsius, setCelsius] = useState(0);

    const fahrenheit = (celsius * 9) / 5 + 32;

    return (
        <div>
            <input
                type="number"
                value={celsius}
                onChange={e => setCelsius(Number(e.target.value))}
            />
            <span>°C = {fahrenheit.toFixed(1)}°F</span>
        </div>
    );
}

정리

개념특징
props부모 → 자식, 읽기 전용
childrenJSX를 props로 전달
useState컴포넌트 내부 상태
불변성직접 수정 금지, 새 객체/배열 생성
State 끌어올리기공유 상태는 공통 부모로

다음 편에서는 이벤트 처리 — 클릭, 입력, 폼 제출 등 다양한 이벤트를 다루는 방법을 배웁니다.

궁금한 점이 있으신가요?

협업·의뢰는 아래로, 가벼운 소통은 인스타그램 @bluefox._.hi도 환영이에요.