본문 바로가기

개발/React

[React] Input과 State

Step 0. 실습을 위한 src/App.jsx 작성

먼저 아주 단순한 입력창을 만들어 보자.

function App() {
  return (
    <div>
      <input />
    </div>
  );
}

export default App;

브라우저에서 입력창에 글자를 입력할 수는 있지만 React는 아직 그 값을 관리하고 있지 않다.

 

원하는 것은 사용자가 값을 입력하면 React가 그 값을 기억하고 다른 UI에서도 사용할 수 있도록 만드는 것이다.

이를 위해 useState를 사용한다.

 

Step 1. 입력값을 State로 만들기

import { useState } from "react";

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

  return (
    <div>
      <input />
      <p>{name}</p>
    </div>
  );
}

export default App;

여기서 name의 초기 상태값은 빈 문자열이고 아직 input과 name은 연결되어 있지 않다.

 

Step 2. onChange

onChange는 사용자가 입력창의 값을 변경하면 이벤트가 발생한다.

function App() {
  function handleChange(event) {
    console.log(event);
  }

  return <input onChange={handleChange} />;
}

export default App;

 

입력창에 글자를 입력할 때마다 handleChange가 실행된다.

 

Step 3. event.target.value

그런데 알고자 하는 것은 이벤트가 발생했다는 그 사실이 아니라 사용자가 현재 무엇을 입력했는지이다.

 

handleChange 함수의 코드를 변경하자.

function handleChange(event) {
  console.log(event.target.value);
}

 

그 다음 입력창에 글자를 입력해 보면

 

위와 같이 사용자가 현재 무엇을 입력하고 있는지 알 수 있다.

 

여기서

  • event는 발생한 이벤트의 모든 정보를 담고 있는 이벤트 객체이다.
  • event.target은 이벤트가 실제로 발생한 구체적인 HTML 요소를 가리킨다. 여기서는 사용자가 입력 중인 <input> 태그를 가리킨다. 
  • event.target.value는 해당 태그가 현재 가지고 있는 value 값으로 현재 사용자가 입력창에 작성한 내용에 해당한다.

 

Step 4. 입력값을 State에 저장하기

이제 입력값과 State를 연결해보자.

import { useState } from "react";

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

  function handleChange(event) {
    setName(event.target.value);
  }

  return (
    <div>
      <input onChange={handleChange} />
      <p>{name}</p>
    </div>
  );
}

export default App;

이제 입력창에 글자를 입력하면 화면에 출력되는 것을 볼 수 있다.

 

Step 5. value까지 연결하기

React에서는 일반적으로 다음과 같이 작성한다.

<input
  value={name}
  onChange={handleChange}
/>

 

전체 코드를 보면

import { useState } from "react";

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

  function handleChange(event) {
    setName(event.target.value);
  }

  return (
    <div>
      <input value={name} onChange={handleChange} />

      <p>입력한 이름: {name}</p>
    </div>
  );
}

export default App;

여기서 핵심은

value={name}

으로 이제 입력창의 값은 name State와 연결된다.

즉, 사용자가 input 태그에 입력한 값=name State 이다.

 

이런 형태를 React에서는 제어 컴포넌트(Controlled Component)라고 부른다.

 

핵심은 input의 값을 브라우저에게 맡겨두지 않고 React State가 관리한다는 것이다.

구조를 보면

input
 ↓
onChange
 ↓
setName
 ↓
name State
 ↓
value={name}
 ↓
input

순환 구조처럼 보이는데 이것이 React 폼의 기본 패턴이다.

 

그런데 여기서 이런 생각이 들 수 있다.

 

"왜 value까지 넣을까? onChange만 있어도 입력값이 잘 저장되는데 왜 value={name}도 쓰지?"

 

코드 자체는 정상적으로 동작한다.

 

하지만 

<input
  value={name}
  onChange={handleChange}
/>

처럼 작성하면 입력창의 값을 React State가 완전히 제어할 수 있다.

 

예를 들어, 다음과 같이 코드를 작성하여 버튼을 누르면 입력창이 바로 비워지도록 할 수 있다.

function clearName() {
  setName("");
}
<button onClick={clearName}>
  지우기
</button>

 

왜냐하면, value={name}으로 name State는 입력창의 값과 연결되어있기 때문에 name이 빈 문자열이 되면 입력창도 바로 비워진다.

 

다음 전체 실습 코드를 통해 직접 확인해보자.

