Compit
소개문의요금제개인정보처리방침이용약관환불규정

온드(ONDE) | 대표: 노승현 | 사업자등록번호: 778-22-02215

서울특별시 광진구 긴고랑로 133-1, 401호(중곡동, 백림주택) | 전화: 010-7794-3962

© 2026 Compit. All rights reserved.
COMPIT
챌린지연습장쇼케이스학습블로그랭킹가이드보안기초
챌린지연습장쇼케이스학습블로그랭킹가이드보안기초
홈/블로그/React 입문 가이드 — 컴포넌트부터 상태 관리까지
React

React 입문 가이드 — 컴포넌트부터 상태 관리까지

React의 핵심 개념인 컴포넌트, JSX, props, state, 이벤트 처리를 예제와 함께 기초부터 설명합니다.

2026-03-05·⏱ 15분
ReactJavaScript프레임워크

React란?

React는 Meta(구 Facebook)가 만든 자바스크립트 UI 라이브러리입니다. 웹 페이지를 "컴포넌트"라는 작은 조각으로 나누어 만들고, 데이터가 변하면 화면을 자동으로 업데이트합니다.

2026년 현재 가장 많이 사용되는 프론트엔드 기술이며, Instagram, Netflix, Airbnb, Discord 등이 React로 만들어졌습니다.

왜 React를 쓸까?

순수 HTML/CSS/JS로도 웹사이트를 만들 수 있는데, 왜 React가 필요할까요?

문제: 데이터와 UI 동기화

// 순수 JS: 할 일 추가할 때마다 직접 DOM 조작
function addTodo(text) {
  todos.push(text);
  const li = document.createElement('li');
  li.textContent = text;
  document.querySelector('.todo-list').appendChild(li);
  document.querySelector('.count').textContent = todos.length + '개';
}

할 일이 추가될 때마다 리스트도 업데이트, 카운터도 업데이트... 데이터가 변할 때마다 관련 UI를 전부 직접 바꿔야 합니다. 앱이 복잡해지면 버그의 온상이 됩니다.

해결: 선언적 UI

// React: 데이터만 바꾸면 UI는 자동 업데이트
function TodoApp() {
  const [todos, setTodos] = useState([]);

  return (
    <div>
      <p>{todos.length}개</p>
      <ul>
        {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
      </ul>
    </div>
  );
}

"이 데이터가 이렇게 보여야 해"라고 선언만 하면, React가 알아서 DOM을 업데이트합니다.

컴포넌트 (Component)

React의 기본 단위입니다. 함수 하나가 UI 조각 하나입니다.

function Button() {
  return <button className="btn">Click Me</button>;
}

function Card() {
  return (
    <div className="card">
      <h3>제목</h3>
      <p>내용</p>
      <Button />  {/* 컴포넌트 재사용 */}
    </div>
  );
}

컴포넌트 이름은 반드시 대문자로 시작합니다. 소문자면 HTML 태그로 인식됩니다.

JSX

HTML처럼 생겼지만 실제로는 JavaScript입니다. 몇 가지 차이점:

function Profile() {
  const name = "김개발";
  const isLoggedIn = true;

  return (
    <div className="profile">       {/* class → className */}
      <img src="avatar.png" alt="" /> {/* 닫는 태그 필수 */}
      <p>{name}님 환영합니다</p>       {/* {} 안에 JS 표현식 */}
      {isLoggedIn && <button>로그아웃</button>}  {/* 조건부 렌더링 */}
      <label htmlFor="email">이메일</label>  {/* for → htmlFor */}
    </div>
  );
}
HTMLJSX
classclassName
forhtmlFor
onclickonClick
닫는 태그 선택반드시 닫기 (<img />)

props: 부모 → 자식 데이터 전달

컴포넌트에 데이터를 전달하는 방법입니다. HTML 속성처럼 사용합니다.

// 부모
function App() {
  return (
    <div>
      <UserCard name="김개발" role="프론트엔드" level={5} />
      <UserCard name="이보안" role="보안전문가" level={3} />
    </div>
  );
}

// 자식
function UserCard({ name, role, level }) {
  return (
    <div className="card">
      <h3>{name}</h3>
      <p>{role} · Lv.{level}</p>
    </div>
  );
}

props는 읽기 전용입니다. 자식이 props를 직접 바꿀 수 없습니다.

state: 컴포넌트의 내부 상태

useState로 컴포넌트 안에서 변하는 데이터를 관리합니다.

import { useState } from 'react';

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

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(0)}>리셋</button>
    </div>
  );
}

