Lists and Keys (리스트, 키) / 여러 개의 컴포넌트에 렌더링하기 [처음 만난 리액트 #28]
10강. Lists and Keys
List
- List: 목록
- 컴퓨터 프로그래밍에서는 같은 아이템을 순서대로 모아놓은 것이 리스트에 해당됩니다.
- 리스트를 위해 사용하는 자료구조가 Array(배열)입니다.
Array
: JavaScript의 변수나 객체들을 하나의 변수로 묶어놓은 것
const numbers = [1, 2, 3, 4, 5];
▲ JavaScript에서의 배열
리액트에서는 배열을 사용해서 리스트 형태로 element들을 렌더링할 수 있습니다.
Key
- Key: 열쇠
- 열쇠의 특징? 열쇠는 모두 모양이 다르다 -> 열쇠는 각각 고유함
- 각 객체나 아이템을 구분할 수 있는 고유한 값
- 리액트에서의 Key) 아이템을 구분하기 위한 고유한 문자열
- 리스트에서 어떤 아이템이 변경, 추가, 제거되었는지 구분하기 위해 사용
- 일상생활에서 key? 주민등록번호, 학번, 핸드폰 번호, 여권번호 ... (고유한 값이라는 특징을 가짐)
고유하다는 특징은 범위가 한정되어 있음 → 특정 범위에서만 고유하면 된다!
리액트에서의 key 값은 같은 List에 있는 elements 사이에서만 고유한 값이면 된다
각 대학교 학생들의 학번이 겹침
But 학교별로 구분해서 A 대학교 학생들만 보면 학번이 모두 다르고, B 대학교 학생들만 보면 학번이 모두 다릅니다.
→ 학번은 학교 내에서 학생을 구분하기 위한 일종의 key이기 때문에 속한 집합 내에서만 고유한 값이면 됩니다.
두 개의 NumberList 간의 아이템들의 key 값이 중복됨
각 리스트 내에서 아이템을 구분하기 위한 용도이기 때문에 속한 리스트 내에서만 고유하면 됨!
Key로 값을 사용하는 경우
const numbers = [1, 2, 3, 4, 5];
const listItems = number.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
numbers 배열의 숫자들이 중복되지 않는 경우에는 정상적으로 작동하지만,
numbers 배열에 중복된 숫자가 들어있다면 key값이 중복되기 때문에 고유해야한다는 key값의 조건이 충족되지 않습니다.
(실행해보면 key값이 중복된다는 경고메세지가 출력)
Key로 id을 사용하는 경우
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Key로 index를 사용하는 경우
const todoItems = todos.map((todo, index) =>
// 아이템들의 고유한 ID가 없을 경우에만 사용해야 함
<li key={index}>
{todo.text}
</li>
);
: map() 함수에서 두 번째 파라미터로 제공해주는 index값을 key로 사용
index는 배열 내에서 현재 아이템의 인덱스를 의미 (고유한 값이므로 key값으로 사용해도 됨)
배열에서 아이템의 순서가 바뀔 수 있는 경우에는 성능에 부정적인 영향을 끼칠 수 있고, 컴포넌트의 state와 관련하여 문제를 일으킬 수 있기 때문에 사용 x
리액트에서는 key를 명시적으로 넣어주지 않으면 기본적으로 index값을 key값으로 사용합니다.
map() 함수 안에 있는 Elements는 꼭 key가 필요하다!
여러 개의 컴포넌트 렌더링하기
리액트에서는 배열과 키를 사용하여 반복되는 여러 개의 컴포넌트들을 쉽게 렌더링 할 수 있습니다.
(반복되는 다수의 element들이 렌더링되는 것)
▲ 에어비엔비 첫 화면
A 컴포넌트와 B 컴포넌트가 반복적으로 나오는 것을 볼 수 있습니다.
이렇게 같은 컴포넌트를 화면에 반복적으로 나타내야 할 경우에 이를 코드 상에 하나하나씩 직접 넣는 것은 매우 비효율적입니다.
- 같은 코드가 계속해서 반복되기 때문
- 동적으로 화면의 내용이 바뀌는 경우 코드를 직접 하나씩 넣는 방식으로는 구현하기 까다로움
map() 함수
mapping: 한 쪽의 아이템과 다른 한 쪽의 아이템을 짝지어준다는 의미
map() 함수 → 배열에 들어있는 각 변수에 어떤 처리를 한 뒤 리턴
const doubled = numbers.map((number) => number * 2);
▲ map() 함수 사용 예제 코드
map() 함수를 사용하여 numbers 배열에 들어있는 각 숫자에 2를 곱한 값이 들어난 doubled라는 배열을 생성하는 코드
map() 함수: 배열의 첫 번째 아이템부터 순서대로 각 아이템에 어떠한 연산을 수행한 뒤에 최종 결과를 배열로 만들어서 리턴
리액트에서 엘리먼트를 랜더링할 때 map() 함수를 이용하는 방법
const numbers = [1, 2, 3, 4, 5];
const listItems = number.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
▲ 리액트에서 map() 함수를 사용하여 element를 렌더링하는 코드
숫자 1부터 5까지 들어있는 numbers라는 배열이 있고,
map() 함수를 사용해서 배열에 들어있는 각 숫자를 li 태그로 감싸 리턴하고 있습니다.
jsx에서는 중괄호를 사용하면 JavaScript 코드를 넣을 수 있기 때문에 <li>{number}</li>처럼 표현하면 각 숫자의 값이 태그 안에 들어가게 됩니다.
이렇게 리턴된 listItems 배열은 5개의 element를 갖고있게 됩니다.
그리고 이를 화면에 렌더링하기 위해서 ReactDOM의 render함수를 사용합니다.
결과적으로 li 태그가 들어있는 listItems 배열을 ul 태그로 감싸서 렌더링하게 됩니다.
ReactDOM.render(
<ul>
<li>{1}</li>
<li>{2}</li>
<li>{3}</li>
<li>{4}</li>
<li>{5}</li>
</ul>,
document.getElementById('root')
);
▲ 최종적으로 렌더링되는 코드
▲ 렌더링 결과
1에서 5까지의 숫자가 글머리 기호와 함께 있는 목록으로 출력됩니다.
기본적인 ListComponent
앞에서 작성한 코드를 기본적인 형태를 가진 별도의 ListComponent로 분리
function NumberList(props) {
const { numbers } = props;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
▲ 숫자 목록을 출력하는 NumberList 컴포넌트
NumberList 컴포넌트는 props로 숫자가 들어있는 배열인 numbers를 받아서 이를 목록으로 출력합니다.
NumberList 컴포넌트를 사용하면 numbers의 숫자가 수십개, 수백개가 되어도 별도의 코드를 작성할 필요없이 화면에 렌더링할 수 있습니다.
이 코드를 실행해보면 개발자 도구의 콘솔 탭에 경고 문구가 나옴
Each child in a list should have a unique "key" prop
리스트의 각 아이템은 무조건 고유한 키를 가지고 있어야 한다
→ 현재 각 아이템의 키가 없기 때문에 경고문이 출력되는 것!