Python/Python 웹프로그래밍

[Flask-③] 파이썬 Flask 애플리케이션 구조화 (Blueprint)와 에러 핸들링

코딩 코디네이터 2025. 4. 15. 12:00
반응형

Flask 애플리케이션 구조화 (Blueprint)와 에러 핸들링

복잡해지는 Flask 프로젝트,
하나의 파일로 관리하고 계신가요?
지금이 구조화를 배워야 할 때입니다!

 

 

안녕하세요, Flask 웹 개발 여정을 함께하고 있는 여러분 반갑습니다 😊

셋째 날에는 프로젝트 규모가 커질수록 꼭 필요한 애플리케이션 구조화에러 핸들링에 대해 배워볼 거예요.

처음엔 단순했던 app.py 하나로 잘 운영되던 앱도 기능이 늘어나면 유지보수가 어려워지기 마련이죠.

이럴 때 Blueprint를 활용하면 각 기능을 깔끔하게 나눌 수 있고,

@errorhandlerabort() 등을 통해 사용자에게 더 친절한 오류 메시지를 보여줄 수 있습니다.

실습과 함께 프로젝트 구조를 정리해보면서 한층 더 실전 같은 웹 앱으로 성장시켜볼까요?

 

1. Blueprint 개념과 필요성 🧩

Flask로 웹 애플리케이션을 만들다 보면 처음에는 모든 라우터와 로직을 하나의 app.py 파일에 작성하게 되죠.

하지만 기능이 늘어나고 규모가 커질수록 이 파일은 점점 복잡해지고, 유지보수가 어려워지기 시작합니다.

바로 이럴 때 Blueprint가 등장합니다.

Blueprint란 무엇인가요?

Blueprint는 Flask에서 제공하는 모듈화 도구로, 애플리케이션의 기능을 분리된 파일과 폴더로 나누어 관리할 수 있게 해줍니다.

각각의 Blueprint는 독립적인 라우팅, 템플릿, 정적 파일 등을 가질 수 있어 하나의 작은 앱처럼 동작할 수 있죠.

그리고 최종적으로 메인 애플리케이션에 등록하여 전체 시스템의 일부분으로 동작하게 됩니다.

  • 라우트 및 뷰 함수 분리로 코드 가독성 향상
  • 협업 시 역할 분담 용이 (예: board, auth, admin 모듈)
  • URL prefix로 기능별 주소 체계 구성

Blueprint 적용 전과 후의 구조 비교

구분 적용 전 적용 후
파일 구조 app.py 하나에 모든 코드 작성 board.py, auth.py 등 기능별 파일 분리
라우팅 모든 라우트가 app 객체에 직접 연결됨 Blueprint 내에서 정의 후 app에 등록
템플릿 관리 templates 폴더에 모든 템플릿 혼합 기능별 templates/폴더 구성 가능

Blueprint를 꼭 써야 할까?

처음부터 복잡하게 구조화하려는 강박은 없었으면 해요.

하지만 기능이 3개 이상이라면, 혹은 협업이 필요하거나 앱이 커질 계획이 있다면, Blueprint는 필수입니다.

그리고 이건 단순한 폴더 분할이 아닌 Flask 철학에 맞는 확장 가능한 설계이기도 해요.

자, 이제 기본 개념을 이해했으니...

 

다음은 실습을 통해 실제로 Blueprint를 생성하고 등록해보는 시간을 가져볼게요! 👇

 

 

2. Blueprint 생성 및 등록 실습 🛠️

Blueprint의 이론을 이해했다면 이제 직접 만들어볼 차례죠!

이 실습에서는 게시판 기능을 별도의 board.py 파일로 분리하고, 메인 앱에서는 이를 불러와서 등록하는 흐름을 구성해보겠습니다.

Blueprint 생성: board.py 파일 작성

우선 게시판 관련 기능을 담을 board.py 파일을 생성합니다.

이 안에는 Blueprint 객체를 만들고, 그 안에 라우트를 정의합니다.

from flask import Blueprint, render_template

board_bp = Blueprint('board', __name__, url_prefix='/board')

@board_bp.route('/')
def board_list():
    return render_template('board/list.html')

@board_bp.route('/')
def board_detail(post_id):
    return render_template('board/detail.html', post_id=post_id)
  • Blueprint() 생성 시 url_prefix로 라우트 기본 경로를 지정합니다.
  • 라우트 정의는 @board_bp.route 데코레이터를 사용합니다.

Blueprint 등록: app.py 수정

