본문 바로가기
CodeIt_Sprint/React로 데이터 다루기

(4)React로 데이터 다루기_4_json 객체 배열 데이터의 index VS 객체 배열 원소당 고유 id값, sort, 삭제 기능 복습

by 코잼민 2024. 12. 30.

● 이론0_ index를 고유한 id로 지정하여, 랜더링한다면, 어떤 문제가 존재합니까?

실습 구현 코드 : 

- ReviewList.js

 실습 구현 코드 : 

import "./ReviewList.css";


function ReviewItem({ key, item, onDelete }) {
	//2_ ReviewItem 컴포넌트에서 받은 index를
  const handleDelete = () => onDelete(item.id);
  return (
    <div key={key} className="ReviewListItem">
    	//3_ 가장 상위 태그의 key속성에 저장
      <img
        className="ReviewListItem-img"
        src={item.imgUrl}
        alt={item.title}
      ></img>
      <div>
        <h1>{item.title}</h1>
        <p>{item.rating}</p>
        <p>{formatDate(item.createdAt)}</p>
        <p>{item.content}</p>
        <button onClick={handleDelete}>삭제</button>
      </div>
      <input type="text"></input>//4_ input 태그를 추가해봤다.
    </div>
  );
}

function ReviewList({ items, onDelete }) {
  return (
    <ul>
      {items.map((item) => {
      	//1_ 배열 프로퍼티 메소드인 map() 메소드에서, json의 각 index를 ReviewItem 컴포넌트에 전달
        return <ReviewItem key={item.index} item={item} onDelete={onDelete} />;
      })}
    </ul>
  );
}

export default ReviewList;

 알아야 할 개념 :

  • json내의 고유속성 값 "id" : 배열 내 원소의 구성이 달라져도 변하지 않는 값
  • json 배열의 index 값 : 배열 내 원소의 구성이 달라지면 , => index값도 변한다.

위의 코드의 출력 결과 :

2번째 항목의 input 태그에 text를 적고, 1번째 항목을 삭제해보면, 그 다음 출력결과가 나온다.

- 삭제 후, 출력 결과 :

 결론 : 각 json 배열의 고유값은 배열의 프로퍼티로 설정해야 한다. (not index)

해결 방법 코드 :

import "./ReviewList.css";


function ReviewItem({ key, item, onDelete }) {
	//2_ ReviewItem 컴포넌트에서 받은 index를
  const handleDelete = () => onDelete(item.id);
  return (
    <div key={key} className="ReviewListItem">
    	//3_ 가장 상위 태그의 key속성에 저장
      <img
        className="ReviewListItem-img"
        src={item.imgUrl}
        alt={item.title}
      ></img>
      <div>
        <h1>{item.title}</h1>
        <p>{item.rating}</p>
        <p>{formatDate(item.createdAt)}</p>
        <p>{item.content}</p>
        <button onClick={handleDelete}>삭제</button>
      </div>
      <input type="text"></input>//4_ input 태그를 추가해봤다.
    </div>
  );
}

function ReviewList({ items, onDelete }) {
  return (
    <ul>
      {items.map((item) => {
      	//1_ 배열 프로퍼티 메소드인 map() 메소드에서, json의 각 id를 ReviewItem 컴포넌트에 전달
        return <ReviewItem key={item.id} item={item} onDelete={onDelete} />;
      })}
    </ul>
  );
}

export default ReviewList;

그러면 고유 key_id를 설정하는 이유 :

위의 예시에서, 삭제 후 결과만 보고 추정을 해봤을 떄, "망고" 의 원소만 삭제됐다고 생각할 수 있지만,

"포도"를 삭제하고, "망고"원소를 "포도"로 수정하여, 결과가 나올 수도 있다.

그 과정을 제대로 알기 위해서는 고유key를 통해 추적을 할 수 있다.

 

◎ 연습문제 :

실습 설명

리액트에서 배열을 렌더링할 때는 key 라는 Prop을 지정해 주어야 한다고 배웠습니다.

FoodList 컴포넌트에서 key Prop을 올바로 적용해 봅시다.

 

 정답 코드 :

※주의 : key 프로퍼티 값은 내부 태그의 속성값으로 사용하면, 오류는 뜨지 않지만, 사용 X

- FoodList.js

function FoodListItem({ item, onDelete }) {
  const { imgUrl, title, calorie, content } = item;

  const handleDeleteFood = () => onDelete(item.id);

  return (
    <div>
      <img src={imgUrl} alt={title} />
      <div>{title}</div>
      <div>{calorie}</div>
      <div>{content}</div>
      <button onClick={handleDeleteFood}>삭제</button>
    </div>
  );
}

