1. 반복되는 컴포넌트 ✔
- 웹 애플리케이션을 만들다 보면 아래와 같이 반복되는 코드가 있다.
//IterationSample.js
<ul>
<li>박대경</li>
<li>학생</li>
<li>대학생</li>
<li>성인</li>
</ul>
- 위 처럼 코드가 더 복잡하거나 코드양이 더욱 늘어나게 되면 파일 용량도 증가하게 되는데 이것은
낭비이다.
- 이러한 문제점을 리액트 프로젝트에서 효율적으로 보여주고 관리할 수 있다.
1.1 자바스크립트 배열의 map() 함수
- 자바스크립트 배열 객체의 내장 함수인
map 함수
를 사용하여 반복되는 컴포넌트를 렌더링할 수 있다.
map 함수
는 파라미터로 전달된 함수를 사용해서 배열 내 각 요소를 원하는 규칙에 따라 변환하고 새로운 배열을 생성한다.
1.1.1 문법
arr.map(callback, [thisArg])
- callback: 새로운 배열의 요소를 생성하는 함수, 파라미터는 다음과 같다.
- currentValue: 현재 처리하고 있는 요소
- index: 현재 처리하고 있는 요소의 index 값
- array: 현재 처리하고 있는 배열의 원본
- thisArg(선택항목): callback 함수 내부에서 사용할 this 레퍼런스
1.1.2 map 함수 예제✏
- map 함수를 사용하여 배열 [1, 2, 3, 4, 5]의 각 요소를
제곱
해서 새로운 배열을 생성해보자.
var numbers = [1, 2, 3, 4, 5];
var processed = numbers.map(function(num) {
return num * num;
});
// 👉 결과: [1, 4, 9, 16, 25]
// 위의 코드를 ES6 문법으로 작성
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map(num => num * num);
// 👉 결과: [1, 4, 9, 16, 25]
1.2 데이터 배열을 컴포넌트 배열로 변환하기
1.1.2 절
과 같이 똑같은 원리로 기존 배열로 컴포넌트로 구성된 배열을 생성할 수 있다.
1.2.1 컴포넌트 수정하기
1 절
에서 만들었던 IterationSample
컴포넌트를 다음과 같이 수정했다.
//IterationSample.js
const IterationSample = () => {
const names = ['박대경', '학생', '대학생', '성인'];
const nameList = names.map(name => <li>{name}</li>);
return <ul>{nameList}</ul>;
};
- 그 후, App 컴포넌트에서
IterationSample 컴포넌트
를 불러와 렌더링하면 아래와 같이 렌더링된다.
- 하지만, 개발자 도구의 콘솔을 보면
경고메세지
가 출력된다.
- Warning: Each child in a list should have a unique "key" prop.
즉, key
prop가 없다는 경고 메시지다.
1.3 key
- 리액트에서
key
는 컴포넌트 배열을 렌더링했을 때 어떤 원소에 변동이 있는지 알기 위해서 사용한다.
즉, 원소를 생성
, 제거
, 수정
이 가능하다.
- 하지만,
key
가 없을 때는 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교하면서 변화를 알아낸다.
- 쉽게 정리하면
key
가 있을 경우 key
를 사용하여 어떤 변화가 일어나는지 빠르게 알 수 있다.
1.3.1 key 설정
- key 값을 설정할 때는
map 함수
의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 된다.
주의❗❗ key
값은 언제나 유일해야함
1.2 절
의 예제 컴포넌트는 고유 번호가 없기 때문에 map 함수
에 전달되는 콜백 함수인 index
값을 사용하면 된다.
//IterationSameple.js
const IterationSample = () => {
const names = ['박대경', '학생', '대학생', '성인'];
const nameList = names.map((name, index) => <li key={index}>{name}</li>);
return <ul>{nameList}</ul>;
};
- 위와 같이 코드를 작성하면 개발자 도구 콘솔에 더 이상 경고 메시지는 출력되지 않는다.
- 여기서 또 주의해야할 점이 있다.
고유한 값이 없을 때만 index
값을 key
로 사용해야 한다.
- 왜?🧐 배열이 변경될 때 리렌더링 과정이 효율적이지 못하기 때문❗
1.4 다른 예제
- 고정된 배열을 렌더링하는 것이 아닌, 동적인 배열을 렌더링하는 예제와,
key
값이 없을 때 어떻게 key
값을 생성할 수 있는지 코드를 작성해 보자.
1.4.1 컴포넌트 생성
- IterationSample 컴포넌트 에서
useState
를 사용하여 상태를 설정하여 코드를 작성했다.
- 총 세 가지 상태를 사용한다.
데이터 배열
, 텍스트 입력이 가능한 input
, 고유 값 생성
//IterationSample.js
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, // nextId 값을 id로 설정
text: inputText
});
setNextId(nextId + 1); // nextId 값에 +1
setNames(nextNames); // names 값 업데이트
setInputText(''); // inputText 값 비우기.
};
const nameList = names.map(name => <li key={name.id}>{name.text}</li>);
return (
<div>
<input value={inputText} onChange={onChange} />
<button onClick={onClick}>추가</button>
<ul>{nameList}</ul>
</div>
)
};
- 배열에 새 항목을 추가할 때 배열의
push 함수
를 사용하지 않고 concat
을 사용했다.
- 그 이유는
push 함수
는 기존 배열 자체를 변경하지만 concat
은 새로운 배열을 만들어 준다는 차이점이 있다.
- 리액트에서 상태 업데이트할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야한다. ❗❗
- 이와 반대로 데이터를 제거할 때는
filter
함수를 사용하여 구현하면 될것 같다.
end