1. match ID 가져오기

여기서 matchId를 가져와 각 match에 대한 정보를 다른 api에서 불러온다.
딱히 힘든 부분은 없었다. 시작과 count를 0과 20으로 상수 설정을 하였다.
import useFetch from '@/hooks/fetch/useFetch.ts';
import { MATCH_ID } from '@/api/url.ts';
export default function useMatchId({ puuid, enabled }) {
const { data, isLoading, isError } = useFetch({
key: 'matchId',
value: puuid,
url: MATCH_ID(puuid),
options: {
enabled,
},
});
return { data, isLoading, isError };
}
import useFetch from '@/hooks/fetch/useFetch.ts';
import { MATCH_ID } from '@/api/url.ts';
import type { userInfoProps } from '@/types/user.ts';
export default function useMatchId({ puuid, enabled }: userInfoProps) {
const { data, isLoading, isError } = useFetch<string[]>({
key: 'matchId',
value: puuid,
url: MATCH_ID(puuid),
options: {
enabled,
},
});
return { data, isLoading, isError };
}
use 훅에서 useFetch로 데이터를 보내 얻는다.
2. 각 매치 정보 가져오기
위에서 가져온 매치Id로 각 매치의 정보를 가져온다.

롤(League of Legends) Match V5 API 상세 설명
제공해주신 GET /lol/match/v5/matches/{matchId} API는 특정 게임(match)의 상세 데이터를 가져오는 핵심적인 API입니다. 이 API를 통해 얻어온 데이터는 롤 전적 검색 사이트에서 가장 중요하게 사용되는 부분으로, 한 게임의 모든 것을 담고 있다고 볼 수 있습니다.
API 응답은 크게 metadata와 info 두 개의 JSON 객체로 나뉩니다. 각 객체와 그 하위 항목들이 어떤 기능을 하는지 자세히 설명해 드리겠습니다.
1. metadata 객체: 경기에 대한 기본 정보
이 객체는 해당 경기의 메타데이터, 즉 데이터 자체에 대한 정보를 담고 있습니다.
| 항목 | 데이터 타입 | 설명 |
| dataVersion | string | 이 데이터의 버전 정보입니다. 라이엇 게임즈가 데이터 구조를 업데이트할 때마다 변경될 수 있습니다. |
| matchId | string | 경기의 고유 ID입니다. (예: "KR_7755234865") 이 ID를 통해 특정 경기를 조회할 수 있습니다. |
| participants | List[string] | 해당 경기에 참여한 모든 소환사의 PUUID 리스트입니다. PUUID는 계정마다 부여되는 영구적이고 고유한 ID입니다. |
2. info 객체: 경기의 모든 상세 내용
경기의 실질적인 모든 데이터가 이 객체 안에 들어있습니다. 게임 시간, 모드, 참가자 개개인의 성적, 팀 정보 등이 포함됩니다.
2-1. 게임 전반 정보
| 항목 | 데이터 타입 | 설명 |
| gameCreation | long | 게임이 생성된 시점의 Unix 타임스탬프입니다. (로딩 화면 시작) |
| gameDuration | long | 게임 진행 시간입니다. 11.20 패치 이후로는 초(second) 단위로 계산됩니다. |
| gameEndTimestamp | long | 게임이 종료된 시점의 Unix 타임스탬프입니다. |
| gameId | long | 게임의 고유 번호입니다. |
| gameMode | string | 게임 모드(예: CLASSIC, ARAM, URF)를 나타냅니다. |
| gameType | string | 게임 타입(예: MATCHED_GAME - 매칭 게임, CUSTOM_GAME - 사용자 설정 게임)을 나타냅니다. |
| gameVersion | string | 해당 경기가 진행된 게임 클라이언트 버전입니다. (예: 14.15.586.331) 패치 버전을 확인할 수 있습니다. |
| mapId | int | 게임이 진행된 맵의 ID입니다. (예: 11 - 소환사의 협곡, 12 - 칼바람 나락) |
| platformId | string | 경기가 진행된 서버 플랫폼입니다. (예: "KR") |
| queueId | int | 게임 큐(Queue)의 ID입니다. (예: 420 - 솔로 랭크, 430 - 일반 게임, 450 - 무작위 총력전) |
| participants | List[ParticipantDto] | 가장 중요한 데이터 중 하나로, 10명의 참가자 각각의 상세 정보를 담은 리스트입니다. (아래 ParticipantDto에서 상세 설명) |
| teams | List[TeamDto] | 블루 팀(100)과 레드 팀(200)의 정보를 담은 리스트입니다. (아래 TeamDto에서 상세 설명) |
| tournamentCode | string | 토너먼트 코드로 생성된 게임일 경우 해당 코드가 표시됩니다. |
2-2. ParticipantDto: 참가자 개별 데이터
info.participants 리스트에 포함된 각 참가자의 모든 정보를 담고 있습니다. 전적 검색의 핵심 데이터입니다.
주요 항목:
| 항목 | 데이터 타입 | 설명 |
| summonerName | string | 소환사 이름 (인게임 닉네임) |
| riotIdGameName / riotIdTagline | string | 라이엇 ID와 태그라인 (2024년 도입) |
| puuid | string | 플레이어의 고유 PUUID |
| championName | string | 플레이한 챔피언의 영문 이름 (예: "Aatrox", "Ahri") |
| championId | int | 플레이한 챔피언의 고유 ID |
| win | boolean | 승리 여부 (true/false) |
| kills / deaths / assists | int | 킬, 데스, 어시스트 |
| item0, item1, ... item6 | int | 보유 아이템 7개(장신구 포함)의 각 아이템 ID |
| summoner1Id / summoner2Id | int | 사용한 소환사 주문(스펠) 2개의 ID |
| goldEarned | int | 획득한 총 골드 |
| totalDamageDealtToChampions | int | 챔피언에게 가한 총 피해량 |
| totalMinionsKilled | int | CS (미니언 처치 수). 정글 몬스터는 neutralMinionsKilled에 포함됩니다. |
| wardsPlaced / wardsKilled | int | 설치한 와드 수 / 파괴한 와드 수 |
| visionScore | int | 시야 점수 |
| teamPosition | string | 팀 내에서 플레이한 포지션 (예: TOP, JUNGLE, MIDDLE, BOTTOM, UTILITY) |
| lane | string | 실제 라인 (teamPosition보다 덜 정확할 수 있음) |
| champLevel | int | 최종 챔피언 레벨 |
| pentaKills, quadraKills, ... | int | 펜타킬, 쿼드라킬 등 멀티킬 횟수 |
| perks | PerksDto | 선택한 룬 정보 (핵심 룬, 보조 룬, 능력치 파편) |
| challenges | ChallengesDto | 도전과제 관련 데이터. kda, killParticipation (킬 관여율), damagePerMinute (분당 피해량) 등 계산된 수치가 포함되어 매우 유용합니다. |
2-3. TeamDto: 팀 데이터
info.teams 리스트에 포함된 각 팀(블루/레드)의 정보를 담고 있습니다.
| 항목 | 데이터 타입 | 설명 |
| teamId | int | 팀 ID (100: 블루 팀, 200: 레드 팀) |
| win | boolean | 팀의 승리 여부 (true/false) |
| bans | List[BanDto] | 해당 팀이 밴(Ban)한 챔피언 목록과 밴 순서 |
| objectives | ObjectivesDto | 팀이 달성한 오브젝트 정보 |
2-4. ObjectivesDto: 오브젝트 상세 데이터
TeamDto.objectives에 포함되어 있으며, 팀별로 획득한 오브젝트 정보를 나타냅니다.
| 항목 | 데이터 타입 | 설명 |
| baron | ObjectiveDto | 바론 킬 횟수 및 첫 바론 킬 여부 |
| dragon | ObjectiveDto | 드래곤 킬 횟수 및 첫 드래곤 킬 여부 |
| inhibitor | ObjectiveDto | 파괴한 억제기 수 및 첫 억제기 파괴 여부 |
| riftHerald | ObjectiveDto | 전령 킬 횟수 및 첫 전령 킬 여부 |
| tower | ObjectiveDto | 파괴한 타워 수 및 첫 타워 파괴 여부 |
요약 및 활용 방안
- 기본적인 전적 표시: info.participants를 순회하며 각 플레이어의 summonerName, championName, kills, deaths, assists, item0-6, totalMinionsKilled 등을 가져와 화면에 표시합니다.
- KDA 및 킬 관여율 계산:
- KDA는 challenges.kda를 직접 사용하거나, (kills + assists) / deaths 로 계산할 수 있습니다.
- 킬 관여율은 challenges.killParticipation 값을 퍼센트로 변환하여 사용합니다.
- 승/패 확인: info.participants 내의 win boolean 값을 통해 승패를 구분하고 배경색 등을 다르게 표시합니다.
- 팀 전체 정보: info.teams에서 bans 정보를 가져와 밴픽 현황을 보여주고, objectives를 통해 각 팀의 드래곤, 바론 획득 현황을 시각적으로 표시할 수 있습니다.
- 빌드 정보: perks에서 룬 정보를, summoner1Id/summoner2Id에서 스펠 정보를 가져와 아이템 빌드와 함께 보여줍니다.
- 상세 분석: challenges 객체 안에는 damagePerMinute(분당 데미지), goldPerMinute(분당 골드) 등 심화된 분석 지표가 많아 이를 활용해 더 상세한 정보를 제공할 수 있습니다.
이처럼 match-v5 API 하나만으로도 한 경기에 대한 거의 모든 정보를 얻을 수 있으며, 롤 전적 검색 서비스의 가장 기본이자 핵심적인 데이터를 구성하게 됩니다. 이 구조를 잘 이해하고 필요한 데이터를 추출하여 프론트엔드에서 가공하면 풍부한 전적 정보를 사용자에게 제공할 수 있습니다.
현재 matchId에 20개의 id가 배열로 구성되어 있다.
각 id마다 해당 데이터를 얻으려고 map을 써보았는데 오류가 발생했다.
const {
data: matchId,
isLoading: matchIdIsLoading,
isError: matchIdIsError,
} = useMatchId({ puuid: puuidData?.puuid ?? '', enabled: !!puuidData?.puuid });
const {
data: matchInfo,
isLoading: matchInfoIsLoading,
isError: matchInfoIsError,
} = matchId.map((item) => {
useMatchInfo({ matchId: item ?? '', enabled: !!item });
});
matchId를 map으로 돌면서 useMatchInfo에 matchId 값인 item을 넣으려고 했는데 오류가 발생했다.
ESLint: React Hook "useMatchInfo" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.(react-hooks/rules-of-hooks)
훅을 callback 함수에 넣을 수 없다는 오류이다. 즉, React Hooks의 규칙을 위반한다.
map 안에서 Hook을 호출하면 안되는 이유?
Hooks는 반복문, 조건문, 중첨 함수 안에서 호출될 수 없음.
Hooks는 항상 React 컴포넌트 함수의 최사위에서만 호출되어야 함.
React는 매 렌더링마다 Hook이 항상 동일한 순서로 호출될 것이라고 가정하고 내부 상태 관리함.
matchId.map(item =? { useMatchInfo(...) })는 matchId 배열을 순회하는 반복문 안에서 Hook 호출함.
만약 matchId 배열 길이가 렌더링 사이에 바뀐다면, Hook의 호출 순서와 개수가 달라지게 되어 React가 상태를 제대로 추적할 수 없게 됨.
해결법
useQueries 사용하기 (React Query / TanStack Query)
- useQueries를 사용하면 여러 개의 쿼리를 병렬로 보냄. 모든 매치 정보 요청을 동시에 병렬로 보냄.
- 한 번의 렌더링으로 모든 데이터를 가져오고 상태를 업데이트함.
현재에서는 자식 컴포넌트를 만들어야 하므로 자식 컴포넌트로 분리하는 방법을 사용했다.
자식 컴포넌트로 분리하기
- 데이터 패칭 Hook을 호출하는 부분을 별도의 자식 컴포넌트로 분리함.
- matchId 하나를 props로 받아 useMatchInfo Hook을 호출함.
Home에서 아래와 같이 분리한다.
{matchId && matchId.map((matchId) => <MatchInfo key={matchId} matchId={matchId} />)}
자식에서 아래와 같이 호출한다.
import useMatchInfo from '@/hooks/fetch/useMatchInfo.ts';
export default function MatchInfo({ matchId }) {
const {
data: matchInfo,
isLoading: matchInfoIsLoading,
isError: matchInfoIsError,
} = useMatchInfo({ matchId: matchId ?? '', enabled: !!matchId });
if (matchInfoIsLoading) return <p>매치 정보 로딩중...</p>;
if (matchInfoIsError) return <p>매치 정보 로드 중 에러 발생</p>;
return (
<>
{matchInfo && (
<>
<p>{matchInfo.metadata.matchId}</p>
<p>{matchInfo.info.gameCreation}</p>
</>
)}
</>
);
}
하지만 오류가 발생했다.

