포스트

[이제와서 시작하는 React 마스터하기 #3] useState - 화면을 움직이게 만들기

[이제와서 시작하는 React 마스터하기 #3] useState - 화면을 움직이게 만들기

이 포스트를 읽고 나면 ✅ useState가 무엇인지 이해할 수 있어요 ✅ 버튼 클릭으로 화면을 바꿀 수 있어요 ✅ 인터랙티브한 React 앱을 만들 수 있어요


📚 시작하기 전에

이전 포스트 Day 2: 첫 React 앱 만들어보기에서 React 앱을 만들고 JSX로 화면을 그렸어요.

하지만 화면이 고정되어 있었죠? 버튼을 눌러도 아무 일도 일어나지 않았어요.

오늘은 화면을 움직이게 만들 거예요! 🚀

준비물

  • 지난 시간에 만든 React 프로젝트
  • 또는 새 프로젝트: npm create vite@latest my-app -- --template react

🤔 State가 뭐예요?

“State”는 상태를 뜻해요.

쉽게 말하면:

  • 화면에 보여줄 변하는 데이터
  • 사용자가 클릭하거나 입력하면 바뀌는 값

예를 들어볼까요?

일상 생활의 State

1
2
3
4
5
6
7
8
9
10
11
전등 스위치:
- State: 켜짐(ON) 또는 꺼짐(OFF)
- 버튼을 누르면 → State가 바뀜 → 불이 켜지거나 꺼짐

좋아요 버튼:
- State: 좋아요 개수 (0, 1, 2, 3...)
- 클릭하면 → State가 바뀜 → 숫자가 증가

로그인 상태:
- State: 로그인됨(true) 또는 로그아웃(false)
- 로그인하면 → State가 바뀜 → 화면이 바뀜

React에서 이런 “변하는 값”을 관리하는 도구가 useState예요!


🎯 useState 첫 만남

가장 간단한 예제: 카운터

코드를 보기 전에, 뭘 만들 건지 먼저 알아볼게요:

1
2
화면에 숫자가 보여요: 0
버튼을 클릭하면 → 숫자가 1씩 증가해요

코드 작성하기

src/App.jsx 파일을 열고 다음과 같이 작성하세요:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { useState } from 'react';

function App() {
  // useState 사용하기
  const [count, setCount] = useState(0);

  return (
    <div style={{ padding: '20px' }}>
      <h1>카운터</h1>
      <p>현재 숫자: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1 증가
      </button>
    </div>
  );
}

export default App;

결과 확인

파일을 저장하고 브라우저를 확인하세요!

버튼을 클릭하면 숫자가 증가하나요? 🎉

축하합니다! 첫 인터랙티브 React 앱을 만들었어요!


🔍 코드 이해하기

코드를 한 줄씩 뜯어볼게요.

1단계: useState import하기

1
import { useState } from 'react';

React에서 useState를 가져와요. 이게 없으면 useState를 쓸 수 없어요!

2단계: useState 사용하기

1
const [count, setCount] = useState(0);

이 한 줄이 매우 중요해요! 하나씩 뜯어볼게요.

useState(0)

  • 0: 초기값이에요 (처음에 화면에 보일 숫자)
  • 다른 값도 가능해요: useState(10), useState(100)

[count, setCount]

  • count: 현재 값을 저장하는 변수
  • setCount: 값을 바꾸는 함수

비유로 이해하기:

1
2
3
4
5
6
7
useState는 상자를 하나 만들어요

[상자에 든 값, 값을 바꾸는 도구] = useState(초기값)
[count,        setCount]        = useState(0)

count      → 상자를 열어서 안에 든 숫자를 확인
setCount   → 상자에 새 숫자를 넣기

3단계: 화면에 표시하기

1
<p>현재 숫자: {count}</p>

count 변수를 중괄호 {}로 감싸서 화면에 보여줘요.

4단계: 버튼 클릭 시 값 바꾸기

1
2
3
<button onClick={() => setCount(count + 1)}>
  +1 증가
</button>
  • onClick: 버튼을 클릭하면 실행할 코드
  • () =>: 화살표 함수 (함수를 간단히 쓰는 방법)
  • setCount(count + 1): 현재 값에 1을 더한 값으로 바꾸기

💪 실습 1: 감소 버튼 추가하기

이제 여러분 차례예요!

해보기

현재 코드에 “감소” 버튼을 추가해보세요.

힌트:

  • +1 증가 버튼을 복사해서 수정하세요
  • count + 1 대신 count - 1을 써보세요
💡 정답 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function App() {
  const [count, setCount] = useState(0);

  return (
    <div style={{ padding: '20px' }}>
      <h1>카운터</h1>
      <p>현재 숫자: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1 증가
      </button>
      <button onClick={() => setCount(count - 1)}>
        -1 감소
      </button>
      <button onClick={() => setCount(0)}>
        초기화
      </button>
    </div>
  );
}

보너스: 초기화 버튼도 추가했어요!


💪 실습 2: 입력 폼 만들기

카운터는 숫자만 다뤘어요. 이번엔 문자열을 다뤄볼까요?

목표

1
2
입력창에 이름을 입력하면
→ 아래에 "안녕하세요, [이름]님!"이 실시간으로 보여요

따라하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useState } from 'react';

function App() {
  const [name, setName] = useState('');

  return (
    <div style={{ padding: '20px' }}>
      <h1>이름 입력</h1>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="이름을 입력하세요"
      />
      <p>안녕하세요, {name}님!</p>
    </div>
  );
}

export default App;

새로운 포인트

value={name}

  • input의 값을 name state와 연결해요

onChange={(e) => setName(e.target.value)}

  • input에 타이핑할 때마다 실행돼요
  • e.target.value: 사용자가 입력한 값
  • setName(...): 그 값으로 state를 업데이트

실시간으로 화면이 바뀌는 이유:

  1. 사용자가 타이핑 → onChange 실행
  2. setName으로 state 변경
  3. React가 자동으로 화면 다시 그리기 (re-render)
  4. 새로운 값이 화면에 보임

💪 실습 3: 여러 개의 State 사용하기

하나의 컴포넌트에서 여러 개의 state를 쓸 수 있어요!

따라하기

로그인 폼을 만들어봐요:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { useState } from 'react';

function App() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [message, setMessage] = useState('');

  const handleLogin = () => {
    if (email && password) {
      setMessage(`${email}으로 로그인 시도 중...`);
    } else {
      setMessage('이메일과 비밀번호를 입력하세요!');
    }
  };

  return (
    <div style={{ padding: '20px' }}>
      <h1>로그인</h1>

      <div style={{ marginBottom: '10px' }}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="이메일"
          style={{ marginRight: '10px' }}
        />
      </div>

      <div style={{ marginBottom: '10px' }}>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="비밀번호"
        />
      </div>

      <button onClick={handleLogin}>
        로그인
      </button>

      {message && (
        <p style={{ marginTop: '10px', color: '#0066cc' }}>
          {message}
        </p>
      )}
    </div>
  );
}

