#12 React 프로젝트 시작과 입력값 상태 관리

2026. 6. 16. 22:03·Project/Memo Evolution

 

Vanilla JavaScript로 메모장 CRUD 기능을 구현한 뒤, 이번에는 같은 메모장을 React로 다시 만들어보기 시작했다.

이번 단계의 목표는 메모 추가, 수정, 삭제까지 구현하는 것이 아니다.
먼저 React 프로젝트를 만들고, 기존 HTML/CSS 메모장 화면을 React로 옮긴 뒤, 입력창 값을 React의 state로 관리하는 것까지 진행했다.

 

이번 단계에서 한 일은 다음과 같다.

React 프로젝트 생성
Vite 기본 화면 제거
기존 메모장 화면을 JSX로 작성
제목 입력값을 state로 관리
내용 입력값을 state로 관리
onChange로 입력값 갱신
onClick으로 버튼 클릭 시 state 확인

 

 

React 프로젝트 생성

React 프로젝트는 기존 JavaScript 파일을 지우지 않고, 별도 폴더에 만들었다.

현재 프로젝트 구조는 다음과 같다.

Memo Evolution/
  index.html
  style.css
  script.js
  react-memo/

React 프로젝트는 react-memo 폴더 안에 생성했다.

Vite를 사용해서 React 프로젝트를 만들었다.

npm create vite@latest react-memo -- --template react

이후 React 프로젝트 폴더로 이동해서 패키지를 설치했다.

cd react-memo
npm install

개발 서버는 다음 명령어로 실행한다.

npm run dev

브라우저에서는 보통 다음 주소로 확인한다.

http://localhost:5173

 

개발 서버와 HMR

npm run dev를 실행하면 Vite 개발 서버가 실행된다.

개발 서버가 켜져 있는 상태에서 App.jsx나 App.css를 수정하고 저장하면 보통 브라우저에 자동으로 반영된다.

이 기능을 HMR이라고 한다.

HMR = Hot Module Replacement
파일 저장 시 브라우저에 변경 사항을 빠르게 반영하는 기능

그래서 단순히 컴포넌트나 CSS를 수정할 때마다 npm run dev를 다시 실행할 필요는 없다.

다만 다음 경우에는 재실행이 필요할 수 있다.

개발 서버를 꺼버린 경우
새 라이브러리를 설치한 경우
package.json을 수정한 경우
vite.config.js를 수정한 경우
개발 서버가 에러로 종료된 경우

 

Vite 기본 코드 제거

처음 Vite로 React 프로젝트를 만들면 기본 예제 화면이 들어 있다.

App.jsx에는 로고, 카운터 버튼, 문서 링크 같은 코드가 있었다.
하지만 이번 프로젝트의 목표는 메모장을 React로 다시 만드는 것이므로 기본 코드는 제거했다.

기존 코드에서 필요 없어진 것들은 다음과 같다.

import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
const [count, setCount] = useState(0)

카운터 예제도 제거했다.
이번 단계에서는 메모장 화면과 입력 상태 관리만 다룬다.

 

React에서 정적인 메모장 화면 만들기

JavaScript 단계에서 작성했던 HTML 구조를 React의 JSX로 옮겼다.

React 컴포넌트는 기본적으로 함수 형태다.

function App() {
  return (
    ...
  )
}

현재 App.jsx는 메모장 화면 전체를 담당한다.

import { useState } from 'react'
import './App.css'

function App() {
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  function handleAddMemo() {
    console.log(title)
    console.log(content)
  }

  return (
    <div className="app">
      <div className="memo-form">
        <h2>새 메모 작성</h2>

        <label htmlFor="memo-title">제목</label>
        <input
          id="memo-title"
          type="text"
          placeholder="제목을 입력하세요"
          value={title}
          onChange={(event) => setTitle(event.target.value)}
        />

        <label htmlFor="memo-content">내용</label>
        <textarea
          id="memo-content"
          placeholder="내용을 입력하세요"
          value={content}
          onChange={(event) => setContent(event.target.value)}
        ></textarea>

        <button type="button" onClick={handleAddMemo}>
          메모 추가
        </button>
      </div>

      <div className="memo-list">
        <h2>메모 목록</h2>
        <p className="empty-message">아직 작성된 메모가 없습니다.</p>
      </div>
    </div>
  )
}

export default App

 

JSX에서 HTML과 다른 점

React에서 사용하는 JSX는 HTML과 비슷하게 생겼지만, 완전히 같지는 않다.

대표적으로 class 대신 className을 사용한다.

HTML에서는 다음처럼 작성했다.

<div class="app">

JSX에서는 이렇게 작성한다.

<div className="app">

또 label에서 사용하는 for도 JSX에서는 htmlFor로 작성한다.

HTML에서는 다음과 같았다.

<label for="memo-title">제목</label>

JSX에서는 이렇게 쓴다.

<label htmlFor="memo-title">제목</label>

그리고 input처럼 닫는 태그가 없는 요소는 JSX에서 self-closing 형태로 작성한다.

<input id="memo-title" type="text" />

이번 단계에서 기억할 JSX 차이는 다음과 같다.

class → className
for → htmlFor
input은 />로 닫기

 

useState로 입력값 관리하기

JavaScript에서는 입력값을 버튼 클릭 시점에 DOM에서 직접 가져왔다.

const title = titleInput.value.trim();
const content = contentInput.value.trim();

React에서는 입력값을 보통 state로 관리한다.

이번 단계에서는 제목과 내용을 각각 state로 만들었다.

const [title, setTitle] = useState('')
const [content, setContent] = useState('')

useState('')는 초기값이 빈 문자열인 상태를 만든다.

각각의 의미는 다음과 같다.

