리액트 기본 배우기 -리스트 & 키 -

1. 리스트와 키

 

컴퓨터 프로그래밍에서 "같은 아이템을 순서대로 모아놓은 것이 리스트"

 

리스트를 위해 사용하는 자료구조가 바로 배열(array)

 

배열은 자바스크립트의 변수나 객체를 하나의 변수로 묶어놓은 것

 

const numbers = [1,2,3,4,5];

 

key는 열쇠라는 뜻

 

열쇠는 모두 다 모양이 다른 고유한 형태

 

이와 같은 맥락으로 컴퓨터 프로그래밍에서는 key는 각 객체나 아이템을 구분할 수 있는 고유한 값

 

리액트에서는 이런 배열과 키를 사용해서 반복되는 다수의 엘리먼트를 쉽게 렌더링 할 수 있다.

 

 

2. 여러 개의 컴포넌트 렌더링

 

아래 그림에서 A,B 컴포넌트가 반복적으로 나오고 있다.

 

 

당연하지만 같은 컴포넌트를 화면에 반복적으로 나타내야 할 경우에,

 

코드 상에 하나씩 직접 넣는 것은 같은 코드가 반복되기 때문에 굉장히 비효율적이다.

 

또한 동적으로 화면의 내용이 바뀌는 경우에 코드를 직접 하나씩 넣는 방식으로는 구현하기가 까다롭다

 

 

2-1) map

 

이런 경우에 사용하는 것이 자바스크립트 배열의 map()함수

 

map() 함수는 배열에 들어있는 각 변수에 어떤 처리를 한 뒤 리턴해준다.

 

const doubled = numbers.map((number) => number*2);

 

위 코드는 map() 함수를 사용해서 numbers 배열에 들어있는 각 숫자에 2를 곱한 값이 들어있는 doubled라는 배열을 생성하는 코드

 

이처럼 map()은 배열의 첫번째 아이템부터 순서대로 각 아이템에 어떠한 연산을 수행한 뒤에 최종 결과를 배열로 만들어서 리턴해준다

 

 

2-2) 리액트에서 map

 

const numbers = [1,2,3,4,5]
const listItems = numbers.map((number) => 
    <li>{number}</li>
);

 

위 코드는 숫자 1부터 5까지 들어있는 numbers라는 배열이 있고, map() 함수를 사용해서

 

이 배열에 들어있는 각 숫자를 <li>태그로 감싸 리턴하고 있다.

 

JSX에서는 중괄호를 사용해서 자바스크립트 코드를 넣을 수 있다.

 

이렇게 하면 각 숫자의 값이 <li> 태그 안의 {numbers}에 들어가게 된다.

 

이렇게 리턴된 listItems 배열은 총 5개의 엘리먼트를 갖고 있다

 

이것을 화면에 렌더링할려면...

 

ReactDOM.render(
    <ul>{listItems}</ul>
    document.getElementById('root')
);

 

결과적으로 <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')
);

 

그러면 다음과 같이 렌더링 될 것이다..

 

 

3. 기본적인 리스트 컴포넌트

 

다음은 숫자 목록을 출력하는 NumberList 컴포넌트

 

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 컴포넌트는 props로 숫자가 들어있는 배열인 numbers를 받아서 이를 목록으로 출력

 

이 NumberList 컴포넌트를 사용하면 numbers 배열에 숫자가 수십개 또는 수백개가 되어도 별도의 코드를 작성할 필요 없이 화면에 렌더링할 수가 있다

 

그러나 이 코드를 실행해보면 다음과 같이 개발자 도구의 콘솔 탭에 리스트 아이템에는 무조건 키가 있어야 한다는 경고 문구가 나옴

 

 

이 경고문이 나오는 이유는 당연히 현재 아이템에 키가 없기 때문이다

 

 

4. 리스트의 키

 

키가 갖고 있는 특징은 "고유하다"는 것

 

마찬가지로 리액트에서의 키는 "리스트에서 아이템을 구분하기 위한 고유 문자열"

 

리스트에서 어떤 아이템이 변경, 추가 또는 제거되었는지 구분하기 위해 사용

 

일상생활에서 사용하는 키로는 주민번호, 학번, 핸드폰 번호, 여권번호 등이 있다.

 

모두 다 고유한 값이라는 특징

 

하지만 고유하다는 특징은 범위가 한정되어 있다

 

 

위 그림은 A대학교와 B대학교 학생들의 학번을 나타냄

 

각 대학교 학생들의 학번이 겹치는 것을 알 수 있다.

 

하지만 A대학교 학생들만 보면 학번이 모두 다르며, B대학교 학생들만 보면 역시 학번이 모두 다르다.

 

학번은 특정 대학교 내에서 학생을 구분하기 위한 일종의 키이기 때문에, 속한 집합 내에서만 고유값이면 된다.

 

리액트에서는 어떻게 적용될까?

 

리액트에서의 키 값은 리스트에 있는 엘리먼트 사이에서만 고유한 값이면 된다.

 

 

위 그림은 NumberList를 나타낸 것

 