export default App;

포인트

여러 개의 useState:

1
2
3
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [message, setMessage] = useState('');

각 데이터마다 별도의 state를 만들어요!

함수로 로직 분리:

1
2
3
const handleLogin = () => {
  // 로그인 로직
};

복잡한 코드는 함수로 빼면 깔끔해요!


💪 실습 4: 토글 버튼 만들기

on/off, 보이기/숨기기 같은 기능은 어떻게 만들까요?

따라하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useState } from 'react';

function App() {
  const [isVisible, setIsVisible] = useState(true);

  return (
    <div style={{ padding: '20px' }}>
      <h1>토글 버튼</h1>

      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '숨기기' : '보이기'}
      </button>

      {isVisible && (
        <p style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
          안녕하세요! 이 텍스트는 토글됩니다! 👋
        </p>
      )}
    </div>
  );
}

export default App;

새로운 개념

Boolean state:

1
const [isVisible, setIsVisible] = useState(true);

true 또는 false 값을 가지는 state예요!

NOT 연산자 !:

1
setIsVisible(!isVisible)
  • isVisibletrue면 → false로 바꿈
  • isVisiblefalse면 → true로 바꿈

삼항 연산자:

1
{isVisible ? '숨기기' : '보이기'}

조건 ? 참일때 : 거짓일때

