Python - Flask를 사용한 웹 프로그래밍

3 분 소요

플라스크(Flask)란?

파이썬으로 웹 어플리케이션을 만들기 위한 프레임워크

  • 플라스크 외에도 Django, Tornado, FastAPI 등 다양한 파이썬 웹 프레임워크가 존재한다

자세한 내용: ‘웹 프로그래밍의 개념’ 블로그 포스팅

MVC 구조에 따른 프로그래밍

MVC = Model, View, Controller 로 이루어진 웹 프로그래밍 구조. (자세한 설명은 마찬가지로 “웹 프로그래밍의 개념” 참고)

다음과 같이 디렉토리를 MVC 별로 나눠서 구현

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('/')

댓글남기기