개발 중에 만난 오류 중 하나로, 챔피언 상세 페이지를 구현할 때 발생한 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 객체 내 특정 챔피언의 정보를 가져오는 중요한 역할을 합니다. 이 코드는 다음과 같은 이유로 추가되었습니다:
- API 응답 구조 파악: championDetail.data는 여러 챔피언 정보를 담고 있을 수 있습니다. 그래서 Object.keys(championDetail.data)[0]을 사용하여 데이터 내 첫 번째 키를 추출합니다. 이 키는 챔피언의 고유 ID에 해당하며, 이를 통해 해당 챔피언의 상세 데이터를 얻습니다.
- 가독성 향상: 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 응답의 구조를 이해하고, 안전한 데이터 접근을 통해 실시간 데이터 처리의 안정성을 확보할 수 있었습니다.
배운 점
이번 문제를 해결하면서 몇 가지 중요한 점을 배웠습니다.
- API 데이터 처리의 중요성: API에서 받아오는 데이터는 항상 일관적이지 않을 수 있다는 점을 다시 한 번 깨달았습니다. 데이터가 없거나 예상과 다르게 들어오는 상황을 고려하여 예외 처리를 해주는 것이 매우 중요합니다. 이를 통해 예상치 못한 에러를 방지할 수 있습니다.
- 가독성 높은 코드 작성: championDetail.data[championKey]를 매번 사용하기보다는 champion이라는 변수를 만들어 코드의 가독성을 크게 향상시켰습니다. 특히, 데이터를 여러 번 접근할 때 간결하고 직관적인 코드를 유지하는 것이 개발 과정에서 중요한 요소임을 다시 한 번 배웠습니다.
- 방어적인 코딩: 데이터가 없을 때 발생하는 문제를 방지하기 위한 방어적 코딩의 중요성을 배웠습니다. 예외 상황에 대비한 방어적 코딩이 없으면 예상치 못한 오류가 발생할 가능성이 커집니다. if (!champion || !champion.stats)와 같은 체크를 통해 데이터가 완전하지 않을 경우에 대한 대응을 할 수 있게 되었습니다.
'🚀 프로젝트 > 리그 오브 레전드 정보 앱' 카테고리의 다른 글
| 리그 오브 레전드 정보 앱 | 트러블슈팅 (4): 챔피언 정보 조회 시 Access Denied 오류 해결 (0) | 2024.10.11 |
|---|---|
| 리그 오브 레전드 정보 앱 | 트러블 슈팅 (2): TypeScript: 챔피언 데이터 인덱싱 오류 해결하기 (0) | 2024.10.08 |
| 리그 오브 레전드 정보 앱 | 트러블 슈팅 (1): Next.js next/image 컴포넌트 오류 해결 방법 (0) | 2024.10.07 |
| 리그 오브 레전드 정보 앱 | 개발 과정 (2) : 메인페이지, 레이아웃 구성 및 네비게이션 추가 (0) | 2024.10.01 |
| 리그 오브 레전드 정보 앱 | 개발 과정 (1): 시작 전 계획 및 기능 정리 (1) | 2024.09.30 |