다음으로 board.py에 정의된 Blueprint를 메인 애플리케이션에 등록해보죠.

app.py를 다음과 같이 수정합니다:

from flask import Flask
from board import board_bp

app = Flask(__name__)
app.register_blueprint(board_bp)

@app.route('/')
def index():
    return '메인 페이지'

이제 /board로 시작하는 URL들은 모두 board.py에서 정의된 라우트로 연결됩니다.

템플릿 디렉토리 분리 및 테스트

템플릿 파일은 templates/board/ 폴더를 만들어 관리합니다.

예를 들어 게시글 목록을 보여주는 템플릿은 아래처럼 구성할 수 있겠죠.

<!-- templates/board/list.html -->
<h1>게시판 목록</h1>
<p>이곳은 게시판 메인입니다.</p>

이제 서버를 실행한 후 http://localhost:5000/board로 접속하면 게시판 라우트가 정상 작동하는지 확인해보세요!

✅ 실습 요약

  • Blueprint는 from flask import Blueprint로 생성
  • 라우트는 @board_bp.route로 작성
  • 메인 앱에 register_blueprint()로 등록

 

다음 장에서는 이 구조를 바탕으로 실제 프로젝트를 리팩터링해보고,

더 정돈된 구조로 발전시켜 보겠습니다! 💪

 

 

3. 기존 프로젝트 구조 리팩터링 🗂️

지금까지는 간단한 예제 중심으로 Blueprint를 실습했죠.

이번에는 실제로 우리가 만든 Flask 애플리케이션을 기능별로 나누고, 디렉토리 구조를 재편해보는 시간을 가져보겠습니다.

프로젝트가 커지면 구조화가 얼마나 중요한지 직접 느껴보실 수 있을 거예요.

디렉토리 구조 개편 예시

myflaskapp/
│
├── app.py                  # Flask 앱 생성 및 Blueprint 등록
├── board/
│   ├── __init__.py         # Blueprint 객체 생성
│   ├── views.py            # 게시판 라우팅 로직
│   └── templates/
│       └── board/
│           ├── list.html   # 게시글 목록 템플릿
│           └── detail.html # 게시글 상세 템플릿
├── static/
│   └── css/
│       └── style.css       # 공통 스타일 시트
└── templates/
    └── base.html           # 공통 레이아웃 템플릿

이처럼 각 기능은 독립된 폴더(board/)로 나뉘고, __init__.py에서 Blueprint를 초기화한 뒤, views.py로 라우트를 따로 관리합니다.

템플릿도 해당 Blueprint 하위에 위치시켜 명확한 소속을 표현하죠.

🔧 예제 코드: board/__init__.py

from flask import Blueprint

board_bp = Blueprint('board', __name__, url_prefix='/board')

from . import views

🧩 예제 코드: board/views.py

from flask import render_template
from . import board_bp

@board_bp.route('/')
def list_posts():
    return render_template('board/list.html')

@board_bp.route('/<int:post_id>')
def post_detail(post_id):
    return render_template('board/detail.html', post_id=post_id)

템플릿 디렉토리 또한 templates/board/에 위치시키면 Flask가 자동으로 해당 위치를 찾아 렌더링합니다.

특히 팀 프로젝트에서 이런 구조는 유지보수성과 협업 효율을 대폭 향상시켜줍니다.

💡 리팩터링 실습 팁

  • URL은 /board, /board/1 등이 예상 경로입니다.
  • 라우트 이름은 가급적 의미 있는 이름으로! list_posts(), post_detail()처럼요.
  • 앱 실행 전 __init__.pyfrom . import views 빠뜨리지 않도록 주의!

여기까지 리팩터링을 잘 마쳤다면 이제 프로젝트 구조가 훨씬 명확하고 확장 가능한 상태가 되었을 거예요.

다음은 이 구조 위에 에러 핸들링을 추가해서 사용자 경험을 한 단계 더 끌어올려보겠습니다!

 

 

4. Flask 에러 핸들링 기법 ⚠️

웹 서비스를 운영하다 보면 오류 상황은 피할 수 없어요.

하지만 오류를 어떻게 처리하느냐에 따라 사용자 경험이 완전히 달라질 수 있습니다.

Flask는 @errorhandler 데코레이터와 abort() 함수를 통해 예외 상황을 보다 우아하게 처리할 수 있도록 도와줍니다.

기본 에러 핸들링: @app.errorhandler

다음은 대표적인 에러인 404 Not Found 오류를 처리하는 코드예요.

