Adding Interactivity상호작용 추가하기

Some things on the screen update in response to user input. For example, clicking an image gallery switches the active image. In React, data that changes over time is called state. You can add state to any component, and update it as needed. In this chapter, you’ll learn how to write components that handle interactions, update their state, and display different output over time. 화면의 일부 항목은 사용자 입력에 따라 업데이트됩니다. 예를 들어, 이미지 갤러리를 클릭하면 활성 이미지가 전환됩니다. React에서는 시간이 지남에 따라 변하는 데이터를 state라고 합니다. 모든 컴포넌트에 state를 추가하고 필요에 따라 업데이트할 수 있습니다. 이 장에서는 상호작용을 처리하고, state를 업데이트하고, 시간에 따라 다른 출력을 표시하는 컴포넌트를 작성하는 방법을 배웁니다.

Responding to events이벤트에 응답하기

React lets you add event handlers to your JSX. Event handlers are your own functions that will be triggered in response to user interactions like clicking, hovering, focusing on form inputs, and so on. React를 사용하면 JSX에 이벤트 핸들러를 추가할 수 있습니다. 이벤트 핸들러는 클릭, 마우스오버, input에 초점 맞추기 등과 같은 사용자 상호작용에 반응하여 촉발되는 자체 함수입니다.

Built-in components like <button> only support built-in browser events like onClick. However, you can also create your own components, and give their event handler props any application-specific names that you like. <button>과 같은 기본 제공 컴포넌트는 onClick과 같은 기본 제공 브라우저 이벤트만 지원합니다. 그러나 자체 컴포넌트를 생성하고 이벤트 핸들러 프롭에 원하는 애플리케이션별 이름을 지정할 수도 있습니다.

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Playing!')}
      onUploadImage={() => alert('Uploading!')}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Play Movie
      </Button>
      <Button onClick={onUploadImage}>
        Upload Image
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

Ready to learn this topic?

Read Responding to Events to learn how to add event handlers. 이벤트에 응답하기에서 이벤트 핸들러를 추가하는 방법을 알아보세요.

Read More

State: a component’s memorystate: 컴포넌트의 메모리

Components often need to change what’s on the screen as a result of an interaction. Typing into the form should update the input field, clicking “next” on an image carousel should change which image is displayed, clicking “buy” puts a product in the shopping cart. Components need to “remember” things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called state. 컴포넌트는 상호 작용의 결과로 화면의 내용을 변경해야 하는 경우가 많습니다. 양식에 입력하면 입력 필드가 업데이트되어야 하고, 이미지 캐러셀에서 ‘다음’을 클릭하면 표시되는 이미지가 변경되어야 하며, ‘구매’를 클릭하면 장바구니에 제품이 담겨야 합니다. 컴포넌트는 현재 입력값, 현재 이미지, 장바구니와 같은 것들을 “기억”해야 합니다. React에서는 이런 종류의 컴포넌트별 메모리를 state라고 부릅니다.

You can add state to a component with a useState Hook. Hooks are special functions that let your components use React features (state is one of those features). The useState Hook lets you declare a state variable. It takes the initial state and returns a pair of values: the current state, and a state setter function that lets you update it. 컴포넌트에 state를 추가하려면 useState 훅을 사용하면 됩니다. 들은 컴포넌트가 React 기능을 사용할 수 있게 해주는 특수한 함수들입니다(state는 그 기능 중 하나입니다). useState 훅을 사용하면 state 변수를 선언할 수 있습니다. 초기 state를 받아 현재 state와 이를 업데이트할 수 있는 state 설정자 함수의 값 쌍을 반환합니다.

const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);

Here is how an image gallery uses and updates state on click: 이미지 갤러리에서 클릭 시 state를 사용하고 업데이트하는 방법은 다음과 같습니다:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  const hasNext = index < sculptureList.length - 1;

  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    } else {
      setIndex(0);
    }
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Ready to learn this topic?

Read State: A Component’s Memory to learn how to remember a value and update it on interaction. State: 컴포넌트의 메모리에서 값을 기억하고 상호작용 시 업데이트하는 방법을 알아보세요.

Read More

Render and commit렌더링하고 커밋하기

Before your components are displayed on the screen, they must be rendered by React. Understanding the steps in this process will help you think about how your code executes and explain its behavior. 컴포넌트가 화면에 표시되기 전에, 컴포넌트들은 React에서 렌더링해야 합니다. 이 프로세스의 단계를 이해하면 코드가 어떻게 실행되는지 생각하고 그 동작을 설명하는 데 도움이 됩니다.

