리스트 렌더링
데이터 모음에서 유사한 컴포넌트를 여러 개 표시하고 싶을 때가 종종 있습니다. JavaScript 배열 메서드를 사용하여 데이터 배열을 조작할 수 있습니다. 이 페이지에서는 React에서 filter()
와 map()
을 사용해 데이터 배열을 필터링하고 컴포넌트 배열로 변환해보겠습니다.
You will learn
- JavaScript의
map()
을 사용하여 배열을 컴포넌트로 렌더링하는 방법 - JavaScript의
filter()
를 사용하여 특정 컴포넌트만 렌더링하는 방법 - React에서 Key가 필요한 때와 이유
배열을 데이터로 렌더링하기
내용이 있는 리스트가 있다고 가정해 봅시다.
<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>
이러한 리스트 항목의 유일한 차이점은 콘텐츠, 즉 데이터입니다. 댓글 목록에서 프로필 이미지 갤러리에 이르기까지 인터페이스를 구축할 때 서로 다른 데이터를 사용하여 동일한 컴포넌트의 여러 인스턴스를 표시해야 하는 경우가 종종 있습니다. 이러한 상황에서 해당 데이터를 JavaScript 객체와 배열에 저장하고 map()
과 filter()
같은 메서드를 사용하여 해당 객체에서 컴포넌트 리스트를 렌더링할 수 있습니다.
다음은 배열에서 항목 리스트를 생성하는 방법에 대한 간단한 예시입니다.
- 데이터를 배열로 이동합니다.
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
people
의 요소를 새로운 JSX 노드 배열인listItems
에 매핑합니다.
const listItems = people.map(person => <li>{person}</li>);
<ul>
로 래핑된 컴포넌트의listItems
를 반환합니다.
return <ul>{listItems}</ul>;
결과는 다음과 같습니다.
const people = [ 'Creola Katherine Johnson: mathematician', 'Mario José Molina-Pasquel Henríquez: chemist', 'Mohammad Abdus Salam: physicist', 'Percy Lavon Julian: chemist', 'Subrahmanyan Chandrasekhar: astrophysicist' ]; export default function List() { const listItems = people.map(person => <li>{person}</li> ); return <ul>{listItems}</ul>; }
위의 샌드박스에 콘솔 에러가 표시된다는 점에 주의하세요.
이 에러를 수정하는 방법은 이 페이지의 뒷부분에서 알아보겠습니다. 그 전에 데이터에 몇 가지 구조를 추가해 보겠습니다.
배열의 항목들을 필터링하기
이 데이터는 훨씬 더 구조화될 수 있습니다.
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
직업이 'chemist'
인 사람들만 표시하고 싶다고 가정해 봅시다. JavaScript의 filter()
메서드를 사용하여 해당하는 사람만 반환할 수 있습니다. 이 메서드는 항목 배열을 받아 “test”(true
혹은 false
를 반환하는 함수)를 통과한 후 테스트에 통과된 항목(true
가 반환된 항목)만 있는 새로운 배열을 반환합니다.
직업
이 'chemist'
인 항목만 필요합니다. 이를 위한 “test” 함수는 (person) => person.profession === 'chemist'
와 같습니다. 이를 적용하는 과정은 다음과 같습니다.
people
에서filter()
를 호출해person.profession === 'chemist'
로 필터링해서 “chemist”로만 구성된 새로운 배열chemists
를 생성합니다.
const chemists = people.filter(person =>
person.profession === 'chemist'
);
- 이제
chemists
를 매핑합니다.
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
- 마지막으로 컴포넌트에서
listItems
를 반환합니다.
return <ul>{listItems}</ul>;
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const chemists = people.filter(person => person.profession === 'chemist' ); const listItems = chemists.map(person => <li> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} known for {person.accomplishment} </p> </li> ); return <ul>{listItems}</ul>; }
key
를 사용해서 리스트 항목을 순서대로 유지하기
위의 모든 샌드박스에 콘솔에 에러가 표시되는 것을 확인할 수 있습니다.
각 배열 항목에 다른 항목 중에서 고유하게 식별할 수 있는 문자열 또는 숫자를 key
로 지정해야 합니다.
<li key={person.id}>...</li>
Key는 각 컴포넌트가 어떤 배열 항목에 해당하는지 React에 알려주어 나중에 일치시킬 수 있도록 합니다. 이는 배열 항목이 정렬 등으로 인해 이동하거나 삽입되거나 삭제될 수 있는 경우 중요해집니다. key
를 잘 선택하면 React가 정확히 무슨 일이 일어났는지 추론하고 DOM 트리에 올바르게 업데이트 하는데 도움이 됩니다.
즉석에서 key를 생성하는 대신 데이터 안에 key를 포함해야 합니다.
export const people = [{ id: 0, // JSX에서 key로 사용됩니다. name: 'Creola Katherine Johnson', profession: 'mathematician', accomplishment: 'spaceflight calculations', imageId: 'MK3eW3A' }, { id: 1, // JSX에서 key로 사용됩니다. name: 'Mario José Molina-Pasquel Henríquez', profession: 'chemist', accomplishment: 'discovery of Arctic ozone hole', imageId: 'mynHUSa' }, { id: 2, // JSX에서 key로 사용됩니다. name: 'Mohammad Abdus Salam', profession: 'physicist', accomplishment: 'electromagnetism theory', imageId: 'bE7W1ji' }, { id: 3, // JSX에서 key로 사용됩니다. name: 'Percy Lavon Julian', profession: 'chemist', accomplishment: 'pioneering cortisone drugs, steroids and birth control pills', imageId: 'IOjWm71' }, { id: 4, // JSX에서 key로 사용됩니다. name: 'Subrahmanyan Chandrasekhar', profession: 'astrophysicist', accomplishment: 'white dwarf star mass calculations', imageId: 'lrWQx8l' }];
Deep Dive
각 항목이 하나가 아닌 여러 개의 DOM 노드를 렌더링해야 하는 경우에는 어떻게 해야 할까요?
짧은 <> </>
fragment 구문으로는 key를 전달할 수 없으므로 key를 단일 <div>
로 그룹화하거나 약간 더 길고 명시적인 <Fragment>
문법을 사용해야 합니다.
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
Fragment는 DOM에서 사라지므로 <h1>
, <p>
, <h1>
, <p>
등의 평평한 리스트가 생성됩니다.
key
를 가져오는 곳
데이터 소스마다 다른 key 소스를 제공합니다
- 데이터베이스의 데이터: : 데이터베이스에서 데이터를 가져오는 경우 본질적으로 고유한 데이터베이스 key/ID를 사용할 수 있습니다.
- 로컬에서 생성된 데이터: 데이터가 로컬에서 생성되고 유지되는 경우(예: 메모 작성 앱의 노트), 항목을 만들 때 증분 일련번호나
crypto.randomUUID()
또는uuid
같은 패키지를 사용하세요.
key 규칙
- key는 형제 간에 고유해야 합니다. 하지만 같은 key를 다른 배열의 JSX 노드에 동일한 key를 사용해도 괜찮습니다.
- key는 변경되어서는 안 되며 그렇게 되면 key는 목적에 어긋납니다! 렌더링 중에는 key를 생성하지 마세요.
React에 key가 왜 필요한 이유는 무엇인가요?
데스크탑의 파일에 이름이 없다고 상상해 보세요. 대신 첫 번째 파일, 두 번째 파일 등 순서대로 파일을 참조할 것입니다. 익숙해질 수도 있지만, 파일을 삭제한다면 혼란스러워질 수도 있습니다. 두 번째 파일이 첫 번째 파일이 되고 세 번째 파일이 두 번째 파일이 되는 식으로 말이죠.
폴더의 파일 이름과 배열의 JSX key는 비슷한 용도로 사용됩니다. 이를 통해 형제 항목 간에 항목을 고유하게 식별할 수 있습니다. 잘 선택된 key는 배열 내 위치보다 더 많은 정보를 제공합니다. 재정렬로 인해 _위치_가 변경되더라도 key
는 React가 생명주기 내내 해당 항목을 식별할 수 있게 해줍니다.
Recap
이 페이지에서 학습한 내용
- 컴포넌트에서 배열이나 객체와 같은 데이터 구조로 데이터를 이동하는 방법
- JavaScript의
map()
을 사용하여 유사한 컴포넌트 집합을 생성하는 방법 - JavaScript의
filter()
를 사용하여 필터링된 항목의 배열을 생성하는 방법 - 컬렉션에서 각 컴포넌트에
key
를 설정하여 위치나 데이터가 변경되더라도 React가 각 컴포넌트를 추적할 수 있도록 하는 이유와 방법
Challenge 1 of 4: 리스트를 둘로 나누기
예시는 모든 사람의 리스트를 보여줍니다.
두 개의 개별 리스트 Chemists와 Everyone Else을 차례로 표시하도록 변경하세요. 이전과 마찬가지로 person.profession === 'chemist'
를 확인하여 어떤 사람이 chemist인지 확인할 수 있습니다.
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const listItems = people.map(person => <li key={person.id}> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} known for {person.accomplishment} </p> </li> ); return ( <article> <h1>Scientists</h1> <ul>{listItems}</ul> </article> ); }