function FoodList({ items, onDelete }) {
  return (
  //map메소드 사용할 때, item 매개인자 추가하여, 컴포넌트 프로퍼티 key에 item.id를 대입 적용하면 된다.
    <ul>
      {items.map((item) => {
        return <FoodListItem key={item.id} item={item} onDelete={onDelete} />;
      })}
    </ul>
  );
}

export default FoodList;

- 모범 FoodList.js 코드 :

function FoodList({ items, onDelete }) {
  return (
    <ul className="FoodList">
      {items.map((item) => (
        <li key={item.id}>//li태그에 key값 적용
          <FoodListItem item={item} onDelete={onDelete} />
        </li>
      ))}
    </ul>
  );
}

 

◎ 연습문제2 :

아래의 JSON 파일의 코드는 포켓몬 도감 151번까지의 포켓몬 데이터입니다.

각 데이터를 구분하는 값인 id, 포켓몬 이름을 값으로 하는 name 프로퍼티, 그리고 포켓몬의 속성인 types 프로퍼티가 있습니다.

[
  {
    "id": 1,
    "name": "이상해씨",
    "types": [
      "풀",
      "독"
    ]
  },
  {
    "id": 2,
    "name": "이상해풀",
    "types": [
      "풀",
      "독"
    ]
  },
  
  ...
  
  ]

 문제1_ [src]폴더 내 <App>, <Pocketmon> 컴포넌트를 작성해보시오.

- Pocketmon.js

//1_ 매개인자로 json의 원소 하나씩 전달받기
function Pocketmon({item}){
	
    return (<div>
    	No.{item.id} : {item.name}//2_각 배열의 고유번호와 이름 출력
    </div>);
}

export default Pocketmon;

- App.js 

import items from '../pokemon.json';
import Pocketmon from './pocketmon.js';

function App(){
	return (
    <ul>
		//★map()메소드로 렌더링 한다.
        {items.map((item)=>{
        <li key={item.id}>
        	<Pocketmon item={item} />
        </li>
        })}
	</ul>
    );
}

export default App;

 문제2_ '문제1'의 App 컴포넌트의 map을 변수로 저장하여,  작성해보기

import items from '../pokemon.json';
import Pocketmon from './pocketmon.js';

function App(){

	const renderItems = items.map((item)=>{
    	<li key={item.id}>
        	<Pocketmon items={items} />
        </li>
    });

	return (
    <ul>
		{renderItems}
	</ul>
    );
}

export default App;

 문제3_ '문제2'에서 sort 버튼 기능 적용해보기

배열 메소드의 sort 메소드를 사용하면 배열을 정렬할 수 있었죠?

이렇게 정렬한 배열을 렌더링 할 수 있었습니다.

아래 코드는 id 순서대로 / 반대로 정렬하는 예시입니다.

import { useState } from "react";
import items from "../pokemon.json";
import Pocketmon from "./Pocketmon.js";

function App() {
  const [order, setOrder] = useState(1);

  const handleIdAscSort = () => setOrder(1);
  const handleIdDscSort = () => setOrder(-1);

  const sortedItems = [...items].sort((a, b) => {
    return order * (b.id - a.id);
  });

  const renderItems = sortedItems.map((item) => {
    <li key={item.id}>
      <Pocketmon items={items} />
    </li>;
  });

  return (
    <ul>
      <button onClick={handleIdDscSort}>id 오름차순</button>
      <button onClick={handleIdAscSort}>id 내림차순</button>
      {renderItems}
    </ul>
  );
}

export default App;

 문제4_ '문제3'에서 filter프로퍼티 메소드를 이용하여, 삭제 기능 적용해보기

- App.js

import { useState } from "react";
import pocketmonitems from "../pokemon.json";
import Pocketmon from "./Pocketmon.js";

function App() {
  const [order, setOrder] = useState(1);
  const [items, setItems] = useState(pocketmonitems);

  const handleIdAscSort = () => setOrder(1);
  const handleIdDscSort = () => setOrder(-1);

  const handleDelete = (id) => {
    const deletePocketItems = items.filter((item) => item.id !== id);

    setItems(deletePocketItems);
  };

  const sortedItems = [...items].sort((a, b) => {
    return order * (b.id - a.id);
  });

  const renderItems = sortedItems.map((item) => {
    <li key={item.id}>
      <Pocketmon items={items} onDelete={handleDelete} />
    </li>;
  });

  return (
    <ul>
      <button onClick={handleIdDscSort}>id 오름차순</button>
      <button onClick={handleIdAscSort}>id 내림차순</button>
      {renderItems}
    </ul>
  );
}

export default App;

- Pocketmon.js

function Pocketmon({ item, onDelete }) {
  const handleDeletePocket = () => onDelete(item.id);

  return (
    <div>
      No.{item.id} : {item.name}
      <button onClick={handleDeletePocket}>삭제</button>
    </div>
  );
}

export default Pocketmon;