1. 문제 발생❓
"use client";
import ChampionCard from "@/components/championCard";
import { Champion } from "@/types/Champion";
import { ChampionRotation } from "@/types/ChampionRotation";
import { getChampionRotation } from "@/utils/rioApi";
import { getChampionList, getVersion } from "@/utils/serverApi";
import { useEffect, useState } from "react";
function RotationPage() {
const [version, setVersion] = useState<string | null>(null);
const [freeChampions, setFreeChampions] = useState<Champion[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const [rotationData, championData, versionData] = await Promise.all([
getChampionRotation(),
getChampionList(),
getVersion(),
]);
setVersion(versionData);
const freeChampionList: Champion[] = rotationData?.freeChampionIds
.map((id) =>
championData.find((champion) => champion.key === String(id))
)
.filter((champion): champion is Champion => champion !== undefined);
setFreeChampions(freeChampionList);
console.log("업데이트 전", freeChampionList);
} catch (error) {
console.error("Error fetching data", error);
setError("데이터를 불러오는 중 오류가 발생했습니다.");
} finally {
setLoading(false);
}
};
fetchData();
}, []);
useEffect(() => {
console.log("업데이트 후", freeChampions);
console.log("버전 업데이트", version);
}, [freeChampions, version]);
if (loading) return <div>로딩 중...</div>;
if (error) return <div>error: {error}</div>;
return <ChampionCard champion={freeChampions} version={version} />;
}
export default RotationPage;
< 문제 상황 >
- ChampionCard 컴포넌트에 props로 freeChampions를 내려주고 있는데 freeChampions가 빈배열이여서 오류가 뜨는 문제점
- 분명 새로운 배열 freeChampionList를 만들어줘서 useState freeChampions에 담아주고 있는데 그걸 Propsfh 내려 주었더니 오류라고 뜬다.
2. 원인 추론 🔎
내가 뜨는 오류에 대해서 검색해보았더니, React 는 렌더링이 화면에 커밋 된 후에야 모든 효과를 실행하기 때문이다. 이런 문장을 발견하였다. 이것에 대해 자세하게 알아보았다.
React의 렌더링 과정에는 크게 두 가지 렌더 단계(Render Phase)와 커밋 단계(Commit Phase)가 있다. 렌더 단계는 새로운 가상 DOM을 생성해 그 전 DOM과 비교한다. 그 이후 커밋 단계는 렌더 단계에서 개선된 변경 사항을 실제 DOM에 적용한다. 이 두 가지 단계가 끝나야 모든 효과를 실행할 수 있다. 예를 들어, useEffect와 같은 부수효과가 커밋 단계 이후에 실행된다는 의미이다. 따라서 useEffect안에 있는 freeChampions을 업데이트 시켜주는 함수는 렌더링이 완료된 후에 실행됨으로, freeChampions가 업데이트 되기 전에 아직 업데이트 되지 않은 초기값 빈배열을 가지고 있는 freeChampions가 ChampionCard 컴포넌트에 props로 전달되어 오류가 뜨고 있는 것 같다고 추론하게 되었다.
- 초기 렌더링
- 컴포넌트가 처음으로 마운트될 때, 모든 상태(state)는 초기값을 가진다.
- 이 경우 freeChampions는 빈 배열 []로, version은 null로 초기화된다.
- 렌더링 단계
- React는 초기 상태를 사용하여 컴포넌트를 렌더링 한다.
- 이 시점에서 아마도 <ChampionCard champion={freeChampions} version={version} />는 빈 배열과 null을 props로 받은 것 같다.
- 커밋 단계
- 렌더링 된 결과가 실제 DOM에 반영된다.
- useEffect 실행
- 커밋 단계 이후에 useEffect 훅이 실행된다.
- 여기서 데이터 페칭이 시작되고, 완료되면 freechampions와 version 상태가 업데이트된다.
- 리렌더링
- 상태 업데이트로 인해 컴포넌트가 리렌더링된다.
- 이제 freeChampion에는 실제 데이터가 있고, version에도 유효한 값을 가진다.
이렇게 데이터가 로드되기 전에 이미 컴포넌트가 렌더링되어 빈 데이터로 ChampionCard가 그려지려고 했기 때문에 발생한 문제였던 것 같습니다.
3. 해결 과정 📋
해결책으로는 데이터가 로드되기 전에 렌더링을 방지하기 위해 조건(freeChampions.length === 0 || version === null)을 추가해 주었다. 바로 freeChampions의 배열 길이가 0이거나 version이 null값이라면 <div>로딩 중...< /div> 띄어주는 것이다. 즉 false가 될 때, 데이터가 모두 로드되었을 때만 실제 ChampionCard를 렌더링하게 된다.
if (error) return <div>error: {error}</div>;
// 데이터가 모두 로드되었는지 확인
if (freeChampions.length === 0 || version === null) {
return <div>데이터 로딩 중...</div>;
}
// 데이터가 모두 준비되었을 때만 ChampionCard 컴포넌트 렌더링
return <ChampionCard champions={freeChampions} version={version} />;
}
export default RotationPage;
4. 결과 ❤🔥
조건을 추가해줌으로 인해 오류 없이 freeChampions가 초기 값 빈배열이 아닌 챔피언 정보를 담고 있는 배열로 업데이트된 다음 props로 내려줄 수 있었다!
'개인과제' 카테고리의 다른 글
Riot API를 활용하여 리그 오브 레전드 정보 앱 만들기⭐︎ (5) | 2024.10.08 |
---|---|
리그오브레전드 정보앱(트러블 슈팅1🌟) (0) | 2024.10.01 |
MBTI 과제 마무리 (0) | 2024.09.11 |
MBTI test(트러블 슈팅3🌟) (0) | 2024.09.10 |
MBTI test(트러블 슈팅2🌟) (0) | 2024.09.10 |