백엔드-프론트엔드를 연결하는 일이 익숙해지도록,
로또 추천 사이트 → 영화 검색 사이트 → 데일리모토 → DB 연습 → 멜로디쉐어
순서대로 개념을 충분히 다지면서 프로젝트를 진행
Flask는 만들 프로젝트의 폴더 구조가 정해져 있기 때문에 규칙을 지켜야 한다.
app.py
파일 생성templates
폴더 생성index.html
파일 생성flask 폴더 구조
- - -
flask
|— venv
|— app.py (서버)
|— templates
|— index.html (클라이언트 파일)
📌 규칙
templates
폴더는 반드시 고정해야 한다.- 첫 페이지는 일반적으로 파일명은
index.html
을 사용app.py
는 변경해도 되지만, 라이브러리 이름과 같은 것을 이름으로 사용하면 안된다.
원하는 라이브러리만 설치해서 관리할 수 있도록 담아두는 공구함, 가상환경을 설치하자. 폴더에 하나씩 설치한다.
Ctrl
+ Shift
+ P
Command
+ Shift
+ P
.venv
폴더 생성되면 가상 환경 생성 완료pip install flask
pip list
로 잘 설치되었는지 확인할 수 있음
Flask 서버를 만들 때, 항상 프로젝트 폴더 안에 templates 폴더
와 app.py 파일
만들고 시작한다.
프로젝트 폴더
ㄴ/templates 폴더 (html파일 넣음)
ㄴapp.py 파일
Flask로 로또 추천 사이트를 만들어보자.
Flask는 프레임워크로, 서버를 구동시켜주는 편한 코드 모음이다.
- 웹 서버를 구동하는데 필요한 복잡한 코드들을 쉽게 가져다 쓸 수 있다.
- 프레임워크는 3분 요리/소스 세트
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
if __name__ == '__main__':
# app.run(debug=True) # 포트 5000 이미 사용중이면 포트에 5001 넣어주자.
app.run('0.0.0.0', port=5001, debug=True)
python app.py
또는 python3 app.py
을 입력하여 서버를 킨다.Ctrl+C
@app.route('/')
부분을 수정하면 URL을 나눌 수 있다.
route('/')
내의 주소가 달라야 한다.함수명
도 달라야 한다.from flask import Flask
app = Flask(__name__)
# ✅ / 홈 url 설정
@app.route('/') # 🔥 루트 주소
def home(): # 🔥 함수명
return 'This is Home!'
# ✅ /mypage url 설정
@app.route('/mypage') # 🔥 루트 주소
def mypage(): # 🔥 함수명
return 'This is My Page!'
if __name__ == '__main__':
app.run(debug=True)
로컬호스트:5001/mypage
templates 폴더는 HTML 파일을 담아두고, 불러오는 역할을 한다.
/templates/index.html
파일 생성하기
app.py
에서 flask 내장함수인 render_template("index.html")
를 이용하여 HTML 파일을 불러올 수 있다.
프레임워크의 위력! 간단!
# 🔥 render_template 임포트
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
# 🔥 render_template() 사용하여 html 파일 불러오고 return
return render_template('index.html')
@app.route('/mypage')
def mypage():
return 'This is my page!'
if __name__ == '__main__':
app.run('0.0.0.0', port=5001, debug=True)
보통 홈페이지에서는 데이터에 따라서 값이 변하는 경우가 많다.
서버에서 사용자가 보게 될 웹 페이지로 데이터를 넘겨주는 방법을 알아보자.
데이터를 넘겨주려면 변수에 원하는 데이터를 넣고, render_template('index.html', 데이터이름=데이터)
방식으로 코드를 추가하면 된다.
@app.route('/')
def home():
# 🔥 name 변수에 데이터 담기
name = "예티"
# render_template 사용하여 html 파일 불러온 것 반환하기
# 🔥 data에 name 데이터 넣어 보내기
return render_template('index.html', data=name)
그러면 HTML에서 정해둔 데이터 이름(지금은 data)로 Python 서버의 데이터를 사용할 수 있다.
<!DOCTYPE html>
<html lang="kor">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>홈</title>
</head>
<body>
<h1>안녕하세요, {{data}}님!</h1>
</body>
</html>
# 딕셔너리에 여러 변수 담기
context = {
"HTML에서 사용할 이름": 변수명,
"HTML에서 사용할 이름": 변수명,
}
@app.route('/')
def home():
# 🔥 변수에 데이터 담기
name = "예티"
lotto = [5, 7, 13, 20, 28, 36]
# 🔥 여러 데이터 넘겨주기 위해 딕셔너리에 담기
context = {
"name": name,
"lotto": lotto,
}
# render_template 사용하여 html 파일 불러온 것 반환하기
# 🔥 data에 context 딕셔너리 넣어 보내기
return render_template("index.html", data=context)
<!DOCTYPE html>
<html lang="kor">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>홈</title>
</head>
<body>
<h1>안녕하세요, {{ data.name }}님!</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
</body>
</html>
📝 html 반복문 숏 컷
ffor
: 반복문{% for element in collection %} {{ element|e }} {% endfor %}
<h1>안녕하세요, {{ data.name }}님!</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
{% for number in data.lotto %}
{{ number }}
{% endfor %}
</body>
ol
태그는 ordered list의 약자로 숫자나 알파벳 같은 마커를 붙여 놓는다. <body>
<h1>안녕하세요, {{ data.name }}님!</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
<ol>
{% for number in data.lotto %}
<li>{{ number }}</li>
{% endfor %}
</ol>
</body>
실제 로또처럼 자동으로 번호를 뽑아서 등수를 계산해주는 프로그램을 만들어 보자.
로또는 무작위로 1~45까지 숫자 6개를 중복해서 뽑아야 한다.
import random # 🔥 random 메서드 임포트
# 🔥 1~46 까지 숫자 중, 6개의 로또 번호 랜덤하게 골라와 list로 반환하는 함수
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
# 🔥 함수 호출하여 반환 된 값(6개 랜덤 숫자를 가진 list)을
# 🔥 lotto_numbers 변수에 담기
lotto_numbers = generate_lotto_numbers()
print("추출된 로또 번호:", lotto_numbers)
from flask import Flask, render_template
import random # 🔥 랜덤 메소드
app = Flask(__name__)
@app.route('/')
def home():
# name 변수에 데이터 담기
name = "예티"
lotto = [5, 7, 13, 20, 28, 36]
# 🔥 1~46 까지 숫자 중, 6개의 로또 번호 랜덤하게 골라와 list로 반환하는 함수
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
# 🔥 함수 호출하여 반환 된 값(6개 랜덤 숫자를 가진 list)을
# 🔥 lotto_numbers 변수에 담기
lotto_numbers = generate_lotto_numbers()
print("추출된 로또 번호:", lotto_numbers)
# 여러 데이터 넘겨주기 위해 딕셔너리에 담기
context = {
"name": name,
"lotto": lotto,
"random_lotto": lotto_numbers,
}
# ...
data.키이름
으로 데이터를 가져온다. 헷갈리지 말자~
<body>
<h1>안녕하세요, {{ data.name }}님!</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
<ol>
{% for number in data.lotto %}
<li>{{ number }}</li>
{% endfor %}
</ol>
<h2>랜덤 로또 번호: {{ data.random_lotto }}</h2>
</body>
집합 자료형 특징: set()
- 특징
- 중복을 허용하지 않는다.
- 순서가 없다(Unordered).
- set은 중복을 허용하지 않는 특징 때문에
데이터의 중복을 제거하기 위한 필터로 종종 사용된다.- 리스트나 튜플은 순서가 있기(ordered) 때문에 인덱싱을 통해 요솟값을 얻을 수 있지만, set 자료형은 순서가 없기(unordered) 때문에 인덱싱을 통해 요솟값을 얻을 수 없다.
- 이는 마치 딕셔너리와 비슷하다. 딕셔너리 역시 순서가 없는 자료형이므로 인덱싱을 지원하지 않는다.
- 만약 set 자료형에 저장된 값을 인덱싱으로 접근하려면 리스트나 튜플로 변환한 후에 해야 한다.
set() 메서드로 교집합 구하기
&
를 이용하면 교집합을 간단히 구할 수 있다.s1 = set([1, 2, 3, 4, 5, 6]) s2 = set([4, 5, 6, 7, 8, 9]) print(s1 & s2) # [4, 5, 6]
def count_common_elements(list1, list2):
# set() 메서드: 집합 자료형 만들기
common_elements = set(list1) & set(list2)
# len() 메서드: list의 length 구하기
return len(common_elements)
# 예시 리스트
list1 = [1, 2, 3, 4, 9]
list2 = [4, 5, 6, 7, 8]
common_count = count_common_elements(list1, list2)
print("두 리스트에서 공통된 요소의 개수:", common_count)
from flask import Flask, render_template
import random # 🔥 랜덤 메소드
app = Flask(__name__)
@app.route('/')
def home():
# name 변수에 데이터 담기
name = "예티"
lotto = [5, 7, 13, 20, 28, 36]
# ✅ 랜덤 숫자 함수
# 1~46 까지 숫자 중, 6개의 로또 번호 랜덤하게 골라와 list로 반환하는 함수
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
# 함수 호출하여 반환 된 값(6개 랜덤 숫자를 가진 list)을 lotto_numbers 변수에 담기
lotto_numbers = generate_lotto_numbers()
# print("추출된 로또 번호:", lotto_numbers)
# ✅ 로또 당첨 확인 함수
def count_common_elements(list1, list2):
# set() 메서드: 집합 자료형 만들기
common_elements = set(list1) & set(list2)
# len() 메서드: list의 length 구하기
return len(common_elements)
# 내가 고른 lotto 숫자 list, 랜덤 로또 숫자 리스트 인자로 보내고
# 반환된 교집합 리스트를 common_count 변수에 할당
common_count = count_common_elements(lotto, lotto_numbers)
# print("두 리스트에서 공통된 요소의 개수:", common_count)
# 여러 데이터 넘겨주기 위해 딕셔너리에 담기
context = {
"name": name,
"lotto": lotto,
"random_lotto": lotto_numbers,
"common_count": common_count,
}
# render_template 사용하여 html 파일 불러온 것 반환하기
# data에 name 데이터 넣어 보내기
return render_template("index.html", data=context)
@app.route('/mypage')
def mypage():
return 'This is my page!'
if __name__ == '__main__':
app.run('0.0.0.0', port=5001, debug=True)
📝 html 조건문 숏 컷
fif
: if 조건문{% if expression %} blockofcode {% endif %}
fife
: if else 조건문{% if expression %} blockofcode {% else %} blockofcode {% endif %}
felif
: if elif else 조건문{% if expression %} blockofcode {% elif expression2 %} blockofcode {% else %} blockofcode {% endif %}
<body>
<h1>안녕하세요, {{ data.name }}님!</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
<ol>
{% for number in data.lotto %}
<li>{{ number }}</li>
{% endfor %}
</ol>
<h2>랜덤 로또 번호: {{ data.random_lotto }}</h2>
{% if data.common_count == 6 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 1등입니다.</h2>
{% elif data.common_count == 5 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 2등입니다.</h2>
{% elif data.common_count == 4 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 3등입니다.</h2>
{% elif data.common_count == 3 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 4등입니다.</h2>
{% else %}
<h2>💥{{ data.common_count }}개 맞았습니다! 탈락!💥</h2>
{% endif %}
</body>
로또 홈페이지를 만들려고 하는데, HTML로 공 6개 있는 홈페이지 코드를 작성해보자.
<style>
.ball {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #FFD700;
color: #FFFFFF;
text-align: center;
line-height: 30px;
margin-right: 5px;
}
.ball--random {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: red;
color: #FFFFFF;
text-align: center;
line-height: 30px;
margin-right: 5px;
}
</style>
<h2>로또 번호</h2>
{% for number in data.lotto %}
<div class="ball">{{ number | e}}</div>
{% endfor %}
<h2>랜덤 로또 번호</h2>
{% for number in data.random_lotto %}
<div class="ball--random">{{ number | e}}</div>
{% endfor %}
/static/image 폴더
를 만든다.이미지 태그는
url_for
사용
- url_for는 보통 경로(위치)를 표현할 수 있다.
- 이미지 경로 부분을 바꾸면 된다.
<img src="{{ url_for('static', filename='이미지 경로') }}" alt="캐릭터 이미지" />
<style>
.img{
height: 100px;
}
</style>
<div>
<img class="img" src="{{ url_for('static', filename='image/pochacco.png') }}" alt="캐릭터 이미지" />
</div>
/movie 경로로 접속하면 movie.html 페이지를 보여주도록 코드를 작성
@app.route('/movie')
def movie():
return render_template('movie.html')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>영화 검색</h1>
</body>
</html>
사용자로부터 입력(데이터)를 받아서 처리하기
form
은 회원 가입, 검색, 게시글 작성에 사용form
에 데이터를 입력하고 전송 버튼을 누르면, 입력한 데이터가 서버로 전송된다.action
에는 url_for
로 데이터를 보낼 url을 설정한다.name
속성을 활용해서 데이터에 명찰을 붙인다.<form action="{{ url_for('movie') }}"> //데이터를 보낼 곳
<input type="text" name="query"> //데이터 이름 지정
<button type="submit">검색</button>
</form>
request.args.get('name 속성명')
를 활용하면 Form에서 입력한 데이터를 받을 수 있다.from flask import Flask, render_template, request # request 라이브러리로 데이터 요청
@app.route('/movie')
def movie():
print(request.args.get('query')) # 🔥 Form에서 입력한 데이터를 받기
return render_template('movie.html')
res = requests.get(
"http://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieList.json?key=f5eef3421c602c6cb7ea224104795888"
)
rjson = res.json()
movie_list = rjson["movieListResult"]["movieList"]
영화진흥위원회 API는 GET 방식으로 호출한다.
https://www.google.com/search?q=파이썬
형태로 주소값이 변경된다.기생충을 검색하고 싶다면, 기존 주소 끝에 &movieNm=기생충
을 붙이면 된다.
영화진흥위원회 API에서 응답으로 준 데이터 구조를 파악하면서 작성하자..
pip install requests
설치하기
app.py 작성
from flask import Flask, render_template, request
import random # 🔥 랜덤 메소드
import requests # 🔥 pip로 설치
@app.route("/movie")
def movie():
# 🔥 사용자가 입력한 검색어 받기
query = request.args.get('query')
# 🔥 사용자 입력 값인 query를 영화진흥원 api에 넣어 영화명으로 검색
# 🔥 URL 앞에 f 붙여야 {변수} 넣어 쓸 수 있다.
URL = f"http://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieList.json?key=f5eef3421c602c6cb7ea224104795888&movieNm={query}"
# 🔥 requests.get() 메서드로 응답 get
res = requests.get(URL)
rjson = res.json()
# 🔥 데이터 넣기
movie_list = rjson["movieListResult"]["movieList"]
# 🔥 movie_list를 data에 넣어 "movie.html"로 보내기
return render_template("movie.html", data=movie_list)
<body>
<h1>영화 검색</h1>
<form action="{{ url_for('movie') }}">
<input type="text" name="query">
<button type="submit">검색</button>
</form>
{% for movie in data %}
<!-- <p>{{ movie }}</p> -->
<p>영화 제목: {{ movie.movieNm }}</p>
<p>타입: {{ movie.typeNm }}</p>
{% if movie.directors %}
<p>감독: {{ movie.directors[0].get('peopleNm') }}</p>
{% endif %}
<hr>
{% endfor %}
</body>
아이언맨
검색한 결과