✏️ 자바스크립트 배열의 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) 라는 부분이 상태를 새롭게 변경해 주는 것이며, 이로 인해 새로운 값이 배열에 들어갈 수 있게 됩니다.
이 포스팅은 리액트를 다루는 기술(저자 김민준)
을 학습하는 과정에서 개인적으로 정리한 내용입니다.