Recent Posts
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
관리 메뉴

개발자 박가나

내일배움캠프 54일차 ('League of Legends' 트러블슈팅) 본문

트러블슈팅

내일배움캠프 54일차 ('League of Legends' 트러블슈팅)

gnchoco97 2024. 12. 16. 20:17

'League of Legends' 프로젝트에서 겪은 트러블슈팅을 기록하고자 한다.

 

이번 트러블슈팅의 경우, 오류 해결이 아니라 여러 방법 중에서 가장 효율적인 방법을 선택하기까지의 과정이다.

 

 

로테이션 데이터 불러오기

route handler 방식으로 rotation 데이터를 불러오는 로직을 구현하였다.

export async function GET(): Promise<NextResponse> {
    const res = await fetch('https://kr.api.riotgames.com/lol/platform/v3/champion-rotations', {
        headers: {
            'X-Riot-Token': ${process.env.RIOT_API_KEY}
        }
    });

    const data: RotationType = await res.json();

    return NextResponse.json(data);
}

 

console.log를 이용해서 데이터를 출력해보면 rotation에 해당하는 챔피언들의 id 값만을 배열로 반환하고 있는데, rotation 페이지에서는 rotation에 해당하는 챔피언들의 정보를 보여줘야 한다.

 

결론적으로, 아래와 같이 로직을 구현하고자 하였다.

  • route handler를 호출함으로써 rotation에 해당하는 챔피언들의 id 값에 대한 데이터 가져오기
  • 챔피언 목록 API를 호출함으로써 챔피언 목록에 대한 데이터 가져오기
  • 해당 데이터들을 이용해서 rotation에 해당하는 챔피언 목록을 추출해서 최종적으로 화면에 보여주기

 

챔피언 목록 API의 경우, fetchChampions라는 이름의 함수로 선언되어 있다.

export const fetchChampions = async (): Promise<ChampionsType> => {
    const version = await fetchVersion();

    const res = await fetch(https://ddragon.leagueoflegends.com/cdn/${version}/data/ko_KR/champion.json, {
        next: {
            revalidate: 60 * 60 * 24
        }
    });

    const data = await res.json();
    const championsData: ChampionsType = data.data;

    return championsData;
};

 

 

방법 1

route handler에서 fetchChampions 함수를 호출한다.

export async function GET(): Promise<NextResponse> {
    const rotationRes = await fetch('https://kr.api.riotgames.com/lol/platform/v3/champion-rotations', {
        headers: {
            'X-Riot-Token': ${process.env.RIOT_API_KEY}
        }
    });

    const rotationData: RotationType = await rotationRes.json();
    
    const championsData: ChampionsType = await fetchChampions();
    
    const filteredData: ChampionsType = Object.fromEntries(
        Object.entries(championsData).filter(([key, value]) =>
        	rotationData.freeChampionIds.includes(parseInt(value.key))
        )
    );

    return NextResponse.json(filteredData);
}

 

fetchChampions 함수의 경우 다른 server action에서 사용하기 위해서 정의되었다. 그런 상황에서 fetchChampions 함수를 route handler에서 사용할 경우, 두 기능 사이에 의존성이 생기게 되기 때문에 fetchChampions 함수에 변화가 생기게 되면 route handler도 영향을 받을 수 있다.

 

 

방법 2

route handler를 호출하는 컴포넌트에서 fetchChampions 함수를 호출한다.

const { data, isPending, isError } = useQuery({
    queryKey: ['rotation'],
    queryFn: async () => {
        const rotationRes = await fetch('/api/rotation');
        const rotationData: RotationType = await rotationRes.json();
        
        const championsData: ChampionsType = await fetchChampions();

        const filteredData: ChampionsType = Object.fromEntries(
            Object.entries(championsData).filter(([key, value]) =>
            	rotationData.freeChampionIds.includes(parseInt(value.key))
            )
        );

        return filteredData;
    }
});

 

클라이언트는 route handler 호출과 fetchChampions 함수 호출, 총 2번의 HTTP 요청을 서버에 보내게 된다. 또한, 데이터를 가공하는 로직이 서버와 클라이언트 모두에 존재하기 때문에 통일성이 떨어지고 중복 코드가 발생할 가능성이 있다.

 

 

방법 3

데이터를 호출하고 가공하는 모든 로직을 route handler 내부에서 수행한다.

import { ChampionsType } from '@/types/Champion';
import { RotationType } from '@/types/Rotation';
import { NextResponse } from 'next/server';

export async function GET(): Promise<NextResponse> {
    const rotationRes = await fetch('https://kr.api.riotgames.com/lol/platform/v3/champion-rotations', {
        headers: {
            'X-Riot-Token': `${process.env.NEXT_PUBLIC_RIOT_API_KEY}`
        }
    });

    const rotationData: RotationType = await rotationRes.json();

    const championsRes = await fetch(
        `https://ddragon.leagueoflegends.com/cdn/${version}/data/ko_KR/champion.json`
    );

    const data = await championsRes.json();
    const championsData: ChampionsType = data.data;

    const filteredData: ChampionsType = Object.fromEntries(
        Object.entries(championsData).filter(([key, value]) =>
            rotationData.freeChampionIds.includes(parseInt(value.key))
        )
    );

    return NextResponse.json(filteredData);
}

 

클라이언트는 route handler 호출, 한 번의 HTTP 요청만을 서버에 보내게 된다. 또한, 데이터를 가공하는 로직이 서버에만 존재하기 때문에 통일성이 증가하고 클라이언트 코드가 간결해진다.

 

 

느낀점

여러가지 장단점을 비교해보면서 최종적으로 세 번째 방법을 선택하게 되었지만 정답이 아닐 수도 있고, 이 경우에는 이 방법이 맞지만 저 경우에는 저 방법이 맞을 수도 있을 것이다. 실제로 첫 번째와 두 번째 방법을 사용해도 기능적으로는 문제가 없으니 말이다. 코딩을 하다보면 명확한 정답이 정해진 경우도 있겠지만 오늘처럼 여러 방법 중에서 가장 효율적인 방법을 선택해야 하는 경우가 훨씬 많을 것이기 때문에, 기능 구현에 성공했다고 끝내는 것이 아니라 코드 및 원리에 대한 이해가 반드시 동반되어야 할 것이라는 생각이 들었다.