@app.errorhandler(404)
def page_not_found(error):
    return render_template('errors/404.html'), 404
  • 404 외에도 403, 500 등의 에러 코드도 커스텀 핸들링 가능해요.
  • HTML 템플릿 파일은 templates/errors/에 분리 저장하는 걸 추천합니다.

abort() 함수로 명시적 에러 발생

Flask에서는 조건에 따라 특정 HTTP 에러를 명시적으로 발생시킬 수도 있어요.

예를 들어 잘못된 게시글 ID가 들어오면 이렇게 처리할 수 있습니다.

from flask import abort

@board_bp.route('/<int:post_id>')
def post_detail(post_id):
    post = get_post_or_none(post_id)
    if post is None:
        abort(404)
    return render_template('board/detail.html', post=post)

이렇게 abort()를 호출하면 Flask가 자동으로 해당 에러 코드에 맞는 핸들러를 호출해줍니다.

Blueprint별 에러 처리도 가능!

재미있는 건, 각 Blueprint에 독립적인 에러 핸들러를 정의할 수 있다는 거예요.

@board_bp.errorhandler(404)
def board_404(error):
    return render_template('board/404.html'), 404

이렇게 하면 게시판 페이지에서 발생한 404는 전용 템플릿으로 분기되고, 나머지 앱 전체에 대해선 @app.errorhandler가 적용되죠.

유저 입장에선 더 세심한 UI로 느껴질 수 있어요.

에러 로깅은 어떻게 하나요?

개발 모드에서는 Flask가 자동으로 오류 스택 트레이스를 보여주죠.

하지만 운영 환경에서는 app.logger를 사용해서 중요한 오류를 로그 파일에 기록하는 것이 좋습니다.

import logging

@app.errorhandler(500)
def internal_error(error):
    app.logger.error(f'Server Error: {error}')
    return render_template('errors/500.html'), 500

이 방식은 운영 서버에서 발생하는 심각한 오류를 추적하고 개선하는 데 매우 유용해요.

✅ 핵심 요약

  • @app.errorhandler 또는 @blueprint.errorhandler로 커스텀 오류 처리
  • abort()로 조건부 오류 강제 발생 가능
  • app.logger를 통해 서버 오류 기록

 

다음은 요청 전후의 공통 작업을 자동화하는 방법인 요청 훅과 미들웨어 유사 기능에 대해 알아보겠습니다.

서버가 모든 요청을 어떻게 처리하는지 흐름을 이해하면, 더 강력한 기능도 구현할 수 있어요!

 

 

5. 요청 훅과 미들웨어 유사 기능 🔄

Flask에는 Django처럼 미들웨어 체인이 명시적으로 존재하지는 않지만, 그 대신 요청 훅(hook)을 이용해 유사한 기능을 구현할 수 있어요.

전역적으로 실행되는 전처리/후처리 로직을 통해 인증 검사, 로깅, CORS 헤더 추가 등 다양한 동작을 자동화할 수 있습니다.

@app.before_request / @app.after_request

모든 요청에 대해 실행되는 전처리와 후처리 훅은 다음처럼 정의할 수 있어요:

from flask import request

@app.before_request
def log_request_info():
    print(f"[요청] {request.method} {request.path}")

@app.after_request
def add_header(response):
    response.headers['X-Frame-Options'] = 'DENY'
    return response
  • before_request: DB 연결, 사용자 인증 등 선처리에 유용
  • after_request: 응답 헤더 추가, 캐시 처리 등에 사용

Blueprint에서 요청 훅 쓰기

요청 훅은 app뿐 아니라 Blueprint에서도 사용할 수 있어요.

예를 들어 게시판 라우트에만 접근 권한을 체크하고 싶을 땐 아래처럼 작성합니다.

@board_bp.before_request
def check_board_auth():
    if not user_has_board_permission():
        return "접근이 제한된 페이지입니다.", 403

이렇게 하면 /board로 시작하는 URL에 대해서만 인증 로직이 동작해요.

전체 앱과 독립적으로 작동하니 구조 관리에 매우 유리하죠.

WSGI 미들웨어 소개 (간략)

보다 정교한 미들웨어 처리가 필요하다면 Flask의 하위 레벨인 WSGI 미들웨어를 사용할 수도 있어요.

다만 이는 Flask 자체보다는 웹 서버와의 연동 관점에서 다뤄지며, 대부분의 경우 요청 훅으로 충분합니다.

