🚀 프로젝트/리그 오브 레전드 정보 앱

리그 오브 레전드 정보 앱 | 트러블 슈팅 (3): TypeError: Cannot read properties of undefined (reading 'attack') 해결 방법

seheej 2024. 10. 10. 20:45

개발 중에 만난 오류 중 하나로, 챔피언 상세 페이지를 구현할 때 발생한 TypeError: Cannot read properties of undefined (reading 'attack') 오류에 대해 다뤄보겠습니다. 이 문제는 championDetail.stats가 undefined 상태일 때 발생합니다.

 

오류 상황

src/app/champions/[id]/page.tsx (39:40) @ attack
⨯ TypeError: Cannot read properties of undefined (reading 'attack')
    at ChampionDetailPage (./src/app/champions/[id]/page.tsx:72:50)

 

위 오류는 championDetail.stats.attack을 읽으려고 할 때 발생했으며, 이는 championDetail 객체가 예상한 구조로 데이터를 제공하지 않았음을 의미합니다.

문제 원인

이 오류의 원인은 다음과 같습니다:

  • championDetail 객체에 기대했던 데이터가 존재하지 않음.
  • API 응답이 올바르지 않거나, 아직 데이터를 받지 못한 상태에서 페이지가 렌더링됨

수정 전 코드

오류가 발생한 코드는 아래와 같습니다.

import React from "react";
import { getChampionDetail } from "@/utils/serverApi";

const ChampionDetailPage = async ({ params }: { params: { id: string } }) => {
  const championDetail = await getChampionDetail(params.id);
 
  const championKey = Object.keys(championDetail.data)[0];
  const abilities = championDetail.data[championKey].spells;
  console.log("championDetail = >>>>", championDetail.data[championKey]);

  return (
    <div>
      <h1>{championDetail.data[championKey].name}</h1>
      <p>{championDetail.data[championKey].title}</p>
      <p>{championDetail.data[championKey].lore}</p>
      <h2>Abilities:</h2>
      <ul>
        <li>공격력: {championDetail.stats.attack}</li>
        <li>방어력: {championDetail.stats.defense}</li>
        <li>마법력: {championDetail.stats.magic}</li>
        <li>난이도: {championDetail.stats.difficulty}</li>
      </ul>
    </div>
  );
};

export default ChampionDetailPage;
 

문제 해결 과정

이 코드에서 championDetail.stats가 정의되지 않은 경우를 처리하지 않았기 때문에 문제가 발생했습니다. 데이터를 안전하게 처리하기 위해 const champion = championDetail.data[championKey]; 라는 변수를 추가했습니다.

champion 변수의 역할

const champion = championDetail.data[championKey]; 부분은 API 응답에서 championDetail.data 객체 내 특정 챔피언의 정보를 가져오는 중요한 역할을 합니다. 이 코드는 다음과 같은 이유로 추가되었습니다:

  1. API 응답 구조 파악: championDetail.data는 여러 챔피언 정보를 담고 있을 수 있습니다. 그래서 Object.keys(championDetail.data)[0]을 사용하여 데이터 내 첫 번째 키를 추출합니다. 이 키는 챔피언의 고유 ID에 해당하며, 이를 통해 해당 챔피언의 상세 데이터를 얻습니다.
  2. 가독성 향상: championDetail.data[championKey]를 계속해서 사용하면 코드가 길어지기 때문에, 이를 champion이라는 변수로 할당하여 가독성을 높였습니다. 이렇게 하면 데이터를 보다 명확하고 깔끔하게 다룰 수 있습니다.

수정 후 코드

수정된 코드는 데이터가 제대로 로드되지 않았을 때 예외 처리를 추가하여 문제를 방지했습니다.

 
import React from "react";
import { getChampionDetail } from "@/utils/serverApi";

const ChampionDetailPage = async ({ params }: { params: { id: string } }) => {
  const championDetail = await getChampionDetail(params.id);

  const championKey = Object.keys(championDetail.data)[0];
  const champion = championDetail.data[championKey]; // 챔피언 데이터를 가져옵니다.

  // 챔피언 또는 stats가 없을 때 예외 처리 추가
  if (!champion || !champion.stats) {
    return <div>챔피언 정보를 불러오는 중 오류가 발생했습니다.</div>;
  }

  return (
    <div>
      <h1>{champion.name}</h1>
      <p>{champion.title}</p>
      <p>{champion.lore}</p>
      <h2>Abilities:</h2>
      <ul>
        <li>공격력: {champion.stats.attack}</li>
        <li>방어력: {champion.stats.defense}</li>
        <li>마법력: {champion.stats.magic}</li>
        <li>난이도: {champion.stats.difficulty}</li>
      </ul>
    </div>
  );
};

export default ChampionDetailPage;

결론

이와 같이 champion 변수를 도입하여 가독성을 높이고, 예외 처리를 추가함으로써 데이터 로드 과정에서 발생할 수 있는 오류를 예방했습니다. API 응답의 구조를 이해하고, 안전한 데이터 접근을 통해 실시간 데이터 처리의 안정성을 확보할 수 있었습니다.

 

배운 점

이번 문제를 해결하면서 몇 가지 중요한 점을 배웠습니다.

  1. API 데이터 처리의 중요성: API에서 받아오는 데이터는 항상 일관적이지 않을 수 있다는 점을 다시 한 번 깨달았습니다. 데이터가 없거나 예상과 다르게 들어오는 상황을 고려하여 예외 처리를 해주는 것이 매우 중요합니다. 이를 통해 예상치 못한 에러를 방지할 수 있습니다.
  2. 가독성 높은 코드 작성: championDetail.data[championKey]를 매번 사용하기보다는 champion이라는 변수를 만들어 코드의 가독성을 크게 향상시켰습니다. 특히, 데이터를 여러 번 접근할 때 간결하고 직관적인 코드를 유지하는 것이 개발 과정에서 중요한 요소임을 다시 한 번 배웠습니다.
  3. 방어적인 코딩: 데이터가 없을 때 발생하는 문제를 방지하기 위한 방어적 코딩의 중요성을 배웠습니다. 예외 상황에 대비한 방어적 코딩이 없으면 예상치 못한 오류가 발생할 가능성이 커집니다. if (!champion || !champion.stats)와 같은 체크를 통해 데이터가 완전하지 않을 경우에 대한 대응을 할 수 있게 되었습니다.