Flask

Flask 이용한 웹사이트 제작기 (4) - auth view code 작성

sseram 2023. 7. 18. 23:41
반응형

2023.07.16 - [Flask] - Flask 이용한 웹사이트 제작기 (3) - DB 연결

 

Flask 이용한 웹사이트 제작기 (3) - DB 연결

2023.07.15 - [Flask] - Flask 이용한 웹사이트 제작기 (2) - 폴더 구조/ hello world 띄우기 Flask 이용한 웹사이트 제작기 (2) - 폴더 구조/ hello world 띄우기 2023.07.10 - [Flask] - Flask 이용한 웹사이트 제작기 (1) -

donot-simsim.tistory.com

 

 

Blueprints and Views

 

이 장부터 뭔가 코드와 글이 꽉꽉 들어차있는 느낌이다.

일단... 진행하자.

 


- Create a Blueprint

 

Blueprint는 애플리케이션 개체를 미리 요구하지 않고 애플리케이션 기능을 정의할 수 있는 개체입니다. 플라스크와 동일한 장식기를 사용하지만 나중에 등록할 수 있도록 기록하여 응용프로그램의 필요성을 줄입니다.

 

blueprint가 뭔고 하니.. 실제로 app이 만들어지기 전, view나 다른 code들을 app에다 직접 붙이는 게 아니라, view / code 를 blueprint에 먼저 붙이고, 그 blueprint를 app에 붙이는 방식으로 사용 가능하게끔 도와주는 거라고 한다.

그러니까 결국 app 생성 전에, 앱이 가지고 있는 property들을 만들어둔 후, 앱이 생성될 때 한번에 붙이는 거라고 생각하면 되지 않을까?

 

 

이 듀토리얼에서는 blog post blueprint 와 authentication blueprint 두 가지의 모듈을 만들 것이라고 한다.

 

먼저 auth.py

 

// flaskr/auth.py

import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash

from flaskr.db import get_db

bp = Blueprint('auth', __name__, url_prefix='/auth')

이런 식으로 /auth 라는 url prefix가 나왔을 때 붙게 할 'auth' blueprint를 만들 수 있다.

만든 blueprint는 

 

// flaskr/__init__.py


def create_app():
    app = ...
    
    ~~~

    from . import auth
    app.register_blueprint(auth.bp)

	~~~

    return app

 

이렇게 app에 register_blueprint를 통하여 붙일 수 있다!

 

 

 

- The First View: Register

 

그 다음은 register view이다. 목표는 /auth/register 로 url이 들어왔을 때, 회원가입하는 창을 띄워주고, 사용자가 해당 칸을 다 채워서 제출하면 valid 검사 후 새로운 user를 만들어 주는 것 까지가 목표이다.

 

html 말고 먼저 내부 동작을 위한 코드는

 

// flaskr/auth.py

@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None

        if not username:
            error = 'Username is required.'
        elif not password:
            error = 'Password is required.'

        if error is None:
            try:
                db.execute(
                    "INSERT INTO user (username, password) VALUES (?, ?)",
                    (username, generate_password_hash(password)),
                )
                db.commit()
            except db.IntegrityError:
                error = f"User {username} is already registered."
            else:
                return redirect(url_for("auth.login"))

        flash(error)

    return render_template('auth/register.html')

 

 

이러하다. auth.py 밑에다 아래와 같은 함수를 추가해 주었다.

적당히 읽어 보면.. request form에서 username, password를 가져와서 저장하고, valid 검사를 수행한 뒤, db에다 저장한다.

끝까지 잘 되었으면 auth.login 창으로 넘어가고, 아니라면 error 메시지를 띄운 후 다시 auth/register 창으로 넘어간다.

 

근데 여기서 redirect(url_for("auth.login")) 과 

return_template('auth/register.html'). 이렇게 두 가지의 방법으로 다른 창으로 넘어가게 했는데... 

 

먼저 url_for은 현재 이름을 기준으로 url을 생성한다고 한다. 여기서 login이라는 url path를 만드는 듯 하다.

 

redirect와 render_template의 차이가 궁금해서 찾아 보니.

 

 

redirect :

- 클라이언트를 다른 URL로 보내는 데 사용.

- 다른 페이지로 이동할 떄 사용

  

render_template :

- HTML 템플릿 파일을 렌더링하여 동적인 콘텐츠를 생성하는 데 사용.

