Posts [React] 반복되는 컴포넌트를 효율적으로 보여주기 - map 함수
Post
Cancel

[React] 반복되는 컴포넌트를 효율적으로 보여주기 - map 함수


✏️ 자바스크립트 배열의 map() 함수

반복되는 컴포넌트를 렌더링하기 위하여 자바스크립트 배열의 내장 함수인 map()을 사용합니다.

map 함수는 파라미터로 전달된 함수를 사용해, 배열 각 요소를 원하는 규칙에 따라 변환한 다음 새로운 배열을 생성합니다.

1
arr.map(callbackFunction(currentValue, index, array), thisArg);

즉, callbackFunction을 실행한 결과를 이용해 새로운 배열을 만들어 내는 함수입니다.

callback function을 작성할 때 맨 앞의 인자는 현재 배열(arr) 내의 값들을 의미하며, 두 번째 인자는 현재 배열 내 값의 인덱스를 의미하고, 마지막 인자는 현재 배열을 의미합니다.

thisArg는 callbackFunction 안에서 사용할 this 레퍼런스를 의미합니다.

위에서 원래 배열이 [1,2,3,4,5] 였습니다. map 함수를 사용해서, 정의한 callback function을 보면 원래 배열값에 제곱한 형태의 값으로 새로운 배열을 만들어 내었습니다.

이것을 ES6 문법으로 작성하면 다음과 같습니다.

1
2
3
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map((num) => num * num);
console.log(result);

var 대신 const 키워드를, function(…){…} 대신 화살표 함수 문법을 사용한 것입니다.


✏️ key 란?

리액트에서 li 태그 반복을 어떻게 map 함수에 활용했는지 보겠습니다.

1
2
3
4
5
6
7
8
9
10
import React from "react";

const iterationSample = () => {
  const names = ["눈사람", "얼음", "", "바람"];
  const nameList = names.map((name) => <li>{name}</li>);

  return <ul>{nameList}</ul>;
};

export default iterationSample;

이를 웹 상에 출력하면 리스트 형태로 눈사람, 얼음, 눈, 바람이 보이게 됩니다.

그런데,

1
2
3
4
5
6
7
Warning: Each child in a list should have a unique "key" prop.

Check the render method of `IterationSample`. See https://fb.me/react-warning-keys for more information.
    in li (at IterationSample.js:5)
    in IterationSample (at App.js:9)
    in App (at src/index.js:9)
    in StrictMode (at src/index.js:8)

이와 같은 오류 메세지를 볼 수 있습니다. key prop이 없다는 경고 메세지입니다.

리액트에서 key란, 렌더링 시 컴포넌트 배열에 어떤 변화가 일어났는지 더욱 빠르게 알아내기 위해 사용합니다.


  • key 설정하기

map 함수 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하는 것과 같이 작성합니다. 단 key 값은 언제나 유일한 값이어야만 합니다. 게시판의 게시물을 렌더링하는 예시가 있다면, 게시물 번호를 key 값으로 설정할 수 있습니다.

map 함수의 index로 key 값을 설정할 수도 있지만, 이는 anti pattern의 일종이라고 볼 수 있습니다. (이에 대한 자세한 이야기는 https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318 에서 확인할 수 있습니다.)

1
2
3
const articleList = articles.map((article) => (
  <Article title={article.title} writer={article.writer} key={article.id} />
));

  • 동적인 배열 렌더링하기

맨 위에서 살펴본 눈사람, 얼음, 눈, 바람 배열을 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
import React, { useState } from "react";

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: "눈사람" },
    { id: 2, text: "얼음" },
    { id: 3, text: "" },
    { id: 4, text: "바람" },
  ]);
  const [inputText, setInputText] = useState("");
  const [nextId, setNextId] = useState(5);

  const onChange = (e) => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,
      text: inputText,
    });
    setNextId(nextId + 1);
    setNames(nextNames);
    setInputText("");
  };

  const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onClick={onClick}>추가</button>
      <ul>{nameList}</ul>
    </>
  );
};

export default IterationSample;
  • const [names, setNames] = useState([ …]) 부분은 함수형 컴포넌트에서 names라는 state를 정의하고, setter인 setNames를 정의한 것입니다. 이 때 {useState}를 import 하는 것을 잊지 않아야 합니다.
    • useState 내부에는 객체 형식의 상태도 정의할 수 있으며, 위 코드는 객체 형태의 배열을 정의한 모습입니다.
  • 이 외에도 inputText, nextId 등의 상태를 정의하였습니다.
  • onChange 함수는 input 요소에서 값을 입력받을 때 onChange 이벤트 핸들링을 위해 반드시 필요합니다. 사용자로부터 받은 값을 입력받는다고 할 때에는 onChange가 필수라고 알고 있으면 될 것 같습니다.
  • onClick 함수는 버튼 클릭 이벤트 핸들링을 위해 정의하였습니다.
    • 내부에서 names.concat 부분은 names라는 객체 형태 배열의 상태에 새로운 항목을 추가하는 것입니다.
    • 그러나 단순히 concat 함수로만 새 항목이 추가되는 것은 아니며, 상태를 변경하려면 setter 함수를 활용해야 합니다. 즉, 밑에 부분에서 setNames(nextNames) 라는 부분이 상태를 새롭게 변경해 주는 것이며, 이로 인해 새로운 값이 배열에 들어갈 수 있게 됩니다.

이 포스팅은 리액트를 다루는 기술(저자 김민준)을 학습하는 과정에서 개인적으로 정리한 내용입니다.