파이썬 웹 서비스 만들기

3. 사용자 등록 기능 - 3.1. 계정 정보 정의

영바이트 2020. 9. 21. 19:56

웹 서비스에 추가할 가장 첫 기능으로 사용자로부터 계정 정보를 받아서 서버에 저장하는 '사용자 등록' 기능을 구현해보고자 한다. 이 기능을 가장 먼저 다루는 첫째 이유는 웹을 통한 데이터 입력을 실습해 볼 수 있고, 둘째 이유는 입력받은 데이터의 보관, 즉, 데이터베이스를 다루는 방법을 알 수 있기 때문이다.

 

웹 서비스의 가장 기본적인 기능이 데이터를 입력 받고 → 관리(보관)하고 → 필요한 데이터를 제공하는 것이기 때문에 '사용자 등록 기능' 구현을 통해 데이터 입력과 관리라는 웹 서비스의 앞의 두 가지 요소를 배울 수 있다.

 


사용자 등록(=계정 생성)의 전제는 사용자 인증(예, 로그인, login)이다. 따라서 계정 생성을 위한 데이터를 입력받을 때 부터 로그인이라는 사용자 인증 과정에 대해 고려하지 않을 수 없다. 이를 위해 Flask login 이라는 오픈소스 패키지의 도움을 받을 것이다. Flask login은 서버에 등록된 사용자 데이터를 이용하여 사용자 인증(로그인)을 돕는 플라스크 패키지이다.

 

앞의 포스팅 '2. 모델/폼 - 뷰 - 로직'에서 살펴보았던 바와 같이 가장 먼저 데이터 입력과 보관(관리)을 위한 데이터 폼(form)과 모델(model) 정의에서부터 웹 서비스 기능 구현을 시작할 것이다. 한 과정씩 차분히 살펴보자.

 

새로 프로젝트를 하나 생성하고(02_user_registration) 아래와 같이 첫 포스팅 '1. 웹 서비스의 기본 틀'에서 만들어 두었던 웹 서비스 뼈대(01_flask_blog_template) 프로젝트 내용을 복사해서 가져온다.

 

<프로젝트 생성 및 내용 복사>

 

그리고 파이참의 File → Settings → Project Interpreter → pip으로 진입한 후 Flask-Login 패키지를 검색하고 설치한다.

 

 

<Flask-Login 패키지 검색 및 설치>

 

appmain 패키지 아래에 user 패키지 디렉토리를 하나 만들자. 이 user 패키지에 사용자 등록, 인증(로그인), 사용자 정보관리 등 사용자 계정을 관리하는 기능들을 모아 둘 것이다.

 

<user 패키지 디렉토리 생성>

 

데이터베이스(DB)에 사용자 정보를 저장하기 위해 데이터 모델(model)을 먼저 정의한다.

 

<사용자 정보 모델 정의>

 

▶ appmain/user/models.py

#### 사용자 데이터는 DB에 저장되고 관리된다. 
#### 이를 위해 DB를 클래스 형태로 연동할 수있게 도와주는 SQLAlchemy 객체인 db를 불러온다. 
#### 생성할 계정의 관리를 위해 Flask-Login의 loginManager 객체도 가져온다.
#### SQLAlchemy db 객체와 loginManager 객체는 상위 패키지인 appmain에서 생성할 것이다.

from appmain import db, loginManager

#### 플라스크 프레임워크가 사용자 관리를 위해 flask_login 패키지를 사용하기 위해서는 서버에 저장될 
#### 사용자 데이터에 flask_login이 필요로 하는 변수 및 함수를 포함시킬 필요가 있다. 
#### 이를 위해 UserMixin 클래스를 가져온다.

from flask_login import UserMixin

#### Flask-Login의 LoginManager의 load_user 함수를 구현(implement)해 주어야 한다.
#### 왜냐하면 데이터 모델에 따라 데이터를 가져오는 방법을 알려주어야 하기 때문이다.
#### DB에 userId를 인자(argument)로 데이터를 요청(query)하고 가져오는(get) 동작이 그 내용이다.

@loginManager.user_loader
def load_user(userId):
        return Userdata.query.get(int(userId))

#### 계정 데이터 모델을 클래스로 정의한다. SQLAlchemy와의 연동을 위해 db객체로부터 Model 클래스를 상속받고, 
#### 다시 flask_login 패키지와의 연동을 위해 UserMixin 클래스를 포함시킨다.

