Riot API는 한 번에 원하는 데이터를 주지 않는다.
소환사 이름으로 매치 상세까지 가져오려면 API를 순서대로 여러 번 호출해야 한다.
흐름이 이렇다.
gameName + tagLine
→ Account v1 → puuid
→ Summoner v4 → summonerId
→ League v4 → tier, rank, LP, wins, losses
→ Match v5 → matchId 목록 20개
→ Match v5 → 매치별 상세
각 단계가 이전 단계 결과를 필요로 한다. puuid 없이는 다음 API를 부를 수 없다.
Account v1 — puuid 획득
const accountResponse = await axios.get(
`https://asia.api.riotgames.com/riot/account/v1/accounts/by-riot-id/${gameName}/${tagLine}`,
{ headers: { "X-Riot-Token": RIOT_API_KEY } }
);
const puuid = accountResponse.data.puuid;
지역은 asia. 한국 서버 소환사도 Account API는 아시아 엔드포인트를 써야 한다.
Summoner v4, League v4
// puuid로 소환사 정보 조회
const summonerResponse = await axios.get(
`https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/${puuid}`,
{ headers: { "X-Riot-Token": RIOT_API_KEY } }
);
// puuid로 랭크 정보 조회
const getRank = await axios.get(
`https://kr.api.riotgames.com/lol/league/v4/entries/by-puuid/${puuid}`,
{ headers: { "X-Riot-Token": RIOT_API_KEY } }
);
여기서부터는 지역이 kr로 바뀐다.
matchId 20개를 한 번에 요청하면 Riot API가 429를 돌려준다.
개발자 키 기준으로 초당 20 요청 제한이 있다. 5개씩 청크로 나눠서, 청크 사이에 1.5초 딜레이를 줬다.
const chunkSize = 5;
for (let i = 0; i < ids.length; i += chunkSize) {
const chunk = ids.slice(i, i + chunkSize);
const results = await Promise.allSettled(
chunk.map(async (matchId) => {
const res = await axios.get(
`https://asia.api.riotgames.com/lol/match/v5/matches/${matchId}`,
{ headers: { "X-Riot-Token": RIOT_API_KEY } }
);
// ... 데이터 파싱
})
);
summaries.push(...results.filter(...).map(...));
await delay(1500);
}
Promise.all 대신 Promise.allSettled를 쓴 이유가 있다. Promise.all은 하나가 실패하면 전체가 reject된다. 매치 하나가 오류나도 나머지 19개는 보여줘야 하기 때문에 allSettled로 개별 실패를 허용했다.
매치 하나에 참가자가 10명이다. puuid로 해당 소환사를 찾아 필요한 필드만 뽑았다.
const player = data.info.participants.find((p) => p.puuid === puuid);
return {
champion: player.championName,
kda: `${player.kills}/${player.deaths}/${player.assists}`,
win: player.win,
items: [player.item0, player.item1, player.item2,
player.item3, player.item4, player.item5, player.item6],
cs: player.totalMinionsKilled,
damage: player.totalDamageDealtToChampions,
teams: {
ally: data.info.participants.filter((p) => p.teamId === player.teamId).map(...),
enemy: data.info.participants.filter((p) => p.teamId !== player.teamId).map(...),
},
};
팀 분리는 teamId로 한다. 같으면 아군, 다르면 적군. 참가자 10명을 두 배열로 나눠서 내려보낸다.
라우터 엔드포인트는 세 개다.
router.get("/", ...) // 소환사 기본 정보
router.get("/rank", ...) // 랭크
router.get("/matches", ...) // 전적 목록
프론트에서 랭크와 전적을 별도로 요청한다. 먼저 응답이 오는 것부터 화면에 뜬다.