본문 바로가기

React

React 시작하기 - 08 - 리스트 반복문으로 엘리먼트 만들기, key 개념

리스트 안에 있는 요소들을 하나씩 가지고와서 리액트 엘리먼트를 만들 수 있다.

리액트라고 해서 전혀 새로운 문법을 사용하지는 않는다.

우리가 기존에 JavaScript에서 쓰던 작동 방식을 거의 그대로 사용한다.

forEach문 같은 느낌이다.

또 반복문을 돌리면서 생성된 리액트 엘리먼트들을 하나하나 식별할 수 있도록 유니크한 값을 만들어 줘야 하는데, 그것이 key다.

아래에서 자세히 알아보자.

<body>
    
    <div id="root1">
    </div>
    <div id="border1"></div>
    
    <div id="root2">
    </div>
    <div id="border2"></div>

    <div id="root3">
    </div>
    <div id="border3"></div>

    <div id="root4">
    </div>
    <div id="border4"></div>


    <div id="root5">
    </div>
    <div id="border5"></div>



    <!-- react cdn -->
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <!-- react dom cdn -->
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <!-- babel cdn -->
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

    <!-- define type as a babel  -->
    <script type="text/babel">
        class MakeBorder extends React.Component {
            render() {
                return <p>--------------------------------root{this.props.num}----------------------------</p>;
            }
        }

        // JavaScript 에서 리스트를 다루는 map 함수
        // 리액트는 리스트를 바탕으로 리액트 엘리먼트들을 만들 때 JavaScript의 map() 함수를 활용한다.
        // 따라서 먼저 JavaScript에서 map() 함수를 어떤 식으로 쓰는지 알아보자.
        const numBefore = [1, 2, 3, 4, 5, 6];
        const numAfter = numBefore.map((item)=>{ return item * 2});
        console.log(numAfter);  // => [2, 4, 6, 8, 10, 12]
        // Array.prototype.map(function(item, index, arr){ return obj; }) 
        // 배열 객체에 멤버 메소드 map() 을 실행하고, 그 안에 콜백 함수를 파라미터로 넣는다.
        // 그러면 해당 배열 객체로부터 요소들을 하나씩 꺼내와서 콜백 함수 안에 넣고 함수를 실행한다.
        // 콜백 함수는 최소 한 개부터 최대 세 개의 파라미터를 받는다.
        // 콜백 함수가 받는 파라미터 세 개는 아래와 같은 순서다.
        // (배열 객체로부터 꺼내온 요소 하나), (그 요소가 배열 객체 안에서 갖는 인덱스), (현재 함수를 실행하고 있는 배열 객체)
        // map 함수는 배열을 하나 반환한다.
        // 그 배열 안에 있는 요소들은 콜백 함수가 반환했던 값들이 순서대로 들어 있다.

        
        // 컴포넌트 여러 개를 한 번에 렌더링하기
        // 리액트가 배열을 가지고 엘리먼트들의 리스트를 만드는 방법도 비슷하다.
        // map() 함수를 실행하면서 파라미터로 콜백 함수를 넣어준다고 했다.
        // 그리고 파라미터로 넣어줬던 콜백 함수가 반환했던 값들이 순서대로 들어 있는 새로운 배열을 마지막으로 되돌려준다고 했다.
        // 그러면 map() 함수 안에 넣어주는 콜백 함수가 리액트 엘리먼트를 반환하도록 만든다면 어떻게 될까.
        // 리액트 엘리먼트들을 요소로 갖는 배열이 만들어지게 된다.
        // 중괄호 {} 안에 리액트 엘리먼트 하나를 넣으면 그 요소 하나가 출력된다.
        // 또한 중괄호 {} 안에  리액트 엘리먼트들이 들어 있는 배열을 넣으면 그 배열 안에 있는 리액트 엘리먼트들이 하나씩 출력된다.
        const colors = ['red', 'yellow', 'blue', 'green'];
        const colorList = colors.map( (item) => {
                    return <li key={item}> {item} </li>
                });
        const ele1 = <p> Hello World </p>;
        ReactDOM.render( <div>
                            <ul>{colorList}</ul>
                            {ele1}
                            {<div> Hello React!! </div>}
                        </div>, 
                                document.getElementById('root1') );
        ReactDOM.render( <MakeBorder num="1" />, document.getElementById('border1') );



        // 기본적인 리스트 컴포넌트 만들어 보기
        // 앞에서 배열을 가지고 리액트 엘리먼트들이 들어 있는 리스트를 만들어 봤다.
        // 이번에는 배열을 속성으로 넘겨주면 그 배열들로 만든 리액트 엘리먼트 리스트를 출력하는 컴포넌트를 만들어 보자.
        function MovieList(props) {
            const movies = props.movies;
            const itemList = movies.map( (item, index)=>{ return <li key={index}>{item}</li>;} );

            return ( <ul>{itemList}</ul> );
        }
        
        const movieList = ['Guardians of the Galaxy', 'The Avengers', 'The Antman', 'Iron Man'];
        ReactDOM.render(<MovieList movies={movieList} />, document.getElementById('root2'));
        ReactDOM.render( <MakeBorder num="2" />, document.getElementById('border2') );


        // key 속성
        // 엘리먼트 리스트를 만들 때는 key 속성을 넣어줘야 한다.
        // key 속성은 리액트 엘리먼트를 고유하게 식별할 수 있도록 돕는 값이다.
        // 보통은 item의 id값을 key값으로 사용한다.
        // 안정적인 id값이 없다면 최후의 수단으로 index를 사용한다.
        // 항목의 순서가 바뀌면 index도 함께 바뀌기 때문에 안정적이지 않으므로 권장하지 않는다.
        // const todoItemsID = todos.map( (todo) =>
        //     <li key={todo.id}>
        //         {todo.text}
        //     </li>
        //     );
        // Only do this below if items have no stable IDs
        // const todoItemsIndex = todos.map( (todo, index) =>
        //     <li key={index}>
        //         {todo.text}
        //     </li>
        //     );

        
        // 컴포넌트에서 key를 넣는 위치
        // 리스트 컴포넌트 안에 있는 <li> 엘리먼트가 아니라 리스트 컴포넌트가 key를 가져야 한다.
        // 키는 주변 배열의 context에서만 의미가 있기 때문이다.
        // 아래는 틀린 예다.
        function ListItemWrong(props) {
            const value = props.value;
            return (
                // 틀렸습니다! 여기에는 key를 지정할 필요가 없습니다.
                <li key={value.toString()}>
                    {value}
                </li>
            );
        }

        function NumberList(props) {
            const numbers = props.numbers;
            const listItems = numbers.map((number) =>
                // 틀렸습니다! 여기에 key를 지정해야 합니다.
                <ListItemWrong value={number} />
            );
            return (
                <ul>
                    {listItems}
                </ul>
            );
        }

        const numbersWrong = [1, 2, 3, 4, 5];
        ReactDOM.render( <NumberList numbers={numbersWrong} />,  document.getElementById('root3') );
        ReactDOM.render( <MakeBorder num="3" />, document.getElementById('border3') );
        

        // 위는 틀린 예고, 아래는 올바른 예다.
        function ListItemRight(props) {
          // 맞습니다! 여기에는 key를 지정할 필요가 없습니다.
            return <li>{props.value}</li>;
        }
        
        function NumberList(props) {
            const numbers = props.numbers;
            const listItems = numbers.map((number) =>
            // 맞습니다! 배열 안에 key를 지정해야 합니다.
            <ListItemRight key={number.toString()} value={number} />
            );
            return (
            <ul>
                {listItems}
            </ul>
            );
        }
        
        const numbersRight = [1, 2, 3, 4, 5];
        ReactDOM.render( <NumberList numbers={numbersRight} />, document.getElementById('root4') );
        ReactDOM.render( <MakeBorder num="4" />, document.getElementById('border4') );


        // key가 고유해야 하는 범위
        // key 는 같은 층위의 요소들 사이에서만 고유하면 된다.
        // 글로벌 스코프까지 고유할 필요는 없다.
        // 예를 들어 한 배열로 리액트 엘리먼트 리스트를 여러 개 만든다면 모두 같은 값을 id로 삼아도 된다.
        function Blog(props) {
            const titleList = props.posts.map((post) =>
                    { return <li key={post.id}>
                                {post.title}
                            </li> 
                    });
            const sidebar = (
                <ul>
                    {titleList}
                </ul>
            );
            const content = props.posts.map((post) =>
                    { return <div key={post.id}>
                            <h3>{post.title}</h3>
                            <p>{post.content}</p>
                        </div>
                    });
            return (
                <div>
                    {sidebar}
                    <hr />
                    {content}
                </div>
            );
        }

        const posts = [
            {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
            {id: 2, title: 'Installation', content: 'You can install React from npm.'}
        ];
        ReactDOM.render( <Blog posts={posts} />, document.getElementById('root5') );
        ReactDOM.render( <MakeBorder num="5" />, document.getElementById('border5') );


        // 덧붙여서 - 1
        // 리액트에서 중괄호 {} 안에 자바스크립트 표현식을 넣을 수 있다고 했다.
        // 이러한 기능을 활용하여 map() 함수를 중괄호 {} 안에 넣어서 인라인으로 처리할 수도 있다.
        // function NumberList(props) {
        //     const numbers = props.numbers;
        //     return (
        //         <ul>
        //             {numbers.map((number) =>
        //                 <ListItem key={number.toString()}
        //                         value={number} />
        //             )}
        //         </ul>
        //     );
        // }

        // 덧붙여서 - 2
        // key 속성은 컴포넌트에 전달되지 않는다.
        // 어떤 값을 컴포넌트나 하위 요소에 전달해야 한다면 key 속성 외에 다른 속성을 따로 하나 더 넣어줘야 한다.
        // 예를 들어 아래 Post 컴포넌트는 props.id는 읽을 수 있지만 props.key는 읽을 수 없다.
        // const content = posts.map((post) =>
        //     <Post
        //         key={post.id}
        //         id={post.id}
        //         title={post.title} />
        //     );
    </script>
</body>

- 220530 

 

참고자료 - 

https://ko.reactjs.org/docs/lists-and-keys.html

반응형