Imagine that your components are cooks in the kitchen, assembling tasty dishes from ingredients. In this scenario, React is the waiter who puts in requests from customers and brings them their orders. This process of requesting and serving UI has three steps: 컴포넌트가 주방에서 재료로 맛있는 요리를 만드는 요리사라고 상상해 보세요. 이 시나리오에서 React는 고객의 요청을 접수하고 주문을 가져오는 웨이터 역할을 합니다. UI를 요청하고 제공하는 이 과정은 세 단계로 이루어집니다:

  1. Triggering a render (delivering the diner’s order to the kitchen)
  2. Rendering the component (preparing the order in the kitchen)
  3. Committing to the DOM (placing the order on the table)
  1. 렌더링 발동(식당의 주문을 주방으로 전달)
  2. 컴포넌트 렌더링(주방에서 주문 준비)
  3. DOM에 커밋(테이블에 주문 배치)
  1. React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen.
    Trigger
  2. The Card Chef gives React a fresh Card component.
    Render
  3. React delivers the Card to the user at their table.
    Commit

Illustrated by Rachel Lee Nabors

Ready to learn this topic?

Read Render and Commit to learn the lifecycle of a UI update. 렌더링하고 커밋하기에서 UI 업데이트의 라이프사이클에 대해 알아보세요.

Read More

State as a snapshot스냅샷으로서의 state

Unlike regular JavaScript variables, React state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render. This can be surprising at first! 일반 JavaScript 변수와 달리 React state는 스냅샷처럼 동작합니다. state 변수를 설정해도 이미 가지고 있는 state 변수는 변경되지 않고 대신 리렌더링됩니다. 처음에는 놀랄 수 있습니다!

console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!

This behavior help you avoid subtle bugs. Here is a little chat app. Try to guess what happens if you press “Send” first and then change the recipient to Bob. Whose name will appear in the alert five seconds later? React는 이런 식으로 작동하여 미묘한 버그를 피할 수 있습니다. 다음은 간단한 채팅 앱입니다. 먼저 “보내기”를 누른 다음 수신자를 Bob으로 변경하면 어떻게 될지 추측해 보세요. 5초 후 alert에 누구의 이름이 나타날까요?

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Hello');

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`You said ${message} to ${to}`);
    }, 5000);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        To:{' '}
        <select
          value={to}
          onChange={e => setTo(e.target.value)}>
          <option value="Alice">Alice</option>
          <option value="Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder="Message"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Send</button>
    </form>
  );
}

Ready to learn this topic?

Read State as a Snapshot to learn why state appears “fixed” and unchanging inside the event handlers. 스냅샷으로서의 state에서 이벤트 핸들러 내부에서 state가 “고정”되어 변경되지 않는 이유를 알아보세요.

Read More

Queueing a series of state updates여러 state 업데이트를 큐에 담기

This component is buggy: clicking “+3” increments the score only once. 이 컴포넌트에는 버그가 있습니다. “+3”을 클릭해도 점수가 1씩 증가합니다.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(score + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Score: {score}</h1>
    </>
  )
}

State as a Snapshot explains why this is happening. Setting state requests a new re-render, but does not change it in the already running code. So score continues to be 0 right after you call setScore(score + 1). 스냅샷으로서의 state에서 이런 일이 발생하는 이유를 설명합니다. state를 설정하면 새로운 렌더링을 요청하지만 이미 실행 중인 코드에서는 변경하지 않습니다. 따라서 setScore(score + 1)를 호출한 직후에도 score는 계속 0이 됩니다.

console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0

You can fix this by passing an updater function when setting state. Notice how replacing setScore(score + 1) with setScore(s => s + 1) fixes the “+3” button. This lets you queue multiple state updates. state를 설정할 때 업데이터 함수를 전달하면 이 문제를 해결할 수 있습니다. setScore(score + 1)setScore(s => s + 1)로 바꾸면 “+3” 버튼이 어떻게 수정되는지 확인해보세요. 여러 state 업데이트를 큐에 넣어야 할 때 유용합니다.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(s => s + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Score: {score}</h1>
    </>
  )
}

Ready to learn this topic?