setCount를 호출하면 React가 자동으로 화면을 다시 그립니다. 직접 DOM을 조작할 필요가 없습니다.

state 주의사항

// 틀린 방법: state를 직접 변경
count = count + 1;           // 화면 업데이트 안 됨!

// 올바른 방법: setter 함수 사용
setCount(count + 1);         // 화면 자동 업데이트

// 이전 값 기반으로 변경할 때
setCount(prev => prev + 1);  // 더 안전한 방법

이벤트 처리

function Form() {
  const [text, setText] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();       // 폼 기본 동작 방지
    console.log('입력값:', text);
    setText('');               // 입력 초기화
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="입력하세요"
      />
      <button type="submit">전송</button>
    </form>
  );
}

React에서는 addEventListener 대신 JSX에 직접 이벤트 핸들러를 붙입니다.

조건부 렌더링

function Greeting({ isLoggedIn, name }) {
  // 방법 1: if문
  if (!isLoggedIn) {
    return <p>로그인해주세요</p>;
  }

  return (
    <div>
      {/* 방법 2: && 연산자 */}
      {name && <h2>{name}님 환영합니다</h2>}

      {/* 방법 3: 삼항 연산자 */}
      <span className={isLoggedIn ? 'badge-green' : 'badge-gray'}>
        {isLoggedIn ? '온라인' : '오프라인'}
      </span>
    </div>
  );
}

리스트 렌더링

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'React 배우기' },
    { id: 2, text: 'Flexbox 연습하기' },
    { id: 3, text: '챌린지 풀기' },
  ]);

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

key는 필수입니다. React가 각 아이템을 구분하는 데 사용합니다. 배열 인덱스 대신 고유한 id를 사용하세요.

useEffect: 부수 효과

컴포넌트가 렌더링된 후 실행되는 코드를 작성합니다. API 호출, 타이머, 이벤트 리스너 등에 사용합니다.

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 컴포넌트가 마운트되거나 userId가 변경될 때 실행
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);  // 의존성 배열: userId가 변하면 다시 실행

  if (!user) return <p>로딩 중...</p>;

  return <h2>{user.name}</h2>;
}

의존성 배열:

  • [] → 마운트 시 1번만 실행
  • [userId] → userId 변경 시마다 실행
  • 생략 → 매 렌더링마다 실행 (보통 이럴 일은 없음)

컴포넌트 구조 잡기

하나의 컴포넌트가 너무 크면 분리합니다.

App
├── Header
│   ├── Logo
│   └── Navigation
├── Main
│   ├── SearchBar
│   └── CardGrid
│       └── Card (×N)
└── Footer

분리 기준:

  1. 재사용 — 여러 곳에서 쓰이면 분리 (Button, Card 등)
  2. 복잡도 — 한 컴포넌트가 100줄 넘으면 분리 고려
  3. 관심사 — 다른 역할이면 분리 (데이터 로직 vs UI)

정리

React 핵심 개념:

  1. 컴포넌트 — UI를 함수 단위로 분리
  2. JSX — HTML처럼 생긴 JavaScript
  3. props — 부모→자식 데이터 전달
  4. state — 컴포넌트 내부 변경 가능한 데이터
  5. 이벤트 — onClick, onChange 등
  6. useEffect — API 호출, 타이머 등 부수 효과

이 6가지를 이해하면 React로 웹 앱을 만들 수 있습니다. 이론을 읽었다면, 직접 React 챌린지를 풀어보세요!

관련 챌린지로 연습하기

카운터 컴포넌트
Lv.1 · React
토글 버튼
Lv.1 · React
투두 리스트
Lv.2 · React
프로필 카드
Lv.2 · React
← 블로그 목록으로