조건부 렌더링:

1
{isVisible && <p>...</p>}

isVisible이 true일 때만 <p> 태그를 보여줘요!


⚠️ 자주 하는 실수

실수 1: State를 직접 바꾸려고 하기

1
2
3
4
5
6
7
8
const [count, setCount] = useState(0);

// ❌ 이렇게 하면 안 돼요!
count = count + 1;  // 에러!
count++;            // 에러!

// ✅ 반드시 set 함수를 사용하세요
setCount(count + 1);  // 정답!

왜 그럴까요?

React는 setCount를 호출할 때만 “값이 바뀌었구나!” 하고 화면을 다시 그려요. 직접 바꾸면 React가 모르기 때문에 화면이 안 바뀌어요!

실수 2: set 함수 이름을 잘못 쓰기

1
2
3
4
5
6
7
// ❌ 이름이 일치하지 않아요
const [count, setNumber] = useState(0);
setNumber(count + 1);  // 헷갈려요!

// ✅ 일관성 있게 지어요
const [count, setCount] = useState(0);
setCount(count + 1);  // 명확해요!

규칙:

  • 변수 이름: count, name, isVisible
  • Set 함수: setCount, setName, setIsVisible

실수 3: onChange 없이 value만 쓰기

1
2
3
4
5
6
7
8
// ❌ 입력이 안 돼요!
<input value={name} />

// ✅ onChange도 함께!
<input
  value={name}
  onChange={(e) => setName(e.target.value)}
/>

value만 있으면 입력창이 읽기 전용이 돼요! onChange로 값을 업데이트해야 입력할 수 있어요.


🎯 종합 실습: Todo 리스트 만들기

배운 내용을 모두 활용해서 간단한 Todo 앱을 만들어봐요!

기능

  1. 입력창에 할 일을 입력
  2. “추가” 버튼 클릭
  3. 아래에 목록으로 표시
  4. “삭제” 버튼으로 제거

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import { useState } from 'react';

function App() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');

  const addTodo = () => {
    if (input.trim()) {
      // 기존 배열에 새 항목 추가
      setTodos([...todos, input]);
      setInput('');  // 입력창 비우기
    }
  };

  const deleteTodo = (index) => {
    // 해당 인덱스 제거
    const newTodos = todos.filter((_, i) => i !== index);
    setTodos(newTodos);
  };

  return (
    <div style={{ padding: '20px', maxWidth: '400px' }}>
      <h1>할 일 목록 📝</h1>

      <div style={{ marginBottom: '20px' }}>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          placeholder="할 일을 입력하세요"
          style={{ padding: '5px', marginRight: '5px' }}
        />
        <button onClick={addTodo}>추가</button>
      </div>

      <ul style={{ listStyle: 'none', padding: 0 }}>
        {todos.map((todo, index) => (
          <li
            key={index}
            style={{
              padding: '10px',
              marginBottom: '5px',
              backgroundColor: '#f0f0f0',
              display: 'flex',
              justifyContent: 'space-between',
              alignItems: 'center'
            }}
          >
            <span>{todo}</span>
            <button onClick={() => deleteTodo(index)}>
              삭제
            </button>
          </li>
        ))}
      </ul>

      {todos.length === 0 && (
        <p style={{ color: '#999' }}>할 일이 없습니다!</p>
      )}
    </div>
  );
}

export default App;

새로운 개념

배열 State:

1
const [todos, setTodos] = useState([]);

여러 개의 항목을 저장해요!

