๐ฎRIOT API ๋ถ์ ํ๋ก์ ํธ(7) - Flask๋ฅผ ์ฌ์ฉํด ์นํ์ด์ง ๊ฐ๋ฐ
flask๋ฅผ ํ์ฉํด ์นํ์ด์ง ๊ฐ๋ฐ
flask_project/
โ
โโโ app.py # Flask ๋ฉ์ธ ํ์ผ
โโโ riot_api.py # Riot API ๊ด๋ จ ํจ์๋ค
โโโ templates/
โ โโโ home.html # ์ํ์ฌ ๊ฒ์์ ์ํ ํผ
โ โโโ results.html # ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ํ๋ ํ์ด์ง
โโโ static/ # CSS, JS ํ์ผ ๋ฑ (ํ์ ์)
์ด์ DB๋ ๊ตฌ์ถ์ ๋ค ํ๊ณ ์ ์ ๋ค์ ๋๋ค์์ ๊ฒ์ํ์ ๋ ์์ ์ ์ต๊ทผ 5ํ ํ๊ท ์ ๋ณด์ฌ์ฃผ๋ ์น์ฌ์ดํธ๋ฅผ ๋ง๋ค๊ฒ ์ต๋๋ค.
flask๋ฅผ ์ด์ฉํ๋ฉด ๋น๊ต์ ์์ฝ๊ฒ ์น์ฌ์ดํธ๋ฅผ ๋ง๋ค ์ ์๊ณ ๊ตฌ์กฐ๋ ์์ ๊ฐ์ต๋๋ค.
๋ฐํํ๋ฉด์ flask_project๋ผ๋ ํด๋๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ app.py์ riot_api.py ๋ผ๋ ํ์ด์ฌ ํ์ผ์ ๋ฃ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ํํ์ด์ง์ ๊ตฌํํ home.html ๊ณผ results.html๋ผ๋ ํ์ผ์ templates๋ผ๋ ํด๋๋ฅผ ๋ง๋ค์ด์ ๊ทธ ์์ ๋ฃ์ต๋๋ค.
app.py ํ์ผ
from flask import Flask, render_template, request
from riot_api import get_summoner_info, get_recent_matches
app = Flask(__name__)
@app.route("/")
def home():
return render_template("home.html")
@app.route("/search", methods=["GET"])
def search():
summoner_name = request.args.get("name")
if not summoner_name:
return render_template("home.html", error="Please enter a summoner name.")
# ์ํ์ฌ ์ ๋ณด์ ์ต๊ทผ ๋งค์น ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
summoner_info = get_summoner_info(summoner_name)
if not summoner_info:
return render_template("home.html", error="Summoner not found.")
# ์ต๊ทผ 5๊ฒ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
match_details = get_recent_matches(summoner_info['puuid'], count=5)
return render_template("results.html", matches=match_details, summoner_name=summoner_name)
if __name__ == "__main__":
app.run(debug=True)
riot_api.py ํ์ผ
import requests
API_KEY = "my_api"
# ์ํ์ฌ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
def get_summoner_info(summoner_name):
url = f"https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summoner_name}"
headers = {"X-Riot-Token": API_KEY}
response = requests.get(url, headers=headers)
# ์ํ ์ฝ๋ ๋ฐ ์๋ต ๋ด์ฉ ๋ก๊ทธ ์ถ๋ ฅ
print(f"API ํธ์ถ ์ํ ์ฝ๋: {response.status_code}")
print(f"API ์๋ต ๋ด์ฉ: {response.text}")
print(f"Rate Limit? Retry-After: {response.headers.get('Retry-After')}")
if response.status_code == 200:
return response.json()
else:
return None
# ์ต๊ทผ ๊ฒฝ๊ธฐ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
def get_recent_matches(puuid, count=5):
match_list_url = f"https://asia.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids?start=0&count={count}"
headers = {"X-Riot-Token": API_KEY}
match_list_response = requests.get(match_list_url, headers=headers)
if match_list_response.status_code != 200:
return []
match_ids = match_list_response.json()
match_details = []
# ๊ฐ ๊ฒฝ๊ธฐ ์์ธ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
for match_id in match_ids:
match_data_url = f"https://asia.api.riotgames.com/lol/match/v5/matches/{match_id}"
match_data_response = requests.get(match_data_url, headers=headers)
if match_data_response.status_code == 200:
match_data = match_data_response.json()
# ํด๋น ๊ฒฝ๊ธฐ์์ ์ํ์ฌ์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถ
participant_data = next(p for p in match_data['info']['participants'] if p['puuid'] == puuid)
match_details.append({
'kills': participant_data['kills'],
'deaths': participant_data['deaths'],
'assists': participant_data['assists'],
'goldEarned': participant_data['goldEarned'],
'wardsPlaced': participant_data['wardsPlaced']
})
return match_details
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Summoner Search</title>
</head>
<body>
<h1>Search for Summoner</h1>
<form action="/search" method="get">
<label for="name">Summoner Name:</label>
<input type="text" id="name" name="name" required>
<button type="submit">Search</button>
</form>
{% if error %}
<p style="color: red;">{{ error }}</p>
{% endif %}
</body>
</html>
results.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Match Results for {{ summoner_name }}</title>
</head>
<body>
<h1>Recent Matches for {{ summoner_name }}</h1>
<ul>
{% for match in matches %}
<li>
<p>Kills: {{ match.kills }}</p>
<p>Deaths: {{ match.deaths }}</p>
<p>Assists: {{ match.assists }}</p>
<p>Gold Earned: {{ match.goldEarned }}</p>
<p>Wards Placed: {{ match.wardsPlaced }}</p>
</li>
{% endfor %}
</ul>
</body>
</html>
์ด๋ ๊ฒ ํ์ผ๋ค์ ๋ง๋ค๊ณ ํฐ๋ฏธ๋์์ python app.py๋ฅผ ์คํ์ํค๋ฉด ์น์ฌ์ดํธ๊ฐ ๋์ต๋๋ค.
ํฐ๋ฏธ๋์ app ์ฐ๊ฒฐ, ์น์ฌ์ดํธ ์ฐ๋ ํ์ธ