import { useState } from "react";

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

  function handleChange(event) {
    setName(event.target.value);
  }

  function clearName() {
    setName("");
  }

  return (
    <div>
      <h1>이름 입력</h1>

      <input
        value={name}
        onChange={handleChange}
        placeholder="이름을 입력하세요"
      />

      <p>안녕하세요, {name}</p>

      <button onClick={clearName}>지우기</button>
    </div>
  );
}

export default App;

입력창에 글자를 입력하면

즉시 화면에 출력되고 지우기 버튼을 누르면 입력창이 즉시 비워지며 문자열에 출력된 문자열도 사라지는 것을 볼 수 있다.

 

Step 6. 여러 개의 Input

실제 화면에서는 입력창이 대개 여러 개인 경우가 많다.

import { useState } from "react";

function App() {
  const [name, setName] = useState("");
  const [job, setJob] = useState("");

  function handleNameChange(event) {
    setName(event.target.value);
  }

  function handleJobChange(event) {
    setJob(event.target.value);
  }

  return (
    <div>
      <h1>프로필 작성</h1>

      <input value={name} onChange={handleNameChange} placeholder="이름" />

      <input value={job} onChange={handleJobChange} placeholder="직업" />

      <h2>미리보기</h2>

      <p>이름: {name}</p>
      <p>직업: {job}</p>
    </div>
  );
}

export default App;

State를 두 개 만든 것 뿐이다.

 

Step 7. 버튼을 눌러서 입력값 사용하기

이번에는 입력한 이름을 바로 화면에 표시하지 않고, 버튼을 누른 뒤 저장해보자.

 

State가 두 개 필요하다.

const [inputName, setInputName] = useState(""); // 지금 입력창에 적혀 있는 상태값
const [savedName, setSavedName] = useState(""); // 등록 버튼을 눌러 확정된 상태값

 

전체 코드는

import { useState } from "react";

function App() {
  const [inputName, setInputName] = useState(""); // 지금 입력창에 적혀 있는 상태값
  const [savedName, setSavedName] = useState(""); // 등록 버튼을 눌러 확정된 상태값

  function handleChange(event) {
    setInputName(event.target.value);
  }

  function handleSave() {
    setSavedName(inputName);
  }

  return (
    <div>
      <h1>사용자 등록</h1>

      <input
        value={inputName}
        onChange={handleChange}
        placeholder="이름 입력"
      />

      <button onClick={handleSave}>등록</button>

      <p>등록된 이름: {savedName}</p>
    </div>
  );
}

export default App;

보면 State가 서로 다른 역할을 담당하고 있다.

  • inputName → 작성 중인 데이터
  • savedName → 확정된 데이터

실제 앱에서도 이런 구조가 매우 자주 등장한다.

 

브라우저를 통해 확인해보면

입력창에 입력한 글자가 등록 버튼을 눌러야 화면에 출력되는 것을 볼 수 있다.


Step 8. 폼 submit
실제 입력 화면에서는 일반적으로 <form> 태그를 사용한다.

<form>
  <input />
  <button>등록</button>
</form>

버튼을 누르면 form이 submit된다.

 

React에서는 다음과 같이 처리한다.

function handleSubmit(event) {
  event.preventDefault();

  console.log("등록");
}

그리고

<form onSubmit={handleSubmit}>

으로 연결한다.

 

전체 코드는

import { useState } from "react";

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

  function handleChange(event) {
    setName(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();

    console.log(name);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={handleChange} />

      <button type="submit">등록</button>
    </form>
  );
}

export default App;

여기서

event.preventDefault();

가 무엇인가하는 의문이 들 수 있다.

 

브라우저의 기본 form 제출 동작은 페이지를 새로고침하는 방향으로 동작할 수 있다.
그런데 React가 화면을 관리하고 있으므로 그 기본 동작(페이지 새로고침)을 막는 것이다.

 

현재 단계에선

  • React에서 form submit 처리 → preventDefault()

정도로 이해한다.

 

실습

이번 단계의 실습으로 간단한 메모장을 직접 만들어보자.

메모를 입력하고 저장 버튼을 누르면 화면 밑에 입력한 메모가 출력되는 기능이다.

 

직접 구현을 해본 다음 밑의 답안 코드와 비교해보자.

 

답안

import { useState } from "react";

function App() {
  const [input, setInput] = useState("");
  const [memo, setMemo] = useState("");

  function handleChange(event) {
    setInput(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();

    setMemo(input);
    setInput("");
  }

  return (
    <div>
      <h1>메모 작성</h1>

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleChange}
          placeholder="메모를 입력하세요"
        />

        <button type="submit">저장</button>
      </form>

      <h2>저장된 메모</h2>

      <p>{memo}</p>
    </div>
  );
}

export default App;