여러가지 템플릿을 찾아보다 마음에드는 친구를 발견하였다
링크
카드를 두개를 가져와서 왼쪽 오른쪽으로 배치를 하면서 솔로랭크와 자유랭크 두가지를 보여주기로 구상해보았다
상단링크의 html과 css를 그대로 가지고 왔다
근데 여기서 큰 문제가 발생하였다
새로운 css를 넣어주는 과정에서 여러가지 태그가 겹쳐버려서 전에 적용하였던 main.css말고 다른 태그들의 스타일이 들어가서 그림이 깨져버렸다
스타일 적용 순서를 찾아보았다
- 속성 값 뒤에 !important 를 붙인 속성
- HTML에서 style을 직접 지정한 속성
- #id 로 지정한 속성
- .클래스, :추상클래스 로 지정한 속성
- 태그이름 으로 지정한 속성
- 상위 객체에 의해 상속된 속성
그래서 제일 문제인 img 크기조절을 위해서 html에서 style을 직접설정하는 수를 사용하였다
navbar.html
{% load static %}
<head>
<style>
.logo {
width: 30%;
height: 30%;
}
</style>
</head>
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
<div class="container-fluid">
...
그 외에 내가 필요한 레이아웃 외에는 전부 삭제하고 내가 원하는 위치에 배치해보았다
search_card.html
{% load static %}
<head>
<link rel="stylesheet" type="text/css" href="{% static 'card.css' %}">
</head>
<div class="card-container_left">
<img src="https://i.postimg.cc/NfR2yhNs/image-equilibrium.jpg" alt="Spinning glass cube"/>
<main class="main-content">
<h1>{{res.name}}</h1>
<p>솔로랭크<br>{{tier.tier}} {{tier.rank}}</p>
<div class="flex-row">
<div class="level-base">
<h2>level : {{res.level}}</h2>
</div>
<div class="winrate">
<p>{{tier.wins}}승 {{tier.losses}}패 ({{tier.winrate}}%)</p>
</div>
</div>
</main>.
</div>
<div class="card-container_right">
<img src="https://i.postimg.cc/NfR2yhNs/image-equilibrium.jpg" alt="Spinning glass cube"/>
<main class="main-content">
<h1>{{res.name}}</h1>
<p>자유랭크<br>{{tier.tier}} {{tier.rank}}</p>
<div class="flex-row">
<div class="level-base">
<h2>level : {{res.level}}</h2>
</div>
<div class="winrate">
<p>{{tier.wins}}승 {{tier.losses}}패 ({{tier.winrate}}%)</p>
</div>
</div>
</main>.
</div>
card.css
/*
=========================
Fonts
=========================
font-family: 'Outfit', sans-serif;
*/
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap');
/*
========================
Variables
========================
*/
:root {
font-size: 15px;
/* Primary */
--var-soft-blue: hsl(215, 51%, 70%);
--var-cyan: hsl(178, 100%, 50%);
/* Neutral */
--var-main-darkest: hsl(217, 54%, 11%);
--var-card-dark: hsl(216, 50%, 16%);
--var-line-dark: hsl(215, 32%, 27%);
--var-lightest: white;
/* Fonts */
--var-heading: normal normal 600 1.5em/1.6em 'Outfit', sans-serif;
--var-small-heading: normal normal 400 1em/1em 'Outfit', sans-serif;
--var-para: normal normal 300 1em/1.55em 'Outfit', sans-serif;
}
/*
=======================
Setup
=======================
*/
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
margin: 0;
}
img {
width: 100%;
display: block;
}
a {
color: inherit;
}
/*
Eye view
https://i.postimg.cc/9MtT4GZY/view.png' border='0' alt='view */
/*
=========================
Font Styling
=========================
*/
h1 {
font: var(--var-heading);
color: var(--var-lightest);
padding-top: 20px;
}
h2 {
font: var(--var-small-heading);
color: var(--var-lightest);
/* padding on .coin-base */
}
p {
font: var(--var-para);
color: var(--var-soft-blue);
}
span {
color: white;
}
/*
=====================
Classes
=====================
*/
/* LAYOUT */
.card-container_left {
float: left;
position: relative;
width: 100%;
max-width: 400px;
margin: 2em auto;
background-color: var(--var-card-dark);
border-radius: 15px;
margin-bottom: 1rem;
margin-top: 10%;
margin-left: 30%;
padding: 2rem;
}
.card-container_right {
float: right;
position: relative;
width: 100%;
max-width: 400px;
margin: 2em auto;
background-color: var(--var-card-dark);
border-radius: 15px;
margin-bottom: 1rem;
margin-top: 10%;
margin-right: 30%;
padding: 2rem;
}
div.flex-row {
display: flex;
justify-content: space-between;
align-items: center;
}
div.level-base, .winrate {
display: flex;
align-items: center;
}
/* Details */
div.attribution {
margin: 0 auto;
width: 100%;
font: var(--var-para);
text-align: center;
padding: 1.5em 0 4em 0;
color: var(--var-line-dark);
}
.attribution a {
color: var(--var-soft-blue);
}
@media (min-width:600px) {
body {
font-size: 18px;
}
}
여기서 만든 html파일을 search_res.html
에 넣어주었다
search_res.html
{% extends 'base.html' %}
{%load static%}
{% block content %}
{% include "score/search_card.html" %}
{% endblock %}
마지막으로 화면에 표시할 정보들을 최종적으로 가공한다
score/views.py를 수정해주었다
def search_res(request):
summoner_result = {}
summoner_tier = {}
summoner_name = request.GET.get('search_text')
summoner_response = requests.get("https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/" + str(summoner_name), headers=request_headers)
if summoner_response.status_code == requests.codes.not_found:
return render(request, 'score/404_not_found.html')
summoner_response = summoner_response.json()
summoner_info = requests.get("https://kr.api.riotgames.com/lol/league/v4/entries/by-summoner/" + str(summoner_response['id']), headers=request_headers)
summoner_info = summoner_info.json()
if not summoner_info :
return render(request, 'score/no_solorank.html')
if summoner_info[0]['queueType'] == 'RANKED_FLEX_SR':
return render(request, 'score/no_solorank.html')
summoner_result['name'] = summoner_response['name']
summoner_result['level'] = summoner_response['summonerLevel']
summoner_result['icon'] = 'https://ddragon.leagueoflegends.com/cdn/13.24.1/img/profileicon/' + str(summoner_response['profileIconId']) + '.png'
summoner_tier['tier'] = summoner_info[0]['tier']
summoner_tier['rank'] = summoner_info[0]['rank']
summoner_tier['wins'] = summoner_info[0]['wins']
summoner_tier['losses'] = summoner_info[0]['losses']
summoner_tier['winrate'] = int(summoner_tier['wins'] / (summoner_info[0]['wins'] + summoner_info[0]['losses']) * 100)
# sum_result['profileIconId'] = summoners_response['profileIconId']
return render(request, 'score/search_res.html', {'res': summoner_result, 'tier': summoner_tier})
현재 솔로랭크 기록이 없는 소환사는 전부 예외처리 해버렸다
화면구성은 소환사 아이콘, 티어, 레벨 , 승패(승률)을 표시해주었다
에러페이지는 다음과 같이 구성해주었다
존재하지않는 소환사
최근 솔로랭크게임이 없는 소환사
이에 따른 html도 제작해주었다
templates/score/404_not_found.html
{% extends 'base.html' %}
{% block content %}
{% load static %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Not Found</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
text-align: center;
}
.container {
max-width: 600px;
margin: 100px auto;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #e44d26;
}
</style>
</head>
<body>
<div class="container">
<h1>404 Not Found</h1>
<p>해당 소환사가 존재하지 않습니다.</p>
</div>
</body>
{% endblock %}
최종 출력
api를 처음 사용해보아서 데이터를 가공한 후 화면에 뿌려주는 작업을 해보았다
처음에는 상용페이지를 그대로 만들어보자(?)는 엄청 큰 포부를 가지고 시작하였다
하지만 web초보에게 5일간의 프로젝트 기간이란 매우 짧은 기간이였다
처음 구상은 최근 20게임 전적까지 화면에 뿌려주려고 했었는데 데이터가공, 그리고 html/css를 어떻게 해야할지 감이 잡히지 않아서 그냥 솔로랭크카드만 보여주는식으로 긴급하게 전환하였다
아마 이 프로젝트는 시간을 들여 조금 더 개선된 페이지를 보여주기위해 노력해보고싶다
좋았던점은 프론트와 백엔드에 대한 대략적인 지식만 알고있었는데 실제로 구성은 어떤식으로 해야할지 깊게 고민해보는 시간이 되어서 좋았다
부족했다 느낀점은 확실히 기본이 부족하다고 느꼈다 주먹구구식으로 복사붙여넣기로 만들다보니 화면이 깨지기도하고 내가 원하는 모양으로 안나와서 속상했다
페이지를 확대를 하면 컴포넌트들의 위치가 뒤틀린다던지
여러개 배치를 하면 위치잡기가 어렵다던지 등등...
그리고 riot api가 일반사용자들에게 초당 검색횟수를 제한을 두어서 여러번 새로고침을 하면 죽는다는점
매번 api를 호출하다보니 너무 검색이 오래걸린다는점(이는 데이터베이스에 한번 검색한 소환사명을 저장해보면 어떨까 생각만 해보았다)
개인 이용자들을 위한 로그인 기능도 없었던거같고
흠...
정말 많은점이 아쉬웠다
좀 더 많은 공부를 한 뒤 새로 만들어보고싶다
근데 정말 재밌다 ㅎㅎ :)
1차 결과
링크
riot 아이디가 태그를 포함해서 바뀌었다고 들었는데 이 부분은 어떻게 처리하셨나요?