class Userdata(db.Model, UserMixin):   
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), unique=True, nullable=False)
    email = db.Column(db.String(60), unique=True, nullable=False)
    picture = db.Column(db.String(20), nullable=False, default="default.png")
    password = db.Column(db.String(60), nullable=False)

 

데이터 모델 클래스 작성방법은 '파이썬 웹 프로그래밍 기본' 편 '7. 정보관리 - 7.2.파이썬으로 DB 사용하기' 포스팅에서 자세히 설명하였다.

 


 

이제 사용자 데이터를 받아오기 위한 폼(form)을 정의해보자. 폼은 '어떤 정보를 어떤 형태로 받겠다'라고 선언하는 것이다. 사용자 데이터 입력과 입력한 데이터 처리(검증 등)를 위해 flask_wtf라는 패키지를 사용할 것이다(). 다시 File → Settings → Project Interpreter → pip으로 진입한 후 Flask-WTF로 패키지를 검색하고 설치한다.

 

<Flask-WTF 패키지 설치>

 

Flask-WTF 패키지를 설치하면 WTForms 패키지도 함께 설치될것이다.

Flask-WTF는 'Flask What The Form'을 줄인 이름이다.

 

<함께 설치된 Flask-WTF, WTForm 패키지>

아래와 같이 user 패키지 안에 forms.py 파일을 생성하고 폼을 정의하자.

 

<입력폼 정의 파일>

 

▶ appmain/user/forms.py

#### 필요한 외부 패키지들을 가져온다.

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from flask_wtf.file import FileField, FileAllowed

#### 앞서 정의한 계정 정보 모델을 가져온다.

from appmain.user.models import Userdata

#### FlaskForm을 상속받은 계정정보 입력 폼 클래스를 정의한다.

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
    picture = FileField('Profile picture', validators=[FileAllowed(['jpg', 'png'])])
    submit = SubmitField('Sign Up')

#### 같은 이름을 사용하는 계정이 있는지 검증(validation)하는 메서드를 정의한다.

    def validate_username(self, username):
        user = Userdata.query.filter_by(username=username.data).first()
        if user:
            raise ValidationError('That username is already taken. Please choose a different username.')

#### 같은 이메일 주소를 사용하는 계정이 있는지 검증하는 메서드를 정의한다.

    def validate_email(self, email):
        email = Userdata.query.filter_by(email=email.data).first()
        if email:
            raise ValidationError('That email is already taken. Please choose a different email address.')

 

계정 정보 입력 클래스 RegistrationForm에 쓰인 요소들을 정리하면 아래와 같다.

FlaskForm 입력 데이터의 전송 등과 같이 기본적인 기능을 제공하는 클래스이다.

 

StringField 문자열 입력 창에 사용되는 데이터 형식을 정의한다.
PasswordField 암호 입력 창에 사용되는 데이터 형식을 정의한다.
SubmitField 제출(submit) 버튼을 정의한다.

 

아래는 validators에 사용된 요소들이다. validators 리스트에 지정된 조건들은 입력된 요소들이 올바로 입력되었는지 검증하는데 사용된다.

DataRequired 입력된 데이터가 있는지 검증한다.
Length 입력된 데이터 길이가 정해진 조건을 만족하는지 검증한다.
EqualTo 입력한 데이터가 어떤 다른 데이터와 같은지 검증한다.
Email 입력한 데이터가 이메일 형식에 맞는지 검증한다.

 

정보 입력 폼 클래스에 사용자가 폼에 입력한 데이터가 제대로 입력되었는지 검증하는 클래스 메서드(클래스 함수)를 정의할 수 있다. 이들 검증 함수들의 정식 명칭은 in-line validator이고 함수 이름으로 접두어(prefix) ‘validate_’를 붙이고, 이어서 검증을 수행할 입력 데이터 이름을 붙인다. 예를 들어 validate_username은 username 입력 값을 검증한다는 뜻이다. 

 

앞서 입력 변수 정의에서 지정한 validators들과 비교하여 프로그래머가 원하는 내용을 직접 구현할 수 있기 때문에 좀 더 다양하고 세밀한 검사가 가능하다. 이들 in-line validator들은 프로그래머가 명시적으로 호출하지 않아도 사용자 입력을 받을 때 자동으로 수행된다.

 

여기까지 다루려는 데이터를 정의하는 '모델/폼 정의' 과정을 살펴보았다. 이어지는 포스팅들에서 '뷰(view)'와 '로직(logic)'을 추가해서 계정 생성 기능을 완성시켜보자.