Map, ForEach에 대하여

김민성·2022년 2월 20일
4
post-thumbnail

목차

  1. Map에 관하여, React에서 활용
  2. Map의 Key 속성에 관하여
  3. Map의 Key로 Index 사용에 대해
  4. Foreach에 대해
  5. 두가지의 차이점

결국은 기본인것 같다. 기본이 완벽해야 응용도 잘하는법!

작성목적

  1. map함수를 정확하게 알고 사용하고싶어서
  2. 특히 key속성이 깊게 궁금했다
  3. forEach랑 차이도 궁금해서

1. Map 함수란?

정의는 단순하다, 배열을 받아 오름차순으로 접근 Callback함수를 적용하여 새로운 배열을 반환하는것!

Array.prototype.map ( callbackfn [ , thisArg ] )

ex)

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
//[2, 4, 6, 8, 10]

Map은 다양한 형태로 활용 되기도 한다.

1. 특정 값 꺼내기

const users = [ { name: 'YD', age: 22 }, { name: 'Bill', age: 32 }, { name: 'Andy', age: 21 }, { name: 'Roky', age: 35 }, ]; 
const ages = users.map(user => user.age); console.log(ages); // [22, 32, 21, 35]

결론적으로 users의 age만 꺼내게 된다.


2. 특정요소 재정의 하기

const users = [
    { name: 'YD', age: 22 },
    { name: 'Bill', age: 32 },
    { name: 'Andy', age: 21 },
    { name: 'Roky', age: 35 },
];

const newUsers = users.map(user => {
    if (user.name === 'YD') {
        return { ...user, age: 18 };
    }

    return { ...user };
});

console.log(newUsers);
// [{name: "YD", age: 18}, {name: "Bill", age: 32}, {name: "Andy", age: 21}, {name: "Roky", age: 35}]

1.1 React 에서의 활용

[1,2,3,4,5]라는 배열을 받아서 map을 통해 li태그에 전달하여 리스트를 만들어내는 함수이다.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

JSX 문법인 중괄호를 이용해 map()을 인라인으로 바로 처리할 수도 있다.

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}

2. Map의 Key 속성에 대해

그렇다면 React는 map함수를 돌릴때 어떻게 어떤항목을 추가/변경할지 식별할까?
Key값을 이용하여 식별한다! 보통은 data의 id 값으로 하기와 같이 배열 내부의 엘리먼트에 지정한다.

ex) 배열 내부의 엘리먼트 key 지정 예시

function ListItem(props) {
  //  여기에는 key를 지정할 필요가 없습니다.
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    //  배열 안에 key를 지정해야 합니다.
    <ListItem key={number.toString()} value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
const todoItems = todos.map((todo) =>
  <li key={todo.id}> // or <li key={index}> 
    {todo.text}
  </li>
);

key값의 속성

  1. key값은 배열안에서만 고유하면 됩니다.
    다른 배열간 map을 돌릴경우 각 배열 내에서의 key값만 고유하다면 key값이 동일해도 상관없습니다.

  2. key는 컴포넌트에 직접적으로 전달할 수 없습니다. 전달받은 데이터의 id 값으로 사용해야 한다.

const content = posts.map((post) =>
  <Post
    key={post.id}
    id={post.id}
    title={post.title} />
);
!!!!key로 전달받은 post.key는 읽을수 없으며 전달받은 id 값을 key로 사용해야한다!!!!
  1. 리액트는 리스트 항목에 명시적으로 key를 지정하지 않으면 기본적으로 index를 key로 사용한다.
    하지만 안정적인 id가 없다면 index를 사용해도 되나 권장하는 방식은 아니다.

index의 key사용을 지양해야하는 이유

결론부터 말하자면 이벤트가 발생하고 데이터가 변경되면
1. component가 Re-render된다.
2. 이때 index를 다시 mapping한다.

위의 2가지 과정에서 혼선이 발생하게 된다.

예를들어 하기의 코드를 보자

addItem: 추가버튼을 누르면 정국을 list 앞에 추가한다.
delItem: 삭제를 누르면 철수를 삭제한다.
현재 map에는 index를 Key로 사용하고 있다.

import React, {useEffect, useState} from 'react';

const Example = () => {
    
    const [list, setList] = useState([
        {name: '철수'},
        {name: '영희'},
        {name: '민수'},
    ])

    const addItem = () => {
        setList([{name: '정국'}, ...list]);
    }

    const delItem = () => {
        setList(list.filter(l => l.name != "철수"));
    }

    return (
        <>
            {/* 추가 버튼과 삭제 버튼*/}
            <input type="button" value="추가" onClick={addItem} />
            <input type="button" value="삭제" onClick={delItem} />

            <h2> Show Problem Example</h2>
            {list.map((v, index) => 
                /*  div 태그의 key로 배열의 index 사용*/
                <div key={index}> {v.name}, idx: {index} <input type="text" /> </div> 
            )}
        </>
    )
}

export default Example;

idx:0인 철수에 아래와 같이 데이터를 넣는다.

여기서 추가 버튼을 누른다면 정국이 추가되고 철수는 idx:1로 밀려나는 아래와 같은 그림이 예상된다.

하지만 실제로는 아래와 같이 작동한다.

왜이런 현상이 발생할까? 처음에 말했던 결론과 동일하다.
이벤트가 발생하고 component가 Re-rending 되면서 index가 다시 mapping 된다.
그 과정에서 정국이 idex 0 이 되면서 철수 데이터가 정국에게 mapping 된것이다.

Key값은 고유한 값으로 쓰자, Auto_increment된 Data.id가 대표적

Map과 Foreach의 차이

forEach는 배열의 각 요소마다 한번씩 콜백함수를 실행한다.
map은 배열내 각 요소에 대해 콜백함수를 실행하고 결과를 모아 새로운 배열을 반환한다.

이 함수는 하기의 3가지 인자를 가지고 호출된다.
1. currentValue(배열의 값)
2. index
3. array(현재배열)

1. forEach

const arr = [1, 2, 3, 4, 5];
const mulArr = [];

arr.forEach(num => {
  mulArr.push(num * 3);
});
console.log(mulArr); // [3, 6, 9, 12, 15]

forEach()는 밖으로 리턴값을 보내지 못한다.

let arr = [1,2,3,4,5];
let a = arr.forEach(function(value){
	return value;
});
console.log(a);   //undefined

2. map

let arr=[1,2,3,4,5];
let a = arr.map(function(value){
	return value +1;
});
console.log(a);  // [2,3,4,5,6]

둘의 가장 큰 차이는 Return값이다. 또한 forEach는 기존 Array를 변경하고 Map은 새로운 배열을 반환한다는것

출처
https://ko.reactjs.org/docs/lists-and-keys.html#gatsby-focus-wrapper 리액트 공식문서
https://medium.com/sjk5766/react-%EB%B0%B0%EC%97%B4%EC%9D%98-index%EB%A5%BC-key%EB%A1%9C-%EC%93%B0%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-3ce48b3a18fb
https://dream-frontend.tistory.com/341

profile
다양한 경험의 개발자를 꿈꾼다

1개의 댓글

comment-user-thumbnail
2022년 2월 27일

map으로 새로운 배열 리턴해준게 참 편리합니다

답글 달기