UI 묘사 : 렌더링 목록
데이터 모음에서 유사한 컴포넌트를 여러 개 표시하고 싶을 때가 많습니다. JavaScript 배열 메서드를 사용해 데이터 배열을 조작할 수 있습니다. 이 페이지에서는 React에서 filter() 및 map()을 사용해 데이터 배열을 필터링하고 컴포넌트 배열로 변환하겠습니다.
학습 내용
- 자바스크립트의 map()을 사용해 배열에서 컴포넌트를 렌더링하는 방법
- JavaScript의 filter()를 사용해 특정 컴포넌트만 렌더링하는 방법
- React 키를 사용해야 할 때 와 이유
배열에서 데이터 렌더링하기
콘텐츠 목록이 있다고 가정해 보겠습니다.
<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() 등의 메서드를 사용하여 해당 객체에서 컴포넌트 목록을 렌더링할 수 있습니다.
다음은 배열에서 항목 목록을 생성하는 방법에 대한 간단한 예시입니다:
1. 데이터를 배열로 옮깁니다:
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
2. people를 새로운 JSX 노드 배열인 listItems에 매핑합니다:
const listItems = people.map(person => <li>{person}</li>);
3. <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>;
}
위의 샌드박스에 콘솔 오류가 표시되는 것을 확인할 수 있습니다:
경고: 목록의 각 자식에는 고유한 "key" prop이 있어야 합니다.
이 오류를 수정하는 방법은 이 페이지의 뒷부분에서 알아보겠습니다. 그 전에 데이터에 몇 가지 구조를 추가해 보겠습니다.
items 배열 필터링
이 데이터는 훨씬 더 구조화될 수 있습니다.
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() 메서드를 사용하여 해당 사람만 반환할 수 있습니다. 이 메서드는 항목 배열을 받아 '테스트'(true 또는 false를 반환하는 함수)를 통과한 후 테스트를 통과한 항목(true)만의 새 배열을 반환합니다.
직업이 'chemist'인 항목만 원합니다. 이를 위한 'test' 함수는 (person) => person.profession === 'chemist'와 같습니다. 이를 조합하는 방법은 다음과 같습니다:
1. person.profession === 'chemist'로 필터링한 사람에 대해 filter()를 호출하여 'chemist'인 사람만 있는 새로운 배열, 화학자 배열을 만듭니다:
const chemists = people.filter(person =>
person.profession === 'chemist'
);
2. 이제 chemist에 map()을 사용합니다:
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>
);
3. 마지막으로 컴포넌트에서 listItems를 반환합니다:
return <ul>{listItems}</ul>;
함정
화살표 함수는 암묵적으로 => 바로 뒤에 식을 반환하므로 return문이 필요하지 않습니다:
const listItems = chemists.map(person =>
<li>...</li> // 암시적 반환!
);
그러나 => 뒤에 { 중괄호가 오는 경우 return을 명시적으로 작성해야 합니다!
const listItems = chemists.map(person => { // 중괄호
return <li>...</li>;
});
{를 포함하는 화살표 함수를 "블록 본문"이라고 합니다. 이 함수를 사용하면 한 줄 이상의 코드를 작성할 수 있지만 반환문을 직접 작성해야 합니다. 잊어버리면 아무것도 반환되지 않습니다!
key 로 목록 항목 순서 유지
경고: 목록의 각 자식에는 고유한 "key" prop이 있어야 합니다.
각 배열 항목에 해당 배열의 다른 항목 중에서 고유하게 식별할 수 있는 문자열 또는 숫자를 키로 지정해야 합니다:
<li key={person.id}>...</li>
참고
map() 호출 내부의 JSX 요소에는 항상 key가 필요합니다!
key는 각 컴포넌트가 어떤 배열 항목에 해당하는지 React에 알려주어 나중에 일치시킬 수 있도록 합니다. 이는 배열 항목이 (정렬 등으로 인해) 이동하거나 삽입되거나 삭제될 수 있는 경우 중요해집니다. 잘 선택된 key는 React가 정확히 무슨 일이 일어났는지 추론하고 DOM 트리를 올바르게 업데이트하는 데 도움이 됩니다.
즉석에서 key를 생성하는 대신 데이터에 key를 포함시켜야 합니다:
export const people = [{
id: 0, // JSX에서 키로 사용
name: 'Creola Katherine Johnson',
profession: 'mathematician',
accomplishment: 'spaceflight calculations',
imageId: 'MK3eW3A'
}, {
id: 1, // JSX에서 키로 사용
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
accomplishment: 'discovery of Arctic ozone hole',
imageId: 'mynHUSa'
}, {
id: 2, // JSX에서 키로 사용
name: 'Mohammad Abdus Salam',
profession: 'physicist',
accomplishment: 'electromagnetism theory',
imageId: 'bE7W1ji'
}, {
id: 3, // JSX에서 키로 사용
name: 'Percy Lavon Julian',
profession: 'chemist',
accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
imageId: 'IOjWm71'
}, {
id: 4, // JSX에서 키로 사용
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
accomplishment: 'white dwarf star mass calculations',
imageId: 'lrWQx8l'
}];
Deep Dive - 각 목록 항목에 대해 여러 DOM 노드 표시하기
각 항목이 하나가 아니라 여러 개의 DOM 노드를 렌더링해야 하는 경우 어떻게 해야 할까요?
짧은 <>...</> 프래그먼트 구문으로는 키를 전달할 수 없으므로 키를 단일 <div>로 그룹화하거나 약간 더 길고 명시적인 <Fragment> 구문을 사용해야 합니다:
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
key는 어디서 얻어야 하는가
데이터 소스에 따라 key의 출처가 달라집니다:
데이터베이스의 데이터: 데이터베이스에서 데이터를 가져오는 경우 고유한 데이터베이스 keys/ID를 사용할 수 있습니다.
로컬에서 생성된 데이터: 데이터가 로컬에서 생성되고 유지되는 경우(예: 메모 작성 앱의 노트), 항목을 만들 때 증분 카운터, crypto.randomUUID() 또는 uuid와 같은 패키지를 사용하세요.
Key 규칙
key는 형제간에 고유해야 합니다. 그러나 서로 다른 배열의 JSX 노드에 동일한 키를 사용해도 괜찮습니다.
키는 변경되어서는 안 되며, 변경되면 키의 목적에 어긋납니다! 렌더링 중에는 생성하지 마세요.
React에 key가 필요한 이유는 무엇인가요?
데스크톱의 파일에 이름이 없다고 상상해 보세요. 대신 첫 번째 파일, 두 번째 파일 등 순서대로 파일을 참조할 것입니다. 익숙해질 수도 있지만 파일을 삭제하면 혼란스러워질 수 있습니다. 두 번째 파일이 첫 번째 파일이 되고, 세 번째 파일이 두 번째 파일이 되는 식으로 말이죠.
폴더의 파일 이름과 배열의 JSX 키는 비슷한 용도로 사용됩니다. 키를 사용하면 형제 항목 간에 항목을 고유하게 식별할 수 있습니다. 잘 선택된 키는 배열 내 위치보다 더 많은 정보를 제공합니다. 재정렬로 인해 위치가 변경되더라도 키는 React가 수명 내내 해당 항목을 식별할 수 있게 해줍니다.
함정
배열에서 항목의 인덱스를 키로 사용하고 싶을 수도 있습니다. 실제로 키를 전혀 지정하지 않으면 React는 이를 사용합니다. 그러나 항목이 삽입되거나 삭제되거나 배열의 순서가 바뀌면 시간이 지남에 따라 항목을 렌더링하는 순서가 변경됩니다. 인덱스를 키로 사용하면 종종 미묘하고 혼란스러운 버그가 발생합니다.
마찬가지로 키={Math.random()}과 같이 즉석에서 키를 생성하지 마세요. 이렇게 하면 렌더링 간에 키가 일치하지 않아 모든 컴포넌트와 DOM이 매번 다시 생성될 수 있습니다. 속도가 느려질 뿐만 아니라 목록 항목 내부의 사용자 입력도 손실됩니다. 대신 데이터에 기반한 안정적인 ID를 사용하세요.
컴포넌트는 key를 props로 받지 않는다는 점에 유의하세요. React 자체에서 힌트로만 사용됩니다. 컴포넌트에 ID가 필요한 경우 별도의 props로 전달해야 합니다: <Profile key={id} userId={id} />.
요약
이 페이지에서 학습한 내용입니다:
- 컴포넌트에서 배열이나 객체와 같은 데이터 구조로 데이터를 이동하는 방법.
- JavaScript의 map()을 사용하여 유사한 컴포넌트 집합을 생성하는 방법.
- JavaScript의 filter()를 사용하여 필터링된 항목의 배열을 만드는 방법.
- 컬렉션의 각 컴포넌트에 키를 설정하여 위치나 데이터가 변경되더라도 React가 각 컴포넌트를 추적할 수 있도록 하는 이유와 방법.
과제 1 / 4: 목록을 둘로 나누기
이 예는 모든 사람의 목록을 보여줍니다.
두 개의 개별 목록을 차례로 표시하도록 변경합니다: Chemists 및 기타 모든 사람으로 변경합니다. 이전과 마찬가지로 person.profession === 'chemists'인지 확인하여 어떤 사람이 화학자인지 확인할 수 있습니다.
과제 2/4: 하나의 컴포넌트에 중첩된 목록 만들기
이 배열에서 레시피 목록을 만드세요!
배열의 각 레시피에 대해 이름을 <h2>로 표시하고 재료를 <ul>에 나열합니다.
과제 3/4: 목록 항목 컴포넌트 추출하기
이 레시피 목록 컴포넌트에는 두 개의 중첩된 맵 호출이 포함되어 있습니다.
이를 단순화하기 위해 id, name, ingredients props를 허용하는 레시피 컴포넌트를 추출합니다.
바깥쪽 key를 어디에 배치하고 그 이유는 무엇일까요?
과제 4/4: 구분 기호로 나열하기
이 예제는 카츠시카 호쿠사이의 유명한 하이쿠(정형시)를 각 줄을 <p> 태그로 감싸서 렌더링합니다. 여러분이 해야 할 일은 각 단락 사이에 <hr /> 구분 기호를 삽입하는 것입니다. 결과 구조는 다음과 같아야 합니다:
<article>
<p>I write, erase, rewrite</p>
<hr />
<p>Erase again, and then</p>
<hr />
<p>A poppy blooms.</p>
</article>
하이쿠는 세 줄로만 구성되어 있지만, 솔루션은 몇 줄이든 상관없습니다.
<hr /> 요소는 <p> 요소 사이에만 표시되며 시작이나 끝에는 표시되지 않습니다!
https://codesandbox.io/s/vigorous-newton-ypzb3u?file=/src/App.js
발표요약)
1. 목록을 그릴땐 map() 함수를 사용한다.
2. 원하는 값만 찾아 목록을 그릴때 filter()과 map() 함수를 사용한다.
3. key는 각각의 배열마다 유일한 값으로, DOM tree가 순서를 식별할수있도록 한다.
map 함수 내에서는 key를 반드시 적을 것.
4. 서버에서 가져오는 데이터의 경우 데이터에 포함된 값을 사용하는것이 좋고,
로컬에서 생성된 데이터의 경우 uuid와 같은 고유한 식별자를 사용한다.
5. key로 map함수의 두번째인자인 index를 사용하지말것. 실제 수정 삭제가 일어나는 경우 순서가 바뀌므로 버그 발생할수있음.
'[STUDY] 스터디 > React Docs' 카테고리의 다른 글
React docs beta 한글 번역 : Responding to Events - 이벤트에 응답하기 (0) | 2023.02.22 |
---|---|
React docs beta 한글 번역 : Keeping Components Pure - 컴포넌트 순수성 유지 (0) | 2023.02.21 |
React docs beta 한글 번역 : Conditional Rendering - 조건부 렌더링 (0) | 2023.02.20 |
React docs beta 한글 번역 : Passing Props to a Component - 컴포넌트에 Props 전달하기 (0) | 2023.02.19 |
[5주차] React docs beta 한글 번역 : Referencing Values with Refs - Refs로 값 참조하기 (0) | 2023.02.12 |