:ledger: [5차] Next.js 리그오브레전드 정보 프로젝트

error-ui

:rocket: 베포 링크

:one: 프로젝트 설명

NextJS와 TypeScript을 사용하고 Riot API를 활용하여 리그오브레전드의 다양한 데이터를 조회하고 이를 기반으로 생성한 프로젝트 입니다.

:two: STACK

Next JS TypeScript React Query TailwindCSS JavaScript GitHub

:three: 프로젝트 구조

  • 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

:four: 작업 목록

:pushpin: 4-1) 글로벌 로딩 작업

전역으로 사용할 수 있는 로딩 컴포넌트를 생성했다.

// app > loading
export default function Loading() {
  return (
    <div className="loading-ui fixed z-[9999]">
      <p className="bg-loading">로딩중입니다.</p>
    </div>
  )
}

:pushpin: 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>
  )
}

:pushpin: 4-3) 챔피언 카드 로딩 작업 (Image 컴포넌트)

로딩 ui가 full 화면으로 나오는데, 특정 챔피언 카드의 데이터가 들어올 때 해당 카드에만 로딩 효과를 주기위해 아래와 같이 작업해보았다.

  1. CSR 환경으로 적용
  2. useState 를 사용하여 초기 로딩의 상태값을 true로 지정함
  3. 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}         
  />
)}

:pushpin: 4-5) global error 적용

error-ui

  1. app > global-error.tsx
    • 모든 에러를 처리하는 전역 에러 컴포넌트
  2. 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} />
  );
}

:joystick: Trouble Shooting

:fire: 회고

로딩과 에러 처리를 구현하면서 사용자 경험을 크게 개선할 수 있었습니다. 로딩 시 진행 상황을 명확히 알리고, 에러 발생 시 사용자가 이해하기 쉬운 메시지를 제공하여 불편함을 줄였다. 그리고 에러 상황에서 재시도나 대안을 제시함으로서 사용자가 에러페이지에서 탈출할 수 있도록 했고 이를 통해 서비스의 안전성을 높이지 않았을까 하는 생각이 든다.

:pushpin: Keep - 현재 만족하고 있는 부분

이번 리그오브레전드 정보 프로젝트를 통해 처음에 태산과도 같았던 Next.js를 조금씩 알 수 있게되어 만족함

:pushpin: Problem - 불편하게 느끼는 부분

에러를 경험할 때 마다 새롭고 고통스럽다. 뭔가 산을 올라가면서 지칠 때 아 이만하면 내려가도되지 않을까 하는 마음이 드는..

:pushpin: Try - problem에 대한 해결책, 당장 실행 가능한 것

포기하지만 말자………!