[5차] Next.js 리그오브레전드 정보 프로젝트 (로딩 및 에러 작업)
[5차] Next.js 리그오브레전드 정보 프로젝트
베포 링크
- vercel
- github
- https://github.com/rarrit/lol-info-app
- branch
- feat/loading
프로젝트 설명
NextJS와 TypeScript을 사용하고 Riot API를 활용하여 리그오브레전드의 다양한 데이터를 조회하고 이를 기반으로 생성한 프로젝트 입니다.
STACK
프로젝트 구조
- 4차에선 전체 페이지 반응형 작업을 진행했습니다.
전체 프로젝트 구조
📦app┣ 📂_components
┃ ┣ 📂champions
┃ ┃ ┣ 📜ChampionCard.tsx
┃ ┃ ┗ 📜ChampionList.tsx
┃ ┗ 📂items
┃ ┃ ┣ 📜itemCard.tsx
┃ ┃ ┣ 📜itemDesc.tsx
┃ ┃ ┗ 📜itemList.tsx
┣ 📂api
┃ ┣ 📂rotation
┃ ┃ ┗ 📜route.ts
┃ ┗ 📜apiKey.ts
┣ 📂champions
┃ ┣ 📂[id]
┃ ┃ ┗ 📜page.tsx
┃ ┣ 📜layout.tsx
┃ ┣ 📜loading.tsx
┃ ┗ 📜page.tsx
┣ 📂fonts
┃ ┣ 📜GeistMonoVF.woff
┃ ┗ 📜GeistVF.woff
┣ 📂items
┃ ┣ 📜loading.tsx
┃ ┗ 📜page.tsx
┣ 📂rotation
┃ ┣ 📜loading.tsx
┃ ┗ 📜page.tsx
┣ 📂types
┃ ┣ 📜Champion.ts
┃ ┣ 📜ChampionRotation.ts
┃ ┗ 📜Item.ts
┣ 📂utils
┃ ┣ 📜riotApi.ts
┃ ┗ 📜serverApi.ts
┣ 📜favicon.ico
┣ 📜global-error.tsx
┣ 📜globals.css
┣ 📜layout.tsx
┣ 📜loading.tsx
┗ 📜page.tsx
작업 목록
4-1) 글로벌 로딩 작업
전역으로 사용할 수 있는 로딩 컴포넌트를 생성했다.
// app > loading
export default function Loading() {
return (
<div className="loading-ui fixed z-[9999]">
<p className="bg-loading">로딩중입니다.</p>
</div>
)
}
4-2) 특정 페이지 로딩 작업
예시) app > champions > loading.tsx
특정 페이지 로딩은 디렉토리 내에 loading.tsx 컴포넌트를 생성해서 해당 페이지에서 로딩을 적용해주었다.
export default function Loading(){
return (
<div className="loading-ui fixed z-[9999]">
<p className="bg-loading">쉿! 챔피언 데이터 받아오는중</p>
</div>
)
}
4-3) 챔피언 카드 로딩 작업 (Image 컴포넌트)
로딩 ui가 full 화면으로 나오는데, 특정 챔피언 카드의 데이터가 들어올 때 해당 카드에만 로딩 효과를 주기위해 아래와 같이 작업해보았다.
-
CSR
환경으로 적용 -
useState
를 사용하여 초기 로딩의 상태값을 true로 지정함 -
Image
컴포넌트의onLoadingComplete
속성을 활용해주엇음- 이미지가 완전히 로딩이 끝나면 호출되는 콜백 함수 관련 속성임
const [loading, setLoading] = useState(true);
const handleLoadingComplete = () => {
setLoading(false); // 이미지 로딩 완료 시 로딩 상태를 false로 변경
};
{
loading ? (
<div className="loading-ui">
<p className="bg-loading">로딩중입니다.</p>
</div>
) : ("")
}
{champion.image && (
<Image
src={`${RIOT_BASE_URL}/cdn/img/champion/splash/${champion.id}_0.jpg`}
className="w-full h-full transition-all duration-500 ease-in-out transform group-hover:scale-110"
layout="fill"
alt={champion ? champion.name : "챔피언 이미지"}
onLoadingComplete={handleLoadingComplete}
/>
)}
4-5) global error 적용
-
app > global-error.tsx
- 모든 에러를 처리하는 전역 에러 컴포넌트
-
app > error.tsx
- 에러 및 reset 함수를 Props로 전달하여 global-error.tsx에서 정의한 에러 로직을 사용함
- 이 파일이 없으면 Next.js는 기본 에러 페이지를 사용하게 됨
// app > global-error.tsx
"use client";
import Link from "next/link";
import { useEffect } from "react";
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div className="fixed top-0 left-0 w-full h-full z-[9999] bg-[#111] flex items-center justify-center">
<div className="min-w-[400px] bg-error">
<h2 className="text-[20px] mb-[20px] text-center">어머 에러가 발생했네요!</h2>
<p className="text-center text-[rgba(85,85,85,1)]">{error.message}</p>
<div className="btnArea flex items-center justify-center gap-[10px] mt-[40px]">
<button onClick={reset} className="flex flex-1 justify-center rounded-[10px] bg-black py-[20px]">다시 시도하기</button>
<Link href="/" className="flex flex-1 justify-center rounded-[10px] bg-black py-[20px]">메인으로 이동하기</Link>
</div>
</div>
</div>
);
}
// app > error.tsx
"use client";
import GlobalError from "./global-error";
type ErrorProps = {
error: Error & { digest?: string }; // error의 타입 정의
reset: () => void; // reset 함수의 타입 정의
};
export default function Error({ error, reset }: ErrorProps) {
return (
<GlobalError error={error} reset={reset} />
);
}
Trouble Shooting
회고
로딩과 에러 처리를 구현하면서 사용자 경험을 크게 개선할 수 있었습니다. 로딩 시 진행 상황을 명확히 알리고, 에러 발생 시 사용자가 이해하기 쉬운 메시지를 제공하여 불편함을 줄였다. 그리고 에러 상황에서 재시도나 대안을 제시함으로서 사용자가 에러페이지에서 탈출할 수 있도록 했고 이를 통해 서비스의 안전성을 높이지 않았을까 하는 생각이 든다.
Keep - 현재 만족하고 있는 부분
이번 리그오브레전드 정보 프로젝트를 통해 처음에 태산과도 같았던 Next.js를 조금씩 알 수 있게되어 만족함
Problem - 불편하게 느끼는 부분
에러를 경험할 때 마다 새롭고 고통스럽다. 뭔가 산을 올라가면서 지칠 때 아 이만하면 내려가도되지 않을까 하는 마음이 드는..
Try - problem에 대한 해결책, 당장 실행 가능한 것
포기하지만 말자………!