시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.
이전 내용(메모앱 1단계 - 3단계)
이전 내용(메모앱 4단계)
이전 내용(메모앱 5단계)
이전 내용(메모앱 6단계)
이전 내용(메모앱 7단계)
8단계: MVC 패턴 적용
★ MVC 패턴(Model-View-Controller)
(보다 자세한 내용은 상단의 링크 참고)
▶ 모델-뷰-컨트롤러(model–view–controller, MVC)는 소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴이다. 이 패턴을 성공적으로 사용하면, 사용자 인터페이스로부터 비즈니스 로직을 분리하여 애플리케이션의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있는 애플리케이션을 만들 수 있다.
[구성요소]
- Model(모델): 애플리케이션의 데이터 구조와 비즈니스 로직을 담당한다. 데이터의 상태 변경이나 저장, 업데이트 등의 작업을 수행한다. 모델의 상태에 변화가 있을 때 컨트롤러와 뷰에 이를 통보한다.
- View(뷰): 사용자에게 보여지는 화면을 담당한다. 모델의 데이터를 기반으로 화면에 출력되며, 사용자로부터 입력을 받는다. 사용자가 볼 결과물을 생성하기 위해 모델로부터 정보를 얻어 온다.
- Controller(컨트롤러): 모델과 뷰를 연결하는 중간 다리 역할을 한다. 사용자의 입력을 받아 모델을 업데이트하고, 변경된 데이터를 뷰에 전달한다. 모델에 명령을 보냄으로써 모델의 상태를 변경할 수 있다.
[장점]
- 유지 보수 용이성: 모델, 뷰, 컨트롤러가 각각 독립적으로 관리되므로, 특정 부분을 수정할 때 다른 부분에 영향을 최소화할 수 있다.
- 재사용성: 각 컴포넌트가 독립적이기 때문에 다른 프로젝트나 부분에서 재사용할 수 있다.
- 개발 속도 향상: 개발팀이 각 컴포넌트에 집중할 수 있어, 동시다발적인 개발이 가능해진다.
◆ 현재 app.py 파일 구조
현재의 app.py 구조는 플라스크 프레임워크를 사용하여 기본적인 MVC 패턴을 따르고 있으나, 명확한 구조적 분리를 통해 MVC 패턴을 더욱 강화할 수 있다.
[현재 파일 구조를 MVC 패턴에 맞춰 재구성하는 방법]
1. 모델
app.py 내에 정의된 Memo와 User 클래스는 데이터 모델을 나타낸다. 이 클래스들을 별도의 파일로 분리하여 모델을 명확하게 구분할 수 있다.예) models.py 파일을 생성하고 Memo와 User 클래스를 이 파일로 옮긴다.
2. 뷰
뷰는 사용자 인터페이스를 관리하며, 현재 templates/ 디렉터리에 home.html 과 memos.html 파일로 구성되어 있다. (그대로 유지)
3. 컨트롤러
컨트롤러는 사용자의 요청을 처리하고 모델과 뷰 사이의 상호 작용을 관리한다.예) 현재 app.py 파일에 컨트롤러 로직이 포함되어 있어 이를 개선하기 위해 라우팅 및 요청 처리 로직을 controllers.py 파일로 분리
[MVC 패턴 적용 전 후]
▼
◆ MVC 패턴 적용하기
앞서 설명했듯, app.py을 app.py / controllers.py / login_manager.py / models.py 파일로 분리하고 구성하는 작업을 시행한다. 각 파일에 대한 대략적인 설명, 코드 및 각 특징은 다음과 같다.
1. login_manager.py
login_manager를 여러 파일에서 참조함에 따라 순환 참조 문제로 에러가 발생하는 것을 막기위해 생성한다.
# login_manager.py
from flask_login import LoginManager
login_manager = LoginManager()
[특징]
login_manager.py 파일은 Flask-Login의 LoginManager 인스턴스를 정의한다.
이 분리는 다음과 같은 이점을 가진다.
- 순환 참조 방지: 여러 파일에서 login_manager를 참조할 때 발생하는 순환 참조 문제를 방지한다. 순환 참조는 모듈 간 종속성으로 인해 발생할 수 있으며, 이를 방지하기 위해 login_manager를 별도의 모듈로 분리한다.
- 재사용성 향상: login_manager가 별도의 모듈에 있으면 다른 모듈에서 쉽게 재사용할 수 있다. 이는 애플리케이션의 다양한 부분에서 일관된 사용자 인증 방식을 유지하는 데 도움이 된다.
- 기능 추가 시 유연성: 향후 사용자 인증 관련 기능을 추가하거나 수정할 때 login_manager.py만 수정하면 된다. 이는 기능 확장을 쉽게 만들어 주며, 다른 모듈에 영향을 주지 않는다.
2. app.py
app.py는 플라스크 애플리케이션을 초기화하고 설정을 구성하는 역할을 담당한다.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from models import db
from login_manager import login_manager
from controllers import setup_routes
app = Flask(__name__)
# 데이터베이스 설정
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:본인비밀번호@localhost:3306/my_memo_app'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'mysecretkey'
# 데이터베이스 및 로그인 관리자 초기화
db.init_app(app)
login_manager.init_app(app)
login_manager.login_view = 'login'
# 라우팅 설정
setup_routes(app)
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run()
[특징]
app.py 파일은 플라스크 애플리케이션의 초기화 및 기본 설정을 담당한다. 이 구조의 주요 이점은 다음과 같다.
- 중앙화된 설정: 모든 기본 설정이 한곳에 모여 있어 관리가 용이하다. 예를 들어 데이터베이스 연결 문자열, 애플리케이션의 비밀 키 등을 중앙에서 관리할 수 있다.
- 애플리케이션 초기화의 명확성: 플라스크 인스턴스와 필요한 확장 기능의 초기화가 app.py에서 명확하게 이루어진다. 이는 애플리케이션의 구조를 이해하기 쉽게 만들어준다.
- 기능 확장 용이성: 새로운 기능이나 확장을 추가할 때 app.py 파일만 수정하면 된다. 이는 애플리케이션의 확장성을 높여 준다.
3. models.py
models.py는 데이터 모델과 관련된 클래스와 설정을 포함한다.
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
db = SQLAlchemy()
class Memo(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.String(1000), nullable=False)
def __repr__(self):
return f'<Memo {self.title}>'
# 데이터베이스 모델 정의
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), unique=True, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
password_hash = db.Column(db.String(512), nullable=False)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
[특징]
models.py 파일은 데이터베이스 모델을 정의한다. 이 구조는 다음과 같은 이점을 제공한다.
- 모델 중심의 설계: 데이터 모델은 애플리케이션의 핵심 구성 요소이며, 이를 별도의 모듈로 분리함으로써 모델 중심의 설계를 장려한다.
- 데이터 무결성 및 관계 관리: 데이터베이스 테이블과 그 관계를 명확하게 정의하여 데이터 무결성을 유지하는 데 도움이 된다.
- 모듈화 및 재사용성: 모델을 별도의 파일로 분리하면 코드의 재사용성이 향상되며, 다른 애플리케이션 부분과의 결합도가 낮아진다.
4. controllers.py
controllers.py에는 사용자 요청을 처리하는 라우팅 및 관련 로직이 포함된다.
from flask import Flask, render_template, request, jsonify, abort, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from models import db, User, Memo
from login_manager import login_manager
def setup_routes(app):
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
#기존 라우트
@app.route('/')
def home():
return render_template('home.html')
@app.route('/about')
def about():
return '이것은 My Memo 앱의 소개 페이지입니다'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = User.query.filter_by(username=request.form['username']).first()
if user and user.check_password(request.form['password']):
login_user(user)
return jsonify({'message': '로그인을 성공하였습니다. 메모 페이지로 이동합니다.'}), 200
return jsonify({'error': '아이디가 없거나 패스워드가 다릅니다.'}), 401
return render_template('home.html') # Corrected here
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('home')) #로그아웃 후 메인 페이지로 리다이렉트
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
password = request.form['password']
existing_user = User.query.filter((User.username == username) | (User.email == email)).first()
if existing_user:
return jsonify({'error': '사용자 이름 또는 이메일이 이미 사용 중 입니다.'}), 400
user = User(username=username, email=email)
user.set_password(password)
db.session.add(user)
db.session.commit()
return jsonify({'message': '회원가입이 성공하였습니다. 가입한 아이디와 패스워드로 로그인할 수 있습니다.'}), 201
return redirect(url_for('home')) # 비정상 요청의 경우 리다이렉트
# 메모 조회
@app.route('/memos', methods=['GET'])
@login_required
def list_memos():
memos = Memo.query.filter_by(user_id=current_user.id).all()
return render_template('memos.html', memos=memos, username=current_user.username)
# 메모 생성
@app.route('/memos/create', methods=['POST'])
@login_required
def create_memo():
title = request.json['title']
content = request.json['content']
new_memo = Memo(user_id=current_user.id, title=title, content=content) #현재 로그인한 사용자의 ID 추가
db.session.add(new_memo)
db.session.commit()
return jsonify({'message': 'Memo Created'}), 201
# 메모 업데이트
@app.route('/memos/update/<int:id>', methods=['PUT'])
@login_required
def update_memo(id):
memo = Memo.query.filter_by(id=id, user_id=current_user.id).first() #현재 사용자의 메모만 선택
if memo:
memo.title = request.json['title']
memo.content = request.json['content']
db.session.commit()
return jsonify({'message': 'Memo updated'}), 200
else:
abort(404, description="Memo not found or not authorized")
# 메모 삭제
@app.route('/memos/delete/<int:id>', methods=['DELETE'])
@login_required
def delete_memo(id):
memo = Memo.query.filter_by(id=id, user_id=current_user.id).first()
if memo:
db.session.delete(memo)
db.session.commit()
return jsonify({'message': 'Memo deleted'}), 200
else:
abort(404, description="Memo not found or not authorized")
[특징]
controllers.py 파일은 사용자의 요청을 처리하는 컨트롤러 로직을 포함한다. 이 구조의 이점은 다음과 같다.
- 관심사의 분리: 컨트롤러 로직을 별도로 분리함으로써 라우팅 및 요청 처리와 관련된 코드를 한 곳에 집중할 수 있다. 이는 코드의 가독성과 유지보수성을 높여준다.
- 확장성 및 유지보수성: 새로운 라우트나 컨트롤러를 추가하거나 수정할 때, 다른 모듈에 영향을 주지 않고 독립적으로 작업할 수 있다.
- 기능별 구성: 각 라우트와 관련된 기능들을 명확하게 구붕하여 기능별로 코드를 관리할 수 있다. 이는 특히 대규모 애플리케이션에서 유용하다.
전체 웹 서비스 사용자 시나리오
1. VS Code 에서 flask run 입력 후, http://127.0.0.1:5000 링크 ctrl+클릭 하여 접속
2. <회원가입> 버튼을 누르고 관련 정보 입력 후 회원가입 하기
3. 새로 생성한 계정 정보로 로그인하기
4. 메모 제목 및 내용을 작성하고, 메모 추가하기
매모 목록을 더 추가할 수 있다.
로그아웃 버튼을 누르면 메인 페이지로 자동 이동된다.
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI 기초 (0) | 2024.08.15 |
---|---|
[파이썬] Fast API란? (0) | 2024.08.15 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(5) (0) | 2024.08.14 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(4) (0) | 2024.08.13 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(3) (0) | 2024.08.08 |