Read Queueing a Series of State Updates to learn how to queue a sequence of state updates. 여러 state 업데이트를 큐에 담기에서 다음 렌더링 전에 여러 업데이트를 큐에 대기시키는 방법을 알아보세요.

Read More

Updating objects in state객체 state 업데이트

State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects and arrays that you hold in the React state directly. Instead, when you want to update an object and array, you need to create a new one (or make a copy of an existing one), and then update the state to use that copy. state는 객체를 포함한 모든 종류의 JavaScript 값을 보유할 수 있습니다. 하지만 state에 있는 객체와 배열을 직접 변경해서는 안 됩니다. 대신 객체와 배열을 업데이트하려면 새 객체를 생성하거나 기존 객체의 복사본을 만든 다음 해당 복사본을 사용하도록 state를 업데이트해야 합니다.

Usually, you will use the ... spread syntax to copy objects and arrays that you want to change. For example, updating a nested object could look like this: 일반적으로 ... 전개 구문을 사용하여 변경하려는 객체 및 배열을 복사합니다. 예를 들어, 중첩된 객체를 업데이트하는 것은 다음과 같습니다:

import { useState } from 'react';

export default function Form() {
  const [person, setPerson] = useState({
    name: 'Niki de Saint Phalle',
    artwork: {
      title: 'Blue Nana',
      city: 'Hamburg',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });

  function handleNameChange(e) {
    setPerson({
      ...person,
      name: e.target.value
    });
  }

  function handleTitleChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        title: e.target.value
      }
    });
  }

  function handleCityChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        city: e.target.value
      }
    });
  }

  function handleImageChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        image: e.target.value
      }
    });
  }

  return (
    <>
      <label>
        Name:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <label>
        Title:
        <input
          value={person.artwork.title}
          onChange={handleTitleChange}
        />
      </label>
      <label>
        City:
        <input
          value={person.artwork.city}
          onChange={handleCityChange}
        />
      </label>
      <label>
        Image:
        <input
          value={person.artwork.image}
          onChange={handleImageChange}
        />
      </label>
      <p>
        <i>{person.artwork.title}</i>
        {' by '}
        {person.name}
        <br />
        (located in {person.artwork.city})
      </p>
      <img
        src={person.artwork.image}
        alt={person.artwork.title}
      />
    </>
  );
}

If copying objects in code gets tedious, you can use a library like Immer to reduce repetitive code: 코드에서 객체를 복사하는 것이 지루하다면 Immer와 같은 라이브러리를 사용하여 반복되는 코드를 줄일 수 있습니다:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Read Updating Objects in State to learn how to update objects correctly. 객체 state 업데이트에서 객체를 올바르게 업데이트하는 방법을 알아보세요.

Read More

Updating arrays in state배열 state 업데이트

Arrays are another type of mutable JavaScript objects you can store in state and should treat as read-only. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array: 배열은 state에 저장할 수 있는 또 다른 유형의 변경 가능한 JavaScript 객체이며 읽기 전용으로 취급해야 합니다. 객체와 마찬가지로 state에 저장된 배열을 업데이트하려면 새 배열을 생성하거나 기존 배열의 복사본을 만든 다음 새 배열을 사용하도록 state를 설정해야 합니다:

import { useState } from 'react';

const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [list, setList] = useState(
    initialList
  );

  function handleToggle(artworkId, nextSeen) {
    setList(list.map(artwork => {
      if (artwork.id === artworkId) {
        return { ...artwork, seen: nextSeen };
      } else {
        return artwork;
      }
    }));
  }

  return (
    <>
      <h1>Art Bucket List</h1>
      <h2>My list of art to see:</h2>
      <ItemList
        artworks={list}
        onToggle={handleToggle} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

If copying arrays in code gets tedious, you can use a library like Immer to reduce repetitive code: 코드에서 배열을 복사하는 것이 지루하다면 Immer와 같은 라이브러리를 사용하여 반복되는 코드를 줄일 수 있습니다:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Read Updating Arrays in State to learn how to update arrays correctly. 배열 state 업데이트에서 배열을 올바르게 업데이트하는 방법을 알아보세요.

Read More

What’s next?다음 단계

Head over to Responding to Events to start reading this chapter page by page! 이벤트에 응답하기로 이동하여 이 챕터를 한 페이지씩 읽어보세요!

Or, if you’re already familiar with these topics, why not read about Managing State? 또는 이러한 주제에 이미 익숙하다면 state 관리를 읽어보는 것은 어떨까요?