인프런 커뮤니티 질문&답변

aalikesme님의 프로필 이미지
aalikesme

작성한 질문수

[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지

8.4) Create - 투두 추가하기

React에서 배열 사용하기 2 - 데이터 추가하기 10분 27초 질문드립니다.

해결된 질문

작성

·

400

2

🚨 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 🚨

질문 하시기 전에 꼭 확인해주세요
- 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다)
- 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다)
- 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 😢)

질문 하실때 꼭 확인하세요
- 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X)
- 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지)
- 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우)
- 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요

- 강의의 몇 분 몇 초 관련 질문인지 알려주세요!
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.


강사님 안녕하세요
React에서 배열 사용하기 2 - 데이터 추가하기 10분 27초 코드에 대해 질문드립니다.
33라인에서
const dataId = useRef(0); 을 주셨는데
아래 코드처럼 useRef( ) 를 안쓰고
const dataId = 0;
로 썼을 때보다 장점이 있는지 질문드리고 싶습니다.

답변 1

3

이정환 Winterlood님의 프로필 이미지
이정환 Winterlood
지식공유자

안녕하세요 이정환입니다.

React 컴포넌트에서 변수가 필요할 때 let이나 const가 아닌 useRef를 이용하는 이유는 간단합니다.
let이나 const를 이용한 변수 선언은 컴포넌트가 리렌더링 될 때 마다 값이 초기화 되기 때문입니다.

예를 들어 아래와 같이 버튼을 클릭할 때 마다
리스트에 새로운 요소를 추가하는 컴포넌트가 있다고 가정해 보겠습니다.

// useRef로 선언한 버전
import { useRef, useState } from "react";
import "./styles.css";

export default function App() {
  const [list, setList] = useState([]);
  const idRef = useRef(0);

  const onClick = () => {
    const newListItem = {
      content: idRef.current
    };
    setList([...list, newListItem]);
    idRef.current++;
  };

  return (
    <div className="App">
      <button onClick={onClick}>새로운 아이템 추가</button>
      <div>
        {list.map((it) => (
          <div key={it.content}>{it.content}</div>
        ))}
      </div>
    </div>
  );
}

App 컴포넌트에는 배열을 저장하는 list State가 있고 버튼을 클릭하여 onClick 이벤트 핸들러가 실행되면 setList가 호출되어 새로운 아이템이 추가됩니다.

이때 아이템의 content로는 아이템이 추가되는 순서대로 0부터 1씩 증가하는 숫자를 할당해 주고 있습니다.

따라서 첫번째로 추가된 아이템은 content가 0, 두번째로 추가된 아이템은 content가 1 이겠죠?

image

자 그렇다면 이때 만약 idRefuseRef 대신 let을 이용해 변수로 선언하면 어떨까요?

다음 코드는 useRef대신 그냥 let을 이용해 변수를 선언한 코드입니다.

// let으로 선언한 버전
import { useState } from "react";
import "./styles.css";

export default function App() {
  const [list, setList] = useState([]);
  let idRef = 0; // ①

  const onClick = () => {
    const newListItem = {
      content: idRef
    };
    setList([...list, newListItem]);
    idRef++;
  };

  return (
    <div className="App">
      <button onClick={onClick}>새로운 아이템 추가</button>
      <div>
        {list.map((it) => (
          <div key={it.content}>{it.content}</div>
        ))}
      </div>
    </div>
  );
}

①라인 수정하여 useRef 대신 let을 이용해 변수를 선언하도록 만들었습니다.

위 코드와 이전 코드가 목표하는 바는 동일합니다.
새로운 아이템이 추가될 때 마다 idRef의 값을 1씩 증가시키면서
아이템별로 각각 다른 content 값을 가지게 하려고 합니다.

그러나 결과는 그렇지 않습니다.

image"새로운 아이템 추가" 버튼을 클릭해보면
새롭게 추가되는 모든 아이템의 content 값이 0으로 고정된다는 사실을 알 수 있습니다.

이렇게 되는 이유는 State가 업데이트되어 App 컴포넌트가 리렌더링 될 때 App 컴포넌트(함수)가 다시 호출되기 때문에 ① 라인 또한 다시 실행되기 때문입니다.

① 라인이 다시 실행되면 idRef라는 변수를 다시 생성하고 값을 0으로 다시 초기화 합니다.
따라서 이 App 컴포넌트에 추가되는 모든 list의 아이템들은 0 이라는 고정된 content를 가지게 됩니다.

자 여기서 핵심 포인트는 컴포넌트 함수 내부에 선언한 변수들은
모두 컴포넌트가 리렌더링 될 때(다시 호출 될 때) 결국 다시 생성되어 초기화 된다는 점 입니다.

결론적으로 위 코드의 idRef 처럼 컴포넌트가 리렌더링 될 때에도 기존의 값을 유지하는 변수를 사용하고 싶다면 let이 아닌 useRef를 이용하셔야 합니다.

감사합니다.

aalikesme님의 프로필 이미지
aalikesme
질문자

강사님 상세한 답변을 주셔서 감사합니다.

혹시 하나만 더 질문드려도 될까요?

const dataId = useRef(0); //리렌더링 되어도 기존 값 유지
const dataId = 0; //리렌더링 될 때마다 0으로 초기화

이라면

const [dataId, setdataId] = useState(0);
처럼 useState를 사용할 때의 장점도 질문드리고 싶습니다

aalikesme님의 프로필 이미지
aalikesme
질문자

강사님 좀 더 고민해봤는데, 이렇게 생각해도 될까요?
컴포넌트의
리렌더링을 유발하고 싶을 때, 상태 변수를 만들어주는 useState를 사용하고
(상태변수 안의 값이 변화해야 컴포넌트의 리렌더링이 생기므로)
리렌더링을 유발하고 싶지 않으면 useref나, 상수로 변수를 초기화합니다.
이러면 변수의 값이 변화해도 컴포넌트의 리렌더링이 생기지 않기 때문입니다.

이정환 Winterlood님의 프로필 이미지
이정환 Winterlood
지식공유자

리렌더링을 유발하고 싶을 때, 상태 변수를 만들어주는 useState를 사용하고
(상태변수 안의 값이 변화해야 컴포넌트의 리렌더링이 생기므로)
리렌더링을 유발하고 싶지 않으면 useref나, 상수로 변수를 초기화합니다

아이코 .. 답글은 메일로 안알려주어서 확인이 늦었네요

넵! 정확합니다.

aalikesme님의 프로필 이미지
aalikesme
질문자

강사님 답변해주셔서 감사합니다

aalikesme님의 프로필 이미지
aalikesme

작성한 질문수

질문하기