안 쓰던 블로그

[파이썬 Flask] 회원가입 웹 시스템 만들기_2 WTF 적용하기 본문

언어/파이썬

[파이썬 Flask] 회원가입 웹 시스템 만들기_2 WTF 적용하기

proqk 2020. 9. 4. 12:56
반응형

foxtrotin.tistory.com/269

 

[파이썬 Flask] 회원가입 웹 시스템 만들기

Flask 플라스크는 파이썬으로 웹 어플리케이션을 구현할 때 사용하는 프레임워크다 파이썬 웹 프로그래밍 프레임워크 중 잘 알려진 것이 django와 flask이다 쟝고는 여러 모듈과 기능을 제공하여 무

foxtrotin.tistory.com

회원가입 코드를 더 간결하게 만들어 본다

 

플라스크는 가볍게 하기 위해서 기본 기능을 많이 넣지 않았다. form관리를 하기 위해서는 WTF 패키지를 사용한다

파이썬에서 form을 만들고 template에 전달해서 form을 표현하는 식으로 구현한다

이렇게 하면 좋은 점은, CSRF보호 기법도 넣을 수 있고 validate도 쓸 수 있다

CSRF는 사이트 간 요청 위조를 방지하기 위해서 form안에 함호화된 해쉬키를 넣어 변조되었는지 확인하는 패키지로, 해쉬값을 자동으로 전달해서 검증까지 해 준다

 

form.py를 먼저 만들어 준다

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms import PasswordField
from wtforms.validators import DataRequired, EqualTo

class RegisterForm(FlaskForm):
    userid = StringField('userid', validators=[DataRequired()])
    email = StringField('email', validators=[DataRequired()])
    password = PasswordField('password', validators=[DataRequired(), EqualTo('password_2')]) #비밀번호 확인
    password_2 = PasswordField('repassword', validators=[DataRequired()])

이렇게 하면 form객체가 정보를 받으면서 유효성 검사도 해 준다

 

register.html도 csft를 넣어서 수정한다

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no' />
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
        <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    </head>
    <body>
        <div class="container">
            <div class="row mt-5">
                <h1>회원가입</h1>
            </div>
            <div class="row mt-5">
                <div class="col-12">
                    <form method="POST" >
                        {{form.csrf_token}} <!--csft활성화-->
                        <div class="form-group">
                            {{form.userid.label("아이디")}}
                            {{form.userid(class="form-control", placeholder="아이디")}}
                        </div>
                        <div class="form-group">
                            {{form.email.label("이메일")}}
                            {{form.email(class="form-control", placeholder="이메일")}}
                        </div>
                        <div class="form-group">
                            {{form.password.label("비밀번호")}}
                            {{form.password(class="form-control", placeholder="비밀번호")}}
                        </div>
                        <div class="form-group">
                            {{form.password_2.label("비밀번호 확인")}}
                            {{form.password_2(class="form-control", placeholder="비밀번호 확인")}}
                        </div>
                        <button type="submit" class="btn btn-primary">제출</button>
                    </form>
                </div>
    
            </div>
        </div>
    </body>
    </html>

 

app.py에서 register부분을 수정한다

@app.route('/register', methods=['GET', 'POST']) #GET(정보보기), POST(정보수정) 메서드 허용
def register():
    if request.method == 'GET': #get일 때는 보여 준다
        return render_template("register.html")
    else: #post일 때는 정보를 보낸다
        userid = request.form.get('userid')
        email = request.form.get('email')
        password = request.form.get('password')
        password_2 = request.form.get('password')

        if not(userid and email and password and password_2): #이 부분을 form.validate_on_submit()으로 대체 했다 
            return "입력되지 않은 정보가 있습니다"
        elif password != password_2:
            return "비밀번호가 일치하지 않습니다"
        else:
            usertable=User() #user_table 클래스
            usertable.userid = userid
            usertable.email = email
            usertable.password = password
            
            db.session.add(usertable)
            db.session.commit()
            return "회원가입 성공"
        return redirect('/') #post요청이면 회원가입 성공 시 메인으로 이동

저번까지 만들었던 레지스터는 이런 코드였다

csrf를 넣었으니 아래처럼 form으로 대체 가능하다

 

@app.route('/register', methods=['GET', 'POST']) #GET(정보보기), POST(정보수정) 메서드 허용
def register():
    form = RegisterForm()
    if form.validate_on_submit(): #내용 채우지 않은 항목이 있는지까지 체크
        usertable = User() 
        usertable.userid = form.data.get('userid')
        usertable.email = form.data.get('email')
        usertable.password = form.data.get('password')

        db.session.add(usertable) #DB저장
        db.session.commit() #변동사항 반영
        
        return "회원가입 성공" 
    return render_template('register.html', form=form)

코드가 훨씬 간결해졌다

 

전체 app.py코드

import os #디렉토리 절대 경로
from flask import Flask
from flask import render_template #template폴더 안에 파일을 쓰겠다
from flask import request #회원정보를 제출할 때 쓰는 request, post요청 처리
from flask import redirect #리다이렉트
from flask_sqlalchemy import SQLAlchemy
from Models import db
from Models import User
from flask import session #세션
from flask_wtf.csrf import CSRFProtect #csrf
from form import RegisterForm
app = Flask(__name__)

@app.route('/')
def mainpage():
	return render_template('main.html')

@app.route('/register', methods=['GET', 'POST']) #GET(정보보기), POST(정보수정) 메서드 허용
def register():
    form = RegisterForm()
    if form.validate_on_submit(): #내용 채우지 않은 항목이 있는지까지 체크
        usertable = User() 
        usertable.userid = form.data.get('userid')
        usertable.email = form.data.get('email')
        usertable.password = form.data.get('password')

        db.session.add(usertable) #DB저장
        db.session.commit() #변동사항 반영
        
        return "회원가입 성공" 
    return render_template('register.html', form=form) #form이 어떤 form인지 명시한다

# @app.route('/login', methods=['GET','POST'])  
# def login():
#     form = LoginForm() #로그인폼
#     if form.validate_on_submit(): #인증
#         print('{}가 로그인 했습니다'.format(form.data.get('userid')))
#         session['userid']=form.data.get('userid') #form에서 가져온 userid를 세션에 저장
#         return redirect('/') #성공하면 main.html로
#     return render_template('login.html')

if __name__ == "__main__":
    #데이터베이스---------
    basedir = os.path.abspath(os.path.dirname(__file__)) #현재 파일이 있는 디렉토리 절대 경로
    dbfile = os.path.join(basedir, 'db.sqlite') #데이터베이스 파일을 만든다

    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + dbfile
    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True #사용자에게 정보 전달완료하면 teadown. 그 때마다 커밋=DB반영
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #추가 메모리를 사용하므로 꺼둔다
    app.config['SECRET_KEY']='asdfasdfasdfqwerty' #해시값은 임의로 적음

    csrf = CSRFProtect()
    csrf.init_app(app)

    db.init_app(app) #app설정값 초기화
    db.app = app #Models.py에서 db를 가져와서 db.app에 app을 명시적으로 넣는다
    db.create_all() #DB생성

    app.run(host="127.0.0.1", port=5000, debug=True)

 

저번이랑 결과가 달라지지는 않았지만 내부적으로는 코드가 간결해졌고 csrf_protect기능이 추가되었다

 

 

참고

niceman.tistory.com/191

infinitt.tistory.com/113

반응형
Comments