✅ 요약 정리

  • before_request는 인증, 로깅 등 요청 전 공통 처리에 적합
  • after_request는 응답 헤더 수정, 후처리에 사용
  • Blueprint마다 훅 설정 가능 → 인증, 필터 등 기능별 적용

이제 Flask 앱이 점점 더 체계적인 구조와 기능을 갖추게 되었어요.

마지막으로, 이런 기능들을 더욱 풍부하게 만들어주는 Flask 확장들을 소개하면서 이번 단원을 마무리해볼게요.

 

 

6. Flask 확장 라이브러리 소개 📦

Flask는 마이크로 프레임워크인 만큼, 기본 기능이 최소화되어 있어요.

대신에 필요한 기능을 선택적으로 확장할 수 있는 수많은 라이브러리가 존재합니다.

이번에는 Flask 개발에서 자주 사용되는 확장 도구들을 간단히 정리해볼게요.

1. Flask-SQLAlchemy - ORM 기능 🧮

SQLAlchemy는 강력한 ORM(Object-Relational Mapping) 라이브러리입니다.

Flask-SQLAlchemy는 이를 Flask에 쉽게 통합할 수 있도록 도와주는 확장이죠.

  • ORM을 통해 SQL 없이도 DB 조작 가능
  • 다양한 DB 지원 (MySQL, PostgreSQL, SQLite 등)

2. Flask-Migrate - DB 마이그레이션 도구 🔄

Flask-Migrate는 데이터베이스 스키마 변경을 관리하는 도구예요.

프로젝트 중반에 테이블 구조가 바뀔 때도 명령어 한 줄로 변경 사항을 적용할 수 있어 매우 유용합니다.

flask db init
flask db migrate -m "Add users table"
flask db upgrade

3. Flask-WTF - 폼 처리 및 검증 📄✅

웹 폼은 사용자 입력의 핵심이죠.

Flask-WTF는 Flask에서 폼 클래스 기반 입력 처리를 가능하게 해주는 확장입니다.

CSRF 보호와 유효성 검사도 기본으로 제공해요.

  • HTML 폼 → Python 클래스 기반 처리
  • 입력 검증 및 에러 메시지 처리 자동화

4. Flask-Login - 사용자 인증 🔐

회원 가입, 로그인, 로그아웃 기능을 구현하려면 Flask-Login이 제격입니다.

인증 흐름을 매우 간단하게 만들어주는 유용한 도구죠.

from flask_login import LoginManager

login_manager = LoginManager()
login_manager.init_app(app)

 

✅ 이 외에도 인기 있는 Flask 확장

  • Flask-Caching – 캐시 처리로 성능 향상
  • Flask-Mail – 이메일 전송 기능
  • Flask-RESTful – REST API 개발을 위한 구조화 도구

 

우리가 앞으로 이 커리큘럼에서 활용할 주요 확장은 Flask-SQLAlchemy, Flask-Migrate, Flask-WTF예요. 인증이 필요한 기능에서는 Flask-Login도 함께 써볼 거고요.

필요에 따라 다른 확장도 자유롭게 추가할 수 있어요.

이제 Flask의 구조화와 오류 처리, 요청 훅, 확장까지 모두 익혔다면, 복잡한 웹 애플리케이션도 자신감 있게 개발할 수 있어요!

다음 수업에서는 ORM과 데이터베이스를 본격적으로 다뤄볼 예정이니 기대해주세요 😊

 

지금까지 Flask 애플리케이션을 구조화하는 방법부터 에러를 다루는 방법, 공통 동작을 처리하는 요청 훅, 그리고 개발을 도와주는 확장 도구까지 차근차근 살펴봤습니다.

처음엔 조금 복잡해 보일 수 있지만, 실제로 코드를 나누고 정리하다 보면 더 깔끔하고 안정적인 서비스 구조를 만들 수 있어요.

특히 Blueprint를 활용한 모듈화는 프로젝트가 커졌을 때 그 진가를 발휘합니다.

더불어, @app.errorhandler, abort(), before_request 같은 기능은 개발자의 세심함을 사용자에게 전달할 수 있는 강력한 도구죠.

앞으로 더 복잡한 기능을 구현하기 위해선 이런 기초를 튼튼히 다지는 게 중요하답니다 💪

 

다음 강의에서는 ORM을 활용한 데이터베이스 연동을 본격적으로 다루게 될 텐데요,

오늘 배운 구조화 개념이 이후 내용과도 깊게 연결되니 꼭 복습해두세요.

직접 실습을 통해 익히는 것이 최고의 학습 방법이라는 것, 잊지 마세요!

반응형