Python - Flask를 사용한 웹 프로그래밍
플라스크(Flask)란?
파이썬으로 웹 어플리케이션을 만들기 위한 프레임워크
- 플라스크 외에도 Django, Tornado, FastAPI 등 다양한 파이썬 웹 프레임워크가 존재한다
자세한 내용: ‘웹 프로그래밍의 개념’ 블로그 포스팅
MVC 구조에 따른 프로그래밍
MVC = Model, View, Controller 로 이루어진 웹 프로그래밍 구조. (자세한 설명은 마찬가지로 “웹 프로그래밍의 개념” 참고)
다음과 같이 디렉토리를 MVC 별로 나눠서 구현
- Model 디렉토리 안의 파일들이 데이터를 처리하는 Model 파트의 역할을 하고, 각 파일안에는 VO, DAO, Service 클래스 및 메소드 정의가 들어있다.
- Templates 디렉토리는 화면 구현에 필요한 html 및 css 파일, 즉 View 파트로 구성되어 있다.
- Routes 디렉토리 안에는 Model과 View를 연결시켜주는 Controller 파일들이 들어있다. 추가로, 디렉토리 바깥에 있는 app.py 도 Contoller에 포함된다.
플라스크 서버 작성 예 (app.py)
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return render_template('index.html')
if __name__ == '__main__':
app.run()
-
from flask import Flask, render_template → 플라스크 모듈을 가져온다
-
app = Flask(*name*) → app이라는 변수에 플라스크 객체를 생성
-
@app.route(‘/’) → app 객체를 이용하여 라우팅 경로를 설정, ‘/’ 를 호출했을 때 아래의 함수가 실행되도록 한다.
-
def root(): return render_template(‘index.html’)
→ 해당 라우팅 경로로 요청이 왔을 때 실행할 함수 정의. 여기서는 ‘/’ 경로가 호출되었을 때 index.html 파일을 바탕으로 페이지가 표시되도록 한다.
-
if __name__ == ‘__main__’: app.run()
→ 메인 모듈로 실행될 때 플라스크 서버 구동
로그인 구현
플라스크를 통해 웹사이트에서 로그인/로그아웃을 구현해 보았다.
먼저 회원관련 기능들을 컨트롤할 member_route.py 파일을 만든 뒤 플라스크 객체를 생성하고 회원 데이터를 처리할 모델을 연동한다.
from flask import Blueprint, render_template, request, redirect, session
import Board.Models.member as mem
bp = Blueprint('member', __name__, url_prefix='/member')
mem_service = mem.MemberService()
-
여기서 플라스크 객체 생성에 Flask 대신 Blueprint를 사용했다. 블루프린트는 대형 어플리케이션의 동작 방식을 단순화하고 공통된 패턴의 반복사용을 지원해준다.
여기서는 회원(Member)와 관련된 기능들을 묶고 자동으로 라우트 주소 앞에 ‘/member’ 가 붙도록 해준다. 이렇게 만든 블루프린트는 추후에 app.py 로 연결하여 하나의 어플리케이션에서 여러가지 기능들을 사용할 수 있도록 해준다.
-
member 모델을 불러와서 회원관리 기능들을 사용하기 위한 서비스 객체를 생성했다.
그 다음 로그인/로그아웃 페이지 구현을 위해 route를 만들고 view와 model을 연결시켜준다.
@bp.route('/login')
def login_form():
return render_template('member/login.html')
- ‘루트주소/member/login’을 호출하면 login.html 을 렌더링하여 화면에 출력한다.
@bp.route('/login', methods=['POST'])
def login():
msg = ''
path = 'member/login.html'
id = request.form['id']
pwd = request.form['pwd']
m = mem_service.getMem(id)
if m == None:
msg = "존재하지 않는 아이디"
else:
if pwd == m.pwd:
session['id'] = id
print(session['id'])
path = 'index.html'
msg = "로그인 성공"
else:
msg = "패스워드 불일치"
return render_template(path, msg=msg)
-
POST 방식으로 로그인 정보를 받아 DB에서 회원정보를 확인하고, 회원이 맞다면 로그인을 진행한다.
- 정보를 전달하는 방식은 url 끝에 쿼리스트링을 붙여서 전달하는 GET과 http 메시지의 Body를 통해 전달하는 POST가 있다.
-
이 때 회원의 로그인 상태를 유지하기 위해서 세션을 사용하여 서버에서 현재 로그인 되어있는 사용자 정보를 보관하도록 한다.
*세션&쿠키
- 세션: 상태 유지 및 웹 서비스 이용에 필요한 정보를 서버가 계속 가지고 있도록 함
- 쿠키: 사용자 정보를 서버 대신 클라이언트가 저장하도록 하고 서비스 제공을 위해 필요에 따라 쿠키에 담긴 정보를 서버가 확인한다. (주민등록증 같은 역할)
- 최근에는 Restful API로 개발을 많이 하면서 세션의 사용이 줄고 있다
- Restful API: 하나의 어플리케이션을 가지고 모바일과 웹 모두 사용이 가능하도록 한다. JSON을 사용하여 정보를 저장. 상태를 유지하지 않는다 = 세션을 저장하지 않는다.
사용자에게 보여지고 정보를 입력받을 login.html 은 다음과 같이 구현되어있다.
-
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>로그인</title> </head> <body> <h3>로그인</h3> <form action="/member/login" method="post"> id:<input type="text" name="id"><br/> pwd:<input type="password" name="pwd"><br/> <input type="submit" value="login"> </form> <a href="/member/join">회원가입</a> </body> </html>
로그아웃을 위한 route
@bp.route('/logout')
def logout():
if 'id' in session:
session.pop('id', None)
return redirect('/')
세션에 저장되어 있는 id를 확인하고 값을 제거함으로써 로그인 유지를 해제시킨다.
최종 결과물 (member_route.py)
로그인/로그아웃 외에도 회원가입, 회원정보 확인, 수정/삭제 기능들이 구현되어있다.
from flask import Blueprint, render_template, request, redirect, session
import Board.Models.member as mem
bp = Blueprint('member', __name__, url_prefix='/member') #블루프린트 객체 생성
mem_service = mem.MemberService()
@bp.route('/join')
def join_form():
return render_template('member/join.html')
@bp.route('/join', methods=['POST'])
def join():
id = request.form['id']
pwd = request.form['pwd']
name = request.form['name']
email = request.form['email']
mem_check = mem_service.getMem(id)
if mem_check == None:
m = mem.Member(id, pwd, name, email)
mem_service.addMem(m)
return redirect('/')
else:
return '이미 존재하는 ID입니다.'
@bp.route('/login')
def login_form():
return render_template('member/login.html')
@bp.route('/login', methods=['POST'])
def login():
msg = ''
path = 'member/login.html'
id = request.form['id']
pwd = request.form['pwd']
m = mem_service.getMem(id)
if m == None:
msg = "존재하지 않는 아이디"
else:
if pwd == m.pwd:
session['id'] = id
print(session['id'])
path = 'index.html'
msg = "로그인 성공"
else:
msg = "패스워드 불일치"
return render_template(path, msg=msg)
@bp.route('/logout')
def logout():
if 'id' in session:
session.pop('id', None)
return redirect('/')
@bp.route('/getmember')
def get_member():
if 'id' in session:
id = session['id']
m = mem_service.getMem(id)
else:
return '먼저 로그인을 해주세요'
return render_template('member/detail.html', mem=m)
@bp.route('/edit', methods=['POST'])
def edit_mem():
id = request.form['id']
pwd = request.form['pwd']
name = request.form['name']
email = request.form['email']
m = mem.Member(id, pwd, name, email)
mem_service.editMem(m)
return redirect('/')
@bp.route('/del')
def delete_mem():
if 'id' in session:
id = session['id']
mem_service.delMem(id)
session.pop('id', None)
return redirect('/')
댓글남기기