api를 초당 불러오는데 제한을 둔다.
현재 코드는 병렬 처리를 하는데 1초도 안되는 시간동안 20개의 데이터를 요청한다.
이를 해결하기 위해서는 페이지내이션을 두어 정보를 부분화하여 보여주던지,
async/await를 통한 시간 지연을 두어야 한다.
여기서는 페이지내이션을 통해 5개씩 정보가 보이게 할 것이다.
왜냐하면 정보가 20개 이상으로 설정할 수 있기 때문에 페이지를 두는 것이 더 좋을 것으로 판단된다.
const {
data: matchId,
isLoading: matchIdIsLoading,
isError: matchIdIsError,
} = useMatchId({ puuid: puuidData?.puuid ?? '', enabled: !!puuidData?.puuid });
const [visibleMatchId, setVisibleMatchId] = useState(5);
const handleLoadMore = () => {
setVisibleMatchId((prevCount) => prevCount + 5);
};
const visibleMatchIds = (matchId ?? []).slice(0, visibleMatchId);
useState에 5를 두어 버튼을 누를 때마다 5씩 데이터가 더 보이게 하였다.
matchId 배열에서 slice 메서드를 통해 원하는 length만큼 데이터가 보이게 하였다.

에러가 보이지 않고 잘 표시되었다.
3. 가장 많이 사용하는 챔피언 가져오기