- 페이지를 동적으로 생성할 때 사용합니다.

 

이라고 chat gpt님께서 알려주셨다.

 

 

즉 현재 창을 그대로 두고 다른 화면을 보여 줄 것이냐,

아예 다른 곳으로 갈 것이냐, 의 차이인 듯 하다.

 

 

- Login

 

그다음은 위에서 보내주기만 한 login 창을 이어서 만든다.

 

// flaskr/auth.py

@bp.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None
        user = db.execute(
            'SELECT * FROM user WHERE username = ?', (username,)
        ).fetchone()

        if user is None:
            error = 'Incorrect username.'
        elif not check_password_hash(user['password'], password):
            error = 'Incorrect password.'

        if error is None:
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))

        flash(error)

    return render_template('auth/login.html')

 

위와 패턴이 비슷비슷 하다.

 

처음 보는 게 몇 개 있는데

- fetchone : 쿼리문에 맞는 row 하나만 가져온다. fetchall 이면 전부.

- check_password_hash : 비밀번호라 그런지, 바로 값을 비교하지 않고 안전하게 비교한다.

- session : 사용자의 상태 / 정보를 유지하기 위한 정보. login에 성공하면 해당 id를 새로운 session으로 저장한다.

                 이를 통해 우리가 여타 다른 홈페이지에서 보던 [로그인 하면 내 정보가 홈페이지에 뜨는] 그 상황을 만들 수 있                   나보다!

 

 

그리고. user가 auth 쪽으로 들어오기 전, session에서 현재 로그인된 id가 무엇인지 확인해오는 함수를 auth.py에 추가한다.

 

// flaskr/auth.py

@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')

    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()

 

 

bp.before_app_request는 어떤 url이 오든, 해당되는 함수가 실행되기 전 불리는 함수라고 한다.

이것을 통해 session에서 user_id를 가져와, g.user에 그 정보를 넣어준다. 없다면 None으로 하고. 아 근데 저 g 또나왔네...

 

 

- Logout

 

이제 login이 있으니 logout도 만들어 준다. session만 클리어하면 되다 보니 상당히 간단하다.

 

// flaskr/auth.py

@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

 

 

- Require Authentication in Other Views

 

blog 포스트를 작성/ 수정/ 삭제 전에는 login 되어있는지 확인하는 작업이 필요하다.

그리고 이 방법은 decorator를 이용하여 쉽게 체크 가능하다고 한다.

 

decorator pattern처럼 해당 기능을 flask에서 쉽게 구현한 것 같다.

decorator pattern이란 현재 내가 가지고 있는 객체에다가 기능을 계속 추가할 수 있게 도와주는 패턴이라고 보면 된다.

 

 

decorator pattern이 궁금하다면?

 

// flaskr/auth.py

def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))

        return view(**kwargs)

    return wrapped_view

 

코드는 위와 같이 작성한다.

view를 한번 감싸서, 바로 원하는 view로 가지 않고, wrapped view를 한 번 거쳐가게 만들었다. 이 때 login이 되어있는지 확인하면 간단할 것이다.

 

 

 

- Endpoints and URLs

 

위에서 잠깐 말했듯, url_for() function은 현재 name과 argu에 따라 url을 생성해 준다고 한다. 이 때 이 뷰와 관련된 이름을 endpoint 라 부르고, 기본적으론 view function의 name과 같다고 한다.

 

그러니.. 자세힌 모르겠지만 읽어보면, 우리가 auth.py에서 'auth' 라는 blueprint를 만들었으니, auth.py 내부에 정의된 login과 같은 함수들의 endpoint는 'auth.login' 이 된다고 한다.

으음.... 이 부분은 나중에 좀 더 해봐야것다.

 

 

 

일단 이 장에서는 html 은 건드리지 않고 코드 부분만 만들었다. 다음 Templates 에서 마저 진행할 듯 하다!

 

 

 

2023.07.21 - [Flask] - Flask 이용한 웹사이트 제작기 (5) - 기본 template 구현

 

Flask 이용한 웹사이트 제작기 (5) -

2023.07.18 - [Flask] - Flask 이용한 웹사이트 제작기 (4) - auth view code 작성 Flask 이용한 웹사이트 제작기 (4) - auth view code 작성 2023.07.16 - [Flask] - Flask 이용한 웹사이트 제작기 (3) - DB 연결 Flask 이용한 웹

donot-simsim.tistory.com

 

반응형