배열에 항목 추가:

1
setTodos([...todos, input]);

...todos: 기존 배열의 모든 항목 input: 새로 추가할 항목

배열에서 항목 제거:

1
const newTodos = todos.filter((_, i) => i !== index);

filter는 조건에 맞는 항목만 남겨요.

배열 렌더링:

1
2
3
{todos.map((todo, index) => (
  <li key={index}>...</li>
))}

map으로 배열의 각 항목을 HTML로 변환해요.


📝 정리

자, Day 3을 마무리할 시간이에요!

오늘 배운 내용 체크리스트

  • useState가 무엇인지 이해했어요
  • state로 숫자를 관리할 수 있어요
  • state로 문자열을 관리할 수 있어요
  • 여러 개의 state를 사용할 수 있어요
  • 배열 state를 다룰 수 있어요
  • 간단한 Todo 앱을 만들었어요

핵심 요약 3줄

  1. useState: React에서 변하는 값을 관리하는 Hook
  2. 사용법: const [값, set함수] = useState(초기값)
  3. 규칙: 값을 바꿀 때는 반드시 set 함수 사용!

💪 숙제 (선택사항)

  1. 카운터 업그레이드
    • +1, -1, +10, -10, 초기화 버튼 추가
    • 숫자에 따라 색상 바꾸기 (양수는 파란색, 음수는 빨간색)
  2. 계산기 만들기
    • 두 숫자를 입력받아요
    • +, -, ×, ÷ 버튼으로 계산해요
    • 결과를 보여줘요
  3. 좋아요 버튼 만들기
    • 버튼을 누르면 좋아요 개수 증가
    • 누른 상태면 빨간색 하트, 안 누르면 회색 하트
    • 취소도 가능하게

🚀 다음 단계

축하합니다! useState를 마스터했어요! 🎉

이제 화면을 마음대로 움직일 수 있어요. 하지만 아직 한 가지 문제가 있어요.

“여러 컴포넌트 간에 데이터를 어떻게 공유하지?”

다음 포스트에서는 Props를 배워서 부모와 자식 컴포넌트 간에 데이터를 전달하는 방법을 알아볼 거예요!

➡️ Day 4: Props - 컴포넌트끼리 대화하기에서 만나요!


💬 State가 어렵게 느껴지나요?

처음엔 모두 그래요! “왜 변수를 그냥 안 쓰고 useState를 써야 해?”라고 생각할 수 있어요.

하지만 몇 번 써보면 이해가 돼요: React가 자동으로 화면을 업데이트하려면 state를 써야 해요!

코드를 직접 타이핑하고, 버튼을 눌러보고, 동작을 확인하세요. 눈으로 보면서 배우는 게 가장 빨라요! 💪

에러가 나도 괜찮아요. 에러 메시지를 읽고, 코드를 고쳐보세요. 그게 바로 성장하는 과정이에요! 🌱


🎓 시리즈 목록

Phase 1: React 기초 (Day 1-8)

  1. React, 왜 배워야 할까요?
  2. 첫 React 앱 만들어보기
  3. useState - 화면을 움직이게 만들기 (현재 포스트)
  4. Hooks 완벽 가이드
  5. 조건부 렌더링과 리스트 렌더링
  6. 컴포넌트 생명주기 이해하기
  7. Context API로 전역 상태 관리하기
  8. Custom Hooks 만들기

Phase 2: React 활용 (Day 9-14)

  1. React Router로 SPA 만들기
  2. 폼과 유효성 검사
  3. 성능 최적화 기법
  4. 에러 처리와 경계
  5. Ref와 DOM 접근
  6. Portals와 고급 패턴

Phase 3: React 심화 (Day 15-20)

  1. TypeScript와 React
  2. 상태 관리 라이브러리
  3. 서버 상태 관리 (TanStack Query)
  4. 테스팅 전략
  5. SSR과 Next.js 기초
  6. 배포와 DevOps

🔗 유용한 리소스

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.