저기서 챔피언 아이디를 가져오면 된다.
이전까지 api를 가져오는 방법은 같다.
const bestChampionId = championId?.map((champion) => champion.championId);
champhionId만 가져오기 위해 map을 사용했다.
챔피언id에 해당하는 챔피언을 가져와야 한다.
ddragon.leagueoflegends.com/cdn/15.15.1/data/ko_KR/champion.json
여기서 챔피언 데이터를 가져온다.
championId는 key와 같은 것을 가져오면 된다.
15.15.1은 패치 버전이다. 최신 것을 가져오면 된다.
import useChampionInfo from '@/hooks/fetch/useChampionInfo.ts';
export default function BestChampion({ championId, championIsLoading, championIsError }) {
const bestChampionId = championId?.map((champion) => champion.championId);
const {
data: championInfo,
isLoading: championInfoIsLoading,
isError: championInfoIsError,
} = useChampionInfo();
const bestChampionInfo = championInfo.map((item) => item.key === bestChampionId);
console.log(bestChampionInfo);
if (championIsLoading || championInfoIsLoading) return <p>챔피언 정보 로딩중...</p>;
if (championIsError || championInfoIsError) return <p>챔피언 정보 로드 중 에러 발생</p>;
return (
<>
</>
);
}
id별로 key에 해당하는 값을 가져오려고 했는데 오류가 발생했다.

