💻 Frontend/React

React - 다수의 객체로 이루어진 배열 데이터 다루기

hjinn0813 2024. 4. 9. 10:18
728x90

최근 SeSAC 교육과정 중에 React를 배우고 있는데, 다수의 객체로 이루어진 배열 데이터를 다루는 부분이 조금 복잡해서 따로 포스팅을 하게 됐다. 일단 수업 시간에 가장 먼저 실험해봤던 예제는 각종 상품에 대한 정보가 담겨있는 객체들로 이루어진 배열 데이터였는데, 코드를 살펴보자.

// prodItem.jsx
export default function ProdItem({ prodData }) {
  return (
    <div className="prod-container" key={prodData.id}>
      <div>제품명: {prodData.name}</div>
      <div>가격: {prodData.price}원</div>
    </div>
  );
}
  • ProdItem은 상품을 하나씩 보여줄 함수 컴포넌트이고, 인자로 들어가는 props는 구조분해할당된 prodData.
  • return 내부에 부모 태그에서 key 속성으로 상품의 id값을 받아와서 보여줄거라고 명시하고, 자식 태그에 상품별로 보여줄 내용(상품 이름과 가격)을 입력한다.
  • key 속성은 기본 요소와 업데이트 요소를 비교할 때 사용하는 속성으로, 다른 요소와 겹쳐지지 않는 고유한 값을 가지고 있어야 한다. 만약 배열의 요소 중 고유한 값(id)이 없다면 "최후의 수단" 으로 index를 사용한다.
    (map 메서드로 배열을 순회할 때 반드시 key를 사용해야 한다.)

// prodContainer.jsx
import ProdItem from './ProdItem';

export default function ProdContainer({ products }) {
  return (
    <div>
      {/* ? : 데이터가 없으면 map 돌지 않도록 하는 기능 */}
      {products?.map((prod) => {
        return <ProdItem prodData={prod} key={prod.id} />;
      })}
    </div>
  );
}
  • ProdContainer는 상품 목록을 보여줄 함수 컴포넌트.
  • 상품 하나씩 보여주는 ProdItem 컴포넌트를 import해서 사용.
  • 인자로 들어가는 props는 구조분해할당된 products.
  • return에서 ProdItem 컴포넌트 불러오면서 id값을 통해 상품을 보여줄거라고 명시하고, map 메소드 사용해서 상품 하나씩 돌아가며 출력한다.
물음표(?.)는 ES11에서 도입된 '옵셔널 체이닝(Optional Chaining)'이라는 기능으로, 물음표 앞의 평가대상(데이터)이 없으면 해당 메서드가 작동하지 않도록 한다. 위 코드에서는 products라는 props가 전달되었다면 map 메서드로 products 배열을 순회하며 배열의 길이만큼 아이템을 브라우저에 보여주지만, products라는 props가 전달되지 않았다면 map 메소드 자체가 동작하지 않아서 error도 발생하지 않는다.
★ map 메소드 내부에 콜백함수 중괄호 사용했으면 return 키워드를 반드시 작성해야 한다.

※ 옵셔널 체이닝 설명 - https://ko.javascript.info/optional-chaining

// App.js
import ProdItem from './components/ProdItem';
import prodContainer from './components/ProdContainer';

function App() {
  const products = [
    {
      id: 1,
      name: '노트북',
      price: 1000000,
    },
    {
      id: 2,
      name: '스마트폰',
      price: 800000,
    },
    {
      id: 3,
      name: '유선키보드',
      price: 20000,
    }
  ]
  return (
    <div className="App">
      {/* map 메소드로 순회하면서 찍어줌 */}
      {products.map((prod) => (
        <ProdItem prodData={prod} key={prod.id} />
      ))}

      {/* map 메소드 어려운 버전 */}
      <prodContainer products={products} />
    </div>
  );
}

export default App;
  • App.js에서 ProdItem, ProdContainer 컴포넌트부터 import한다.
  • return 위에 products 배열 선언. (원래 10개인데 너무 길어서 3개만 가져옴)
  • return에서 컴포넌트들(ProdItem, ProdContainer) 불러와서 상품을 어떻게 보여줄지 작성한다.

다음으로 실험해봤던 예제는 React의 props, state 기능을 전부 사용해서 유저의 데이터가 담긴 객체 배열을 불러오는 코드였는데, 약간 복잡해서 한번에 이해하지 못하고 추가 설명을 듣고서야 이해했다. 일단 코드를 살펴보자.

// CharInfo.jsx
export default function CharInfo({ charData }) {
  return (
    <>
      <div>Name: {charData.name}</div>
      <div>Age: {charData.age}</div>
      <div>NickName: {charData.nickName}</div>
    </>
  );
}
  • CharInfo는 유저 정보를 하나씩 보여줄 함수형 컴포넌트.
  • 매개변수로 들어오는 props는 구조분해할당된 charData를 받아온다.
  • return 내부에 이름, 나이, 닉네임 등 유저마다 보여줄 정보를 입력한다.