각 리스트의 아이템에는 키가 들어있다.

 

위에 나왔던 학번 예와 마찬가지로, 2개의 numberlist 간에 아이템들의 key값은 중복되는 것을 볼 수 있다.

 

하지만 각 리스트 내에서 아이템을 구분하기 위한 용도이므로, 리스트 내에서만 고유하면 충분하다.

 

 

5. 키 값 사용하기

 

const numbers = [1,2,3,4,5]
const listItems = numbers.map((number) =>
<li key={number.toString()}>
    {number}
</li>)

 

 

위 코드는 키 값으로 숫자의 값을 사용한 것이다.

 

이렇게 하면 지금처럼 numbers 배열의 숫자들이 중복되지 않는 경우에는 정상적으로 작동하나,

 

만약 numbers 배열에 중복된 숫자가 들어있다면, 키 값도 중복되므로 고유해야한다는 조건이 충족되지 않는다.

 

또 다른 방법으로 id를 사용하는 방식이 있다.

 

id의 의미 자체가 고유한 값이라는 것으로,

 

만약 id가 존재한다면, 키값으로 사용하기에 보통 가장 적절하다.

 

const todoItems = todos.map((todo) =>
<li key={todo.id}>
    {todo.text}
</li>)

 

 

또한 키값으로 인덱스를 사용하는 방법도 있다.

 

이 방법은 map함수에서 두번째 파라미터로 제공해주는 인덱스 값을 키값으로 사용하는 것이다.

 

이 인덱스는 배열 내에서 현재 아이템의 인덱스를 의미한다.

 

인덱스 값도 고유한 값이므로, 키값으로 사용가능하다.

 

하지만 배열에서 아이템의 순서가 바뀔 수 있는 경우에는 키값으로 인덱스를 사용하는 것을 권장하지 않는다.

 

성능에 부정적인 영향을 끼칠 수 있고, 컴포넌트의 state와 관련하여 문제를 일으킬 수도 있기 때문이다.

 

따라서 인덱스를 키 값으로 사용하는 것은 아이템들의 고유한 id가 없을 경우에 사용하는 것이 좋다.

 

리액트에서는 키를 명시적으로 넣어주지 않으면 기본적으로 이 인덱스를 키 값으로 사용한다고 한다.

 

const todoItems = todos.maps((todo, index) =>
//아이템들의 고유한 id가 없을 경우에만 사용
<li key={index}>
    {todo.text}
</li>)

 

 

여기서 기억해야할 점은 map()함수 안에 있는 엘리먼트는 꼭 키가 필요하다는 것이다.

 

초보 리액트 개발자가 실수하는 부분이 map()함수를 사용하면서 엘리먼트에 키 값을 넣지 않는 경우가 많다.

 

 

6. 실습으로 따라해보기

 

$npx create-react-app으로 프로젝트 생성

 

src 폴더 내에 chapter_10이라는 이름의 폴더 하나 생성

 

AttendanceBook.jsx라는 이름의 파일을 새로 만들고, AttendanceBook이라는 이름의 컴포넌트 생성

 

import React from 'react';

const students = [
    {
        name:'daehyuck'
    },
    {
        name:'taeyeon'
    },
    {
        name:'suzy'
    },
    {
        name:'Jeff'
    }
]

function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student) =>{
                return <li>{student.name}</li>
            })}
        </ul>
    )
}

export default AttendanceBook

 

 

AttendanceBook 컴포넌트는 students라는 배열로부터 학생 정보가 담긴 객체를 받아서 학생들의 이름을 목록 형태로 출력하는 컴포넌트

 

여기서 배열을 렌더링하기 위해 map() 함수 사용

 

이제 만든 AttendanceBook 컴포넌트를 실제로 화면에 렌더링하기 위해서 index.js 파일을 수정

 

import AttendanceBook from './chapter_10/AttendanceBook';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <AttendanceBook />
  </React.StrictMode>
);

 

npm start로 실행

 

 

출력은 잘 되는것 같은데... 콘솔탭 보면 빨간색 경고문구창이 나오고

 

리스트에 키가 빠져있어서 그렇다고함

 

이를 위해 리스트 객체에 id를 추가하고, 렌더링시 key를 추가

 

import React from 'react';

const students = [
    {
        id:1,
        name:'daehyuck'
    },
    {
        id:2,
        name:'taeyeon'
    },
    {
        id:3,
        name:'suzy'
    },
    {
        id:4,
        name:'IU'
    }
]

function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student) =>{
                return <li key={student.id}>{student.name}</li>
            })}
        </ul>
    )
}

export default AttendanceBook

 

 

이러면 경고문구창이 사라짐

 

 

다음과 같이 여러 방식으로 key값을 사용 가능함

 

//포맷팅된 문자열을 키값으로
function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student,index) =>{
                return <li key={`student-id-${student.id}`}>{student.name}</li>
            })}
        </ul>
    )
}

export default AttendanceBook

//인덱스를 키값으로
function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student,index) =>{
                return <li key={index}>{student.name}</li>
            })}
        </ul>
    )
}

export default AttendanceBook
TAGS.

Comments