title
= 현재 제목 입력값

setTitle
= title 값을 바꾸는 함수

content
= 현재 내용 입력값

setContent
= content 값을 바꾸는 함수

setTitle, setContent는 내가 직접 정의한 함수가 아니라 useState가 만들어주는 상태 변경 함수다.

이름은 직접 정할 수 있지만, 보통 관례상 set + 상태이름 형태로 작성한다.

const [title, setTitle] = useState('')
const [content, setContent] = useState('')

 

input의 value 연결

React에서는 입력창의 값을 state와 연결할 수 있다.

<input
  value={title}
/>

이 코드는 다음 의미다.

이 input에 보이는 값은 title state다.

즉, 입력창에 표시되는 값이 React의 title 상태를 따라간다.

내용 입력창도 마찬가지다.

<textarea
  value={content}
></textarea>

이렇게 하면 입력창의 값은 각각 title, content 상태와 연결된다.

 

onChange로 state 갱신하기

value만 연결하면 입력이 제대로 되지 않는다.

입력창의 값을 state와 연결했다면, 사용자가 입력할 때 state도 같이 바꿔줘야 한다.

그래서 onChange를 사용한다.

<input
  value={title}
  onChange={(event) => setTitle(event.target.value)}
/>

이 흐름은 다음과 같다.

사용자가 input에 글자를 입력한다.
onChange 이벤트가 실행된다.
event.target.value에 현재 입력값이 들어 있다.
setTitle(event.target.value)가 실행된다.
title state가 변경된다.
React가 다시 렌더링한다.
input에는 변경된 title 값이 표시된다.

내용 입력창도 같은 방식이다.

<textarea
  value={content}
  onChange={(event) => setContent(event.target.value)}
></textarea>

이렇게 value와 onChange를 함께 사용하는 입력 방식을 controlled input이라고 한다.

이번 단계에서는 용어보다 구조를 이해하는 것이 중요하다.

value
= input이 보여줄 값

onChange
= 사용자가 입력할 때 state를 갱신하는 이벤트

 

onClick으로 버튼 이벤트 연결하기

JavaScript에서는 버튼을 찾고 이벤트를 직접 붙였다.

addMemoButton.addEventListener('click', function () {
    ...
});

React에서는 JSX에서 버튼에 바로 이벤트를 연결한다.

<button type="button" onClick={handleAddMemo}>
  메모 추가
</button>

handleAddMemo는 버튼을 클릭했을 때 실행될 함수다.

function handleAddMemo() {
  console.log(title)
  console.log(content)
}

처음에는 버튼에 onClick을 연결하지 않아서 콘솔이 찍히지 않았다.

잘못된 코드:

<button type="button">메모 추가</button>

이 경우 버튼을 눌러도 아무 함수도 실행되지 않는다.

수정한 코드:

<button type="button" onClick={handleAddMemo}>
  메모 추가
</button>

이렇게 해야 버튼 클릭 시 handleAddMemo()가 실행된다.

 

JS와 React 비교

이번 단계에서 가장 중요한 비교는 다음과 같다.

JavaScript에서는 DOM에서 값을 직접 읽었다.

const title = titleInput.value;

React에서는 state로 값을 관리한다.

const [title, setTitle] = useState('')

JavaScript에서는 버튼을 찾아 이벤트를 직접 붙였다.

addMemoButton.addEventListener('click', function () {
    ...
});

React에서는 JSX에서 이벤트를 연결한다.

<button onClick={handleAddMemo}>
  메모 추가
</button>

JavaScript에서는 입력값을 input.value로 읽었다.

input.value

React에서는 value와 onChange를 사용해 state와 연결한다.

<input value={title} onChange={...} />

즉, React에서는 DOM에서 값을 나중에 꺼내오기보다, 입력하는 순간 state로 값을 관리한다.

 

'Project > Memo Evolution' 카테고리의 다른 글

#14 React에서 localStorage로 메모 저장하기  (0) 2026.06.19
#13 React에서 메모 삭제 기능 구현하기  (0) 2026.06.18
#11 React 시작하기  (0) 2026.06.15
#10 React로 넘어가기 전에 컴포넌트 이해하기  (0) 2026.06.15
#9 JavaScript 코드 구조 정리하기  (0) 2026.06.15
'Project/Memo Evolution' 카테고리의 다른 글
  • #14 React에서 localStorage로 메모 저장하기
  • #13 React에서 메모 삭제 기능 구현하기
  • #11 React 시작하기
  • #10 React로 넘어가기 전에 컴포넌트 이해하기
sqaxe1
sqaxe1
woojoo-devlog 님의 블로그 입니다.
  • sqaxe1
    Woojoo's Devlog
    sqaxe1
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Backend (9)
        • Servlet (7)
        • Spring (2)
      • Frontend (1)
      • CS (0)
      • Book (33)
        • 자바 웹 프로그래밍 Next Step (30)
        • 테스트 주도 개발: 고품질 쾌속개발을 위한 TDD.. (1)
        • 성공과 실패를 결정하는 1%의 네트워크 원리 (2)
      • Engineering (0)
        • Testing (0)
      • Infra (6)
        • AWS (6)
      • Java (4)
      • Network (1)
      • 김영한 (28)
        • 자바 입문 (8)
        • 실전 자바 - 기본편 (6)
        • 실전 자바 - 중급편 (10)
        • 실전 자바 - 고급편 (4)
      • Web (39)
        • Web Basics (39)
      • Project (24)
        • NeoSquare (0)
        • Memo Evolution (24)
      • 정보처리기사 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    aws
    java
    개발서적
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
sqaxe1
#12 React 프로젝트 시작과 입력값 상태 관리
상단으로

티스토리툴바