챔피언 정보가 객체여서 배열로 바꾸어야 한다.
컴포넌트에서 바꾸려면 복잡해 보이니 hook에서 바꿔보자.
import useFetch from '@/hooks/fetch/useFetch.ts';
import { CHAMPION_INFO_URL } from '@/api/url.ts';
export default function useChampionInfo({ enabled }) {
const { data, isLoading, isError } = useFetch({
key: 'championInfo',
value: '',
url: CHAMPION_INFO_URL,
options: {
enabled,
},
});
const championData = data ? Object.values(data.data) : [];
return { data: championData, isLoading, isError };
}
data를 배열로 바꾸면 된다.
const championData = data ? Object.values(data.data) : [];
return { data: championData, isLoading, isError };
Object.values()를 쓰면 객체 value를 배열로 바꾼다.
const bestChampionId = championId?.map((champion) => champion.championId) ?? [];
const {
data: championInfo,
isLoading: championInfoIsLoading,
isError: championInfoIsError,
} = useChampionInfo({ enabled: Boolean(bestChampionId) });
const bestChampionInfo = championInfo.filter((champ) =>
bestChampionId.includes(Number(champ.key))
);
필요한 핵심 api를 다 가져왔다.
이제 이 api를 이용해 ui를 꾸며보도록 하겠다.
match 부분도 더 추가해 보겠다.
'REACT > 롤 전적 사이트' 카테고리의 다른 글
| [5] 무한 스크롤 구현하기 (1) | 2025.08.21 |
|---|---|
| [4] 스타일 꾸미기 (7) | 2025.08.19 |
| [2] puuid를 이용하여 소환사 정보 받아오기 (1) | 2025.08.06 |
| [1] useQuery를 이용하여 puuid 받기 (4) | 2025.08.06 |