// CharContainer.jsx
import { useState } from 'react';
import CharInfo from './CharInfo';

export default function CharactorsContainer() {
  // 사용자의 데이터가 담긴 객체의 배열 = 초기값
  const [characters, setCharacters] = useState([
    {
      id: 1,
      name: '뽀로로',
      age: '7',
      nickName: '사고뭉치',
    },
    {
      id: 2,
      name: '루피',
      age: '5',
      nickName: '공주님',
    },
    {
      id: 3,
      name: '크롱',
      age: '4',
      nickName: '장난꾸러기',
    },
  ]);

  // selectedCharacter state 선언
  const [selectedCharacteor, setSelectedCharactor] = useState(null);

  // handleClick 함수 (인자: 선택된 버튼의 name 값)
  const handleClick = (name) => {
    const selected = characters.find((charactor) => charactor.name === name);
    setSelectedCharactor(selected);
  };

  return (
    <div>
      {characters.map((charactor) => (
        <button key={charactor.id} onClick={() => handleClick(charactor.name)}>
          {charactor.name}
        </button>
      ))}

      {/* 선택된 캐릭터 정보를 props로 전달하기 */}
      {selectedCharacteor && (<CharInfo charData={selectedCharacteor} />)}
    </div>
  );
}
  • CharContainer는 버튼을 클릭할 때마다 다른 유저의 정보를 보여줄(유저 목록을 갖고 있는) 함수형 컴포넌트.
  • useState 기능과 CharInfo 컴포넌트를 사용하기 위해 import 먼저 진행한다.
  • 현재 상태(characters)와 변화될 상태(setCharacters)를 구조분해할당하여 useState라는 변수에 담았고, 유저 정보가 담긴 객체의 배열을 초기값으로 넣었다.
  • 버튼을 클릭하면 선택된 유저의 정보를 보여주기 위한 state를 선언한다.
    여기서도 '선택된 상태(selectedCharacteor)'와 '선택되면 변화할 상태(setSelectedCharactor)'를 바로 구조분해할당하여 useState에 담았다. 여기서 초기값으로 null을 주는 이유는, 선택된 유저가 없는 경우에 charInfo 컴포넌트가 실행되지 않아야하기 때문이다. (이걸 '조건부 렌더링'이라고 한다.)
  • 버튼으로 유저의 정보를 보여줄 예정이니까, onClick으로 연결할 handleClick 함수를 만들고, 인자는 선택된 버튼의 name 값을 받아온다. 선택되면 characters 배열에서 find 메서드로 유저의 이름을 찾아서, 배열에서 찾아낸 유저의 이름과 선택된 버튼의 이름이 일치하면, 해당 결과를 '선택되면 변화할 상태(setSelectedCharactor)'에 저장한다.
  • 브라우저에 보여질 부분을 return 내부에 작성한다. characters 배열을 순회하며 보여지는 버튼들은 유저의 id값을 통해 불려와서 출력되고, 앞서 만든 handleClick 함수를 onClick으로 연결한다.
  • 마지막으로, 버튼 클릭시 해당하는 유저의 정보가 출력되어야 하니까 선택된 유저의 정보를 props로 전달한다. and 연산자(&&)로 '선택된 상태(selectedCharacteor)'가 없으면 CharInfo 컴포넌트가 실행되지 않도록 한다. 여기서 가장 첫 줄에 import했던 CharInfo 컴포넌트를 불러오는데, key 속성명과 props가 같아야 오류가 없다.

// App.js
import CharContainer from './components/CharContainer';

function App() {
  return (
    <div className="App">
      <CharContainer />
    </div>
  );
}

export default App;

CharInfo는 CharContainer 컴포넌트에 넣어뒀기 때문에, CharContainer 컴포넌트만 App.js로 import하여 사용한다.


여기까지가 어제 수업에서 실험했던, React를 이용하여 다수의 객체로 이루어진 배열 데이터를 다루는 방법이었다.

어제 수업내용을 복습하면서 리뷰에서도 잠깐 언급했는데, 이번 파트에서는 코드 하나 작성하려면 생각을 너무 오랫동안 해야하니까 자신감이 약간 떨어졌다. 지금보다 더 빨리 이해하고 싶고 훨씬 잘하고 싶은 욕심 때문인지, 코드 작성이나 이해에 시간이 많이 걸리면 스스로의 실력이 너무 부족하다는게 느껴져서 자괴감이 든다. 한번도 접해보지 않은걸 이제 배우기 시작한거라 주눅들거나 의기소침해질 필요가 없는데 왜 자꾸 그런 마음이 드는지 모르겠다.😢 그래도 어제 수업시간에 리더님의 추가설명을 듣고, 지금 코드 하나씩 읽으면서 내가 자주 쓰는 단어로 설명해보니까 이제 80~90% 정도는 이해된 것 같다.🙂

부족하다고 자책하지 말고, 부족하게 느껴진 부분들을 잘하는 것으로 바꾸려고 해보자.🔥

728x90