ํฐ๋ฏธ๋์ ์ ์ฌ์ง๊ณผ ๊ฐ์ด ํ์๊ฐ ๋๊ณ ์ฃผ์์ http://127.0.0.1:5000๋ฅผ ์ ๋ ฅํ๋ฉด ๋ง๋ ์นํ์ด์ง๊ฐ ๋์ต๋๋ค!

์ด๋ฐ ์น์ฌ์ดํธ๊ฐ ๋ง๋ค์ด์ก์ต๋๋ค.
์ด์ ์ ๋๋ก ๋ง๋ค์๋์ง ํ์ธํ๊ธฐ ์ํด Summoner Name๋ฅผ ์ ๋ ฅํ์ต๋๋ค.
๊ทผ๋ฐ ์๊พธ summoner not found๋ผ๊ณ ๋์ต๋๋ค.

vscode๋ก ๋์์์ ํฐ๋ฏธ๋์ ํ์ธํ๋๋ 403 ์ค๋ฅ๊ฐ ๋ฉ๋๋ค.
403 ์ค๋ฅ๊ฐ ๋๋ ์์ธ์ ๋ณดํต ์๋์ ๊ฐ์ต๋๋ค.
- ์๋ก์ด API ํค ๋ฐ๊ธ: API ํค๊ฐ ๋ง๋ฃ๋์์ ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค.Riot Developer Portal์์ ์๋ก์ด API ํค๋ฅผ ๋ฐ๊ธ๋ฐ๊ณ ์ ์ฉ
- API ํธ์ถ ์ ํ ํ์ธ: API ํธ์ถ ํ์๊ฐ ์ ํ์ ๊ฑธ๋ ธ์ ์ ์๋ค. ์ผ์ ์๊ฐ์ด ์ง๋ ํ ๋ค์ ์๋
- API ํค ์ ๋ ฅ ์ค๋ฅ ํ์ธ: API ํค๊ฐ ์ ํํ๊ฒ ์ ๋ ฅ๋์๋์ง ํ์ธ
- ์ฌ๋ฐ๋ฅธ ์ง์ญ ์๋ฒ ์ฌ์ฉ: ํ๊ตญ ์๋ฒ(kr.api.riotgames.com)๋ฅผ ์ฌ์ฉํ๋์ง ํ์ธ
- HTTPS ํ๋กํ ์ฝ ์ฌ์ฉ: Riot API๋ HTTPS๋ฅผ ํตํด ์์ฒญ
๊ทผ๋ฐ ์ ๋ ์์ ํด๋นํ๋ ์์ธ์ด ์์๊ณ , ์ฝ๋์ ์ค๋ฅ๋ ์คํ๊ฐ ์์ง๋ ์์์ต๋๋ค.
riot api๋ฅผ ์ฌ๋ฐ๊ธ๋ ๋ฐ์๋ดค์ง๋ง ํด๊ฒฐ์ด ๋์ง ์์์ต๋๋ค.
๋ฌธ์ ์ ์์ธ์ด ๋ญ๊น ์ถ์ด์ riot api๋ฅผ ๋ค์ ํ์ธํ๋๋ฐ ์์ธ์ ์ฐพ์์ต๋๋ค..
summonerName API ์๋น์ค ๋ฏธ์ง์

