1.1 웹
1.1.1 웹이란?
웹은 http 프로토콜을 통해 html, image, video, sound와 같은 데이터를 주고받는다.
클라이언트(웹 브라우저)는 요청(request), 서버는 응답(response)을 담당한다.
통신 과정은 다음과 같다.
- 클라이언트는 접속하고자 하는 URL 주소(www.naver.com)을 웹 브라우저에 입력
- 웹 브라우저는 DNS 서버로 가서 www.naver.com의 IP주소를 알아내고 그 IP주소에 데이터(html, image 등)를 요청
- 웹 서버는 요청을 보낸 클라이언트에게 http stauts code 200을 리턴하고 패킷에 데이터를 담아 보냄
- 웹 서버는 TCP 통신에 따라 패킷을 모아 데이터로 만들고 렌더링 해서 사용자에게 최종적으로 보여줌
DNS 서버란?
웹 서버의 진짜 주소는 111.111.111.111 와 같은 숫자와 콤마로 이루어진 IP 주소이다.
우리가 입력하는 www.naver.com을 도메인이라고 하는데, IP 주소를 사람들이 사용하기 쉬운 문자로 바꾼 것이다.
DNS 서버는 이러한 도메인 주소를 IP주소로 리턴해주는 서버를 말한다.
http status code란 ?
http의 요청 결과를 나타내는 코드로 다음과 같이 5개의 그룹으로 나뉜다.
- 100 : 정보를 제공하는 응답
- 200 : 성공적인 응답
- 300 : 리다이렉트
- 400 : 클라이언트 에러
- 500 : 서버 에러
TCP란?
서버와 클라이언트 통신에서 신뢰성을 보장하는 프로토콜
3 way handshaking을 통해 통신하므로 데이터 유실의 우려에서 벗어날 수 있다.
ack를 리턴해서 잘 받았다는 신호를 주고 받고 다음 패킷(데이터를 일정 크기로 자른 것)의 정보를 리턴한다.
1.1.2 웹 서버와 웹 애플리케이션 서버
<웹 서버>
- 웹 서버는 웹 브라우저가 요청한 정적 데이터(html, css, image)를 반환한다.
- EX ) Apache, NGINX
<웹 어플리케이션 서버>
- 웹 서버가 정적인 데이터를 반환한다면, 웹 어플리케이션 서버는 동적인 데이터를 반환한다.
- 즉, DB를 조회해서 데이터를 넘겨주거나 다양한 서버 로직을 처리해 반환한다.
- Ex) Flask, Django
그렇다면 WAS 만 필요한 것이 아닌가? 웹 서버는 필요 없는거 아닌가?
웹 서버는 플라스크, 장고가 쓰는 파이썬언어를 이해하지 못한다.
따라서 웹 서버가 파이썬 코드를 이해할 수 있게 도와주는 것이 WSGI(Web server Gateway Interface)이다.
웹 브라우저(client)가 웹 서버에 요청을 보내면 웹 서버는 WSGI를 통해 WAS(Application Framework)를 사용하여 동적인 콘텐츠를 반환받게 된다.
WSGI 종류로는 mod_wsgi, gunicorn등이 있다.
1.2 플라스크 기초
1.2.1 라우팅
라우팅이란?
URL을 해당 URL에 맞는 기능과 연결해줌으로써 적절한 목적지를 찾아주는 기능을 한다.
플라스크 앱을 만드는 법과 라우팅을 통해 API 를 만드는 법을 배워본다.
API 서버가 뭘까?..
API 라는 단어는 여러 도메인에서 쓰이다보니.. 항상 헷갈린다
API 서버란?
API 서버란 말그대로 API를 제공해주는 서버를 API 서버라고한다.
한번에 이해가 잘 안되니까, API 서버가 왜 필요한지 알아야한다.
클라이언트(웹)에서 쓰는 모든 정보들은 웹 자체에 있는 것이 아니라 모두 서버에서 끌어와서 사용하는 것이다.
클라이언트에서 모든 데이터를 저장한다는 것은
1. 공간적한계 2. 데이터 공유 시 서버를 통해 해야하는 것 3. 보안상의 문제
와 같은 이유로 데이터를 클라이언트로부터 분리해서 서버에 저장해야하는 것이다.
따라서 클라이언트는 계속해서 정보에 접근하고 정보를 수정하기 위해 요청을 보내야한다.
이러한 요청을 보내는 방법을 약속해둔 것이 API이다!!
즉, 여기서 API를 만들어본다는 것은 클라이언트 페이지에 나오는 정보들을 수정해달라고
서버에게 요청을 보내는 API를 만들어보는 것!!
다음은 hello, world 를 출력하는 기본적인 플라스크 코드이다.
hello.py
# flask 모듈에서 Flask 클래스를 임포트해준다. 이 클래스의 인스턴스로 WSGI 애플리케이션을 만들 수 있다.
from flask import Flask
# Flask 클래스의 인스턴스를 생성한다.
# 이 때 인자로 __name__이 들어가는데 해당인자는 정적 파일과 템플릿을 찾는데 쓰인다.
app = Flask(__name__)
# route 데코레이션을 통해 URL을 생성한다.
@app.route('/index')
@app.route('/')
def hello_world():
return 'Hello,World!'
@app.route('/index') , @app.route('/')가 의미하는 것이 뭘까?
이는 Flask에서 URL 라우팅을 설정하는데 사용되는 데 사용되는 데코레이터라고한다.
이 코드는 두 가지 다른 URL 경로를 처리하도록 설정된 Flask 함수이다.
즉, 웹 브라우저에서 /index 경로나 / 루트 경로로 요청이 들어오면 이 함수가 실행되는 것이다.
해당 코드로 경로에 따라 다른 웹페이지를 표시할 수 있으며 이 경로를 통해 Flask가 요청과 응답을 처리하게 되는 것이다.
이 플라스크를 구동하려면 FLASK_APP 환경 변수를 등록해야한다.
FLASK_ENV 값으로 developement로 설정해주어 변경된 코드가 적용될 수 있도록 한다.
현재 디버그 모드가 꺼져있기 때문에 코드를 수정해도 적용되지 않는다.
flask run 명령어로 실행할 때, 위와 같이 --debug 옵션을 추가하여 디버그 모드로 Flask를 실행할 수 있다.
debug 모드에서 코드를 다음과 같이 수정하고 다시 http://127.0.0.1:5000으로 들어가본다.
디버그 모드에서는 코드가 수정된 것이 바로 반영된 것을 확인할 수 있다.
따라서 NameError가 뜬다.
+ 플라스크 구동 코드 가이드 (나 보려고ㅎ)
# 가상환경 폴더
cd flask
# 가상환경 활성화, 파이썬 인터프리터 설정
. env/bin/activate
#환경변수 등록
export FLASK_APP=hello.py
#developement 환경에서는 코드 수정시 바로 반영됨
export FLASK_ENV=development
# debug 모드로 실행
flask --debug run
링크 주소의 path parameter에는 string, int,float, uuid 등 다양한 type이 들어갈 수 있다.
uuid란?
네트워크 상에서 중복되지 않은 ID를 만들기 위한 표준 규약
from flask import Flask
# Flask 클래스의 인스턴스를 생성한다.
# 이 때 인자로 __name__이 들어가는데 해당인자는 정적 파일과 템플릿을 찾는데 쓰인다.
app = Flask(__name__)
#path parameter 종류에 따른 결괏값 반환
@app.route('/users/<username>')
def get_users(username):
return username
@app.route('/posts/<int:post_id>')
def get_post(post_id):
return str(post_id)
@app.route('/uuid/<uuid:uuid>')
def get_uuid(uuid):
return uuid
1.2.2 메서드
메서드에 따른 로그인 또는 로그인 폼을 보여주는 코드이다.
methods 인자로 허용할 메서드를 정할 수 있다.
이때 서로 다른 메서드를 정할 수 있으며 request.method로 구분한다.
# flask 모듈에서 Flask클래스, request 객체 import
from flask import Flask,request
#Flask 애플리케이션 생성
# Flask 클래스의 인스턴스를 생성한다.
# 이 때 인자로 __name__이 들어가는데 해당인자는 정적 파일과 템플릿을 찾는데 쓰인다.
app = Flask(__name__)
#path parameter 종류에 따른 결괏값 반환
@app.route('/login', methods=['GET','POST'])
def login():
if request.method == 'POST' :
return do_the_login()
else:
return show_the_login_form()
POST 메서드로 해당 URL에 접속하면 do_the_login() 함수가 실행되고 GET 메서드로 접속하면 show_the_login_form() 함수가 실행된다. 다음과 같은 형식은 공통 로직을 사용해야할 때 쓰이는 방법이다.
공통로직을 타지 않아도 라우팅을 메서드 개수만큼 만드는 방법도 있다.
# flask 모듈에서 Flask클래스, request 객체 import
from flask import Flask,request
#Flask 애플리케이션 생성
app = Flask(__name__)
@app.route('/login', methods=['GET'])
def login_page():
return show_the_login_form()
@app.route('/login', methods=['POST'])
def login():
return do_the_login()
1.2.3 정적 파일과 템플릿
플라스크로 웹사이트를 만들면 정적파일(css,js, image,..) 와 템플릿(html)등이 필요하다.
Flask 객체는 폴더 지정을 통해 정적 파일과 템플릿 위치를 추적한다.
Flask 객체에 static_folder, template_folder를 지정해준다.
static_folder 를 지정해주면 /static URL로 접근할 수 있다.
template_folder를 지정해주면 render_template에서 해당 템플릿 파일을 찾을 수 있다.
from flask import Flask,render_template
app = Flask(__name__, static_folder='static', template_folder='templates')
@app.route('/hello')
def hello():
return render_template('hello.html')
static 폴더에 지정해둔 내가 원하는 이미지가 출력되는 것을 확인할 수 있다.
1.2.4 템플릿 엔진
플라스크는 템플릿 엔진으로 Jinja2를 사용한다.
웹 템플릿 엔진이란?
웹페이지 연산 같은 작업들을 수행시켜 웹페이지를 편하게 완성시킬 수 있도록 도와주는 기능이다.
이를 사용하면 다음과 같은 장점이 있다.
1. html코드 안에서 파이썬 코드를 작성해줄 수 있는 기능을 제공함으로써 연산을 수행하도록 한다.
2. 중복된 코드를 줄일 수 있다.
이번절에서는 Jinja2를 사용하여 웹페이지를 구성해본다.
Jinja2에는 재사용을 위한 템플릿 상속 기능이 있다.
{% block %}으로 상속받은 파일에서 코드를 추가할 수 있게 제공한다.
layout/base.html
<!doctype html>
<html>
<head>
{% block head %}
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
전체적인 틀은 {% extend %} 키워드를 사용하되, 반복적이거나 따로 분리하고 싶은 부분은 {% include %} 키워드를 사용한다.
hello.html
<!--base.html 템플릿을 확장-->
{% extends 'layout/base.html' %}
<!--head 블록 재정의-->
{% block head %}
<title>hello</title>
{% endblock %}
{% block body %}
{% if name %}
<h1>Hello, {{ name }}!</h1>
<!--name 변수가 없다면-->
{% else %}
<h1>Hello, World!</h1>
{% endif %}
{% include 'hello2.html' %}
{% endblock %}
hello2.html
{% if name %}
<h1>Hello2, {{ name }}!</h1>
<!--name 변수가 없다면-->
{% else %}
<h1>Hello, World!</h1>
{% endif %}
1.2.5 리다이렉션과 에러
redirect()함수는 parameter에 적어둔 url로 이동시켜주는 역할을 한다.
에러를 발생시키고 싶다면 abort()를 사용한다.
다음 예제에서는 로그인하지 않은 유저가 /users를 호출하려고 했을 때 에러 핸들링을 어떻게 처리하는지를 보여준다.
from flask import Flask,redirect, url_for, abort
app = Flask(__name__)
@app.errorhandler(403)
def permission_denied(error):
return '403', 403
@app.route('/')
def index():
return redirect(url_for('users_list'))
@app.route('/users')
def user_list():
abort(403)
어려워서.. 직접쓰면서 코드를 해석해보자면
403을 반환하는 것을 확인할 수 있다.
1.3 플라스크 응용
1.3.1 플러거블 뷰
앞에서 계속 썼던 형태처럼 플라스크는 데코레이터와 함수 기반으로 API를 작성한다. 이를 함수 기반 뷰라고 하는 것!
다음은 이제까지 계속 했던 함수 기반 뷰이다.
from flask import Flask, render_template
# Flask 클래스의 인스턴스를 생성한다.
app = Flask(__name__)
# route 데코레이션을 통해 URL을 생성한다.
@app.route('/users')
def users_list():
users = []
return render_template('users.html', users=users)
이 코드를 클래스 기반 뷰 방식인 플러거블 뷰를 통해 만들어본다.
from flask import Flask, render_template, typing as ft
from flask.views import View
# Flask 클래스의 인스턴스를 생성한다.
app = Flask(__name__)
#View 클래스를 확장한 사용자 클래스 생성
class UserList(View):
def dispatch_request(self)
users =[]
# user.html에 users를 전달
return render_template('users.html', users=users)
#'/users' URL을 등록하고 as_view()를 사용하여 'user_list'이름의 뷰를 등록
app.add_url_rule('/users', view_func=UserList.as_view('user_list'))
책에서 설명을 보면 as_view()함수에 엔드포인트 이름을 전달해서 뷰를 구현한다고 한다.
엔드포인트란?
API가 두 시스템(어플리케이션)이 상호작용 할 수 있게 하는 프로토콜의 총집합이라면,
ENDPOINT는 API가 서버에서 리소스에 접근할 수 있도록 가능하게 하는 URL이다.
즉, 서버 API는 클라이언트(웹) 과 서버 사이에서 요청과 응답을 전달해주는 매개체의 역할을 해주는데
이렇게 API 가 서버에 접근할 수 있게 해주는 URL을 ENDPOINT라고 이해하면 될 것 같다!
해당 포스팅은 책 <처음 배우는 웹 플라스크 프로그래밍>을 기반으로 작성되었습니다.