https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summoner_name} ๋ผ๋ ๊ฒ์ฒ๋ผ
RIOT API๋ ์ํ์ฌ ์ด๋ฆ์ ํตํด ์ํ์ฌ ์ ๋ณด๋ฅผ ๊ฒ์ํ ์ ์๋ ์๋ํฌ์ธํธ๋ฅผ ๋ ์ด์ ์ ๊ณตํ์ง ์์ต๋๋ค.
๊ตฌ๊ธ์์ ๊ฒ์ํด์ RIOT API๋ฅผ ํ์ฉํ์ฌ ์ ์ ๊ฐ ์์ ์ ๋๋ค์์ ๊ฒ์ํ์ ๋ ์ต๊ทผ ์ ์ ์ด ๋์ค๋ ํ๋ก์ ํธ๋ฅผ ์งํํ ๋ชจ๋ ๊ธ๋ค์ https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summoner_name} ๋ฅผ ํ์ฉํ์ต๋๋ค.

๋ค๋ฅธ ๋ธ๋ก๊ทธ์์ ํผ์๋๋ฐ, ์์ ์๋ ์์ ๊ฐ์ด summonerName ์ด๋ผ๋ api๋ฅผ ์ ๊ณตํ์ฌ ๋๋ค์์ ์ ๋ ฅํด์ puuid๋ฅผ ์ป๊ณ ๊ทธ๊ฒ์ ํ์ฉํด matchid๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐฉ์์ด์๋๋ฐ ์ด์ ๋ ๊ทธ ์ ๊ทผ ๋ฐฉ์์ ์ธ ์ ์๊ฒ ๋ ๊ฒ๋๋ค.
์ฌํด 2์์ธ๊ฐ ๊น์ง๋ ์์๋๋ฐ..
๊ทธ๋ฌ๋ riot_api.py์ด๋ผ๋ ์ฝ๋์์ summonerName์ ํธ์ถํ๋ ๋ฐฉ์์ ์ธ ์ ์๋ ๊ฒ์ด์ฃ .
๊ฒ์์ ๋ณด๋ puuid, encryptedSummonerID๋ฅผ ํตํด์๋ง ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค๋ง ๊ฒ์ ๋ด๋ถ์์ ์ ์ ๋์์ ์ puuid, summonerID๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์ง์ง ๋ฌธ์ ๊ฐ ์ฌ๊ธฐ์ ์๊ธด ๊ฒ์ด์ฃ .
์๋น์ค๋ฅผ ๊ตฌ์ํ๊ธฐ ์ํด DB ๊ตฌ์ถ๋ ๋ค ํ๋๋ฐ ์ ์ ๋ค์ด ์์ ์ ๋๋ค์ or puuid๋ฅผ ๊ฒ์ํ์ฌ ์กฐํํ ์ ์๋ ๊ธฐ๋ฅ์ ๋ง๋ค์ง ๋ชปํ๋ ๊ฒ๋๋ค.
๋ฌผ๋ก ์ผ๋ถ ๋น๊ณต์ API๋ ๋กค ์ ์ ์ฌ์ดํธ๋ ์ฌ์ ํ ์ํ์ฌ ์ด๋ฆ์ ํตํด PUUID๋ฅผ ์ถ์ถํด์ฃผ๋ ์๋น์ค๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค. ํด๋น ์๋น์ค๋ฅผ ํ์ฉํด ์ํ์ฌ ์ด๋ฆ์ PUUID๋ ๋ค๋ฅธ ์๋ณ์๋ก ๋ณํํ ํ, ์ด๋ฅผ Riot API์์ ์ฌ์ฉํ๋ ๋ฐฉ์๋ ์์ต๋๋ค.
ํ์ง๋ง ํ์ฌ์ดํธ๋ฅผ ์ด์ฉํ์ง ์๊ณ ์ ๊ฐ ๊ตฌ์ฑํ ๊ฒ๋ง์ผ๋ก ์๋น์ค๋ฅผ ๊ตฌ์ฑํ๊ณ ์ถ์์ต๋๋ค.
๋ฐฉ๋ฒ์ด ์๊ธด ์์ต๋๋ค... ์ฌ๊ธฐ์๋ ๋ค์ด์ 1 ํฐ์ด๋ผ๊ณ ๊ฐ์ ํ๊ฒ ์ต๋๋ค.
1. league-ex-v4์์ ํด๋น๋ ํฐ์ด์ ๋ชจ๋ ์ ์ summonerID๋ฅผ ๊ฐ์ ธ์ต๋๋ค. (2์ฒ๊ฐ๋น 1์๊ฐ, 1๋ง์ ์ ๊ฐ ์กฐ๊ธ ๋๊ธฐ์ 5์๊ฐ ์ ๋ ์๊ฐ์์)
2. summonerID๋ฅผ DB์ ์ค๊ณ summoner-v4์์ summnerID๋ฅผ ๋ฃ์ ๋ค์ puuid๋ฅผ ๊ฐ์ ธ์ต๋๋ค. (์ญ์ 5์๊ฐ)
3. puuid๋ฅผ DB์ ์ค๊ณ match-v4์์ ์ต๊ทผ 1ํ์ matchID๋ง์ ๊ฐ์ ธ์ต๋๋ค. (5์๊ฐ)
4. matchID๋ฅผ DB์ ์ค๊ณ matchID/v5/matches์์ ํด๋นํ ๊ฒ์ ์ ๋ณด๋ฅผ ๋๋ค์๊ณผ PUUID ์ ๋ณด๋ง์ ๊ฐ์ ธ์ต๋๋ค.
5. ๊ทธ๋ฆฌ๊ณ 2๋ฒ์ ๋ง๋ DB์ ๋ค์ด์๋ puuid๊ฐ 4๋ฒ์์ ์์ฑ๋ PUUID์ ์ผ์นํ๋ ๊ฒ๋ง์ ๊ณ ๋ฆ ๋๋ค.
(4๋ฒ์ ๋ง๋ PUUID๋ ๊ทธ ํ์ ์ฐธ์ฌํ ๋ชจ๋ ์ฌ๋์ puuid๊ฐ ๋ค์ด์๊ธฐ์ ํด๋น ํฐ์ด์ ๋ง๋ puuid์ธ 2๋ฒ๋ง์ ๊ณจ๋ผ์ผ ํฉ๋๋ค.)
6. ๊ทธ๋ฌ๋ฉด ์ ์ ์ "๋๋ค์"๊ณผ "PUUID"๋ง์ด ๋จ๊ณ ์ด๊ฒ์ ๋ค์ด์1 ํฐ์ด์ ๋๋ค์๊ณผ puuid ์๋ DB์ผ๋ก ๊ตฌ์ถํฉ๋๋ค.
7. ๊ทธ๋ฌ๋ฉด ๋ค์ด์1 ํฐ์ด์ ํด๋นํ๋ ์ ์ ์ ๋๋ค์+puuid๊ฐ ์๋ DB๊ฐ ์๊ธฐ์ ์ ์ ๋ ์์ ์ ์ด๋ฆ์ ๊ฒ์ํ์ ๋ DB์ ์ฐ๋ํ์ฌ ํด๋น puuid๋ฅผ ๊ฐ์ ธ์ค๊ณ ๊ทธ๊ฒ์ api๋ก ํธ์ถํ๋ ์์ผ๋ก app.py๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
๋ฌธ์ ๋ ์๋ ๊ณํ์ด์๋ ์๋ฉ๋๋ ํฐ์ด๋ก ํ๊ธฐ์๋ ์ ์ ๊ฐ ๋๋ฌด ๋ง๋ค๋ ๊ฒ๋๋ค...
riot์ api rate limit์ ๋๊ธฐ์ ์ ์ DB๋ฅผ ๊ตฌ์ถํ๋ ค๋ฉด ๊ต์ฅํ ์ค๋ ์๊ฐ์ด ๊ฑธ๋ฆด ๊ฒ ๊ฐ์ต๋๋ค.
๊ทธ๋์ ์ฐ์ ๊ทธ๋๋๋ง์คํฐ(700๋ช ) ์ ๋์์ผ๋ก ์ ์ DB๋ฅผ ๋จผ์ ๊ตฌ์ถํ ๋ค์์ ํด๋น ์น์๋น์ค๊ฐ ์ฑ๊ณต์ ์ผ๋ก๊ตฌํ๋๋ค๋ฉด
๋ค์ด์ 1ํฐ์ด(1๋ง 2์ฒ๋ช )๋ฅผ ๋์์ผ๋ก ์ ์ DB๋ฅผ ๊ตฌ์ถ์ ๋ํ๋๊ฐ๊ฒ ์ต๋๋ค.
์ฐ์ ๋ค์ ๊ทธ๋๋๋ง์คํฐ๋ฅผ ํฐ์ด๋ฅผ ๋์์ผ๋ก DB๋ฅผ ๋ค์ ๊ตฌ์ถํ๊ฒ ์ต๋๋ค.
์๋น์ค ๊ฐ๋ฐ์ด ์ฒ์์ด์ด์ ๊ทธ๋ฐ์ง ์ฝ์ง ์๋ค์..