Skip to main content
Summary

지금까지 만든 조각을 하나로 보기

이제 우리는 작은 게시판 웹 애플리케이션을 만들 수 있는 재료를 대부분 배웠습니다. URL을 함수에 연결하는 라우팅, 화면을 만드는 템플릿, 사용자의 입력을 받는 폼, 게시글을 저장하는 데이터베이스, 기능별로 코드를 나누는 블루프린트, JSON API, 오류 처리까지 다뤘습니다.

마지막으로 할 일은 새 기능을 많이 추가하는 것이 아닙니다. 흩어져 있던 개념을 하나의 작동하는 프로젝트로 정리하는 일입니다.

완성된 게시판은 대략 다음 기능을 가집니다.

  • 홈 화면에서 게시글 목록을 보여 준다.

  • 사용자는 게시글을 작성할 수 있다.

  • 서버는 제목, 작성자, 내용을 검증한다.

  • 게시글은 SQLite 데이터베이스에 저장된다.

  • 상세 페이지에서 게시글 내용을 볼 수 있다.

  • 로그인한 사용자만 글을 쓰게 만들 수 있다.

  • 잘못된 주소는 404 페이지로 안내한다.

  • 서버 내부 오류는 사용자에게 자세히 노출하지 않는다.

기능을 적으면 많아 보이지만, 구조로 보면 단순합니다.

브라우저 요청
  ↓
Flask 라우팅
  ↓
블루프린트의 뷰 함수
  ↓
폼 검증 또는 데이터베이스 처리
  ↓
템플릿 렌더링 또는 JSON 응답
  ↓
브라우저 응답

웹 애플리케이션은 결국 이 흐름의 반복입니다. 초보 단계에서 가장 중요한 자신감은 "내가 어느 위치의 코드를 고치고 있는지" 아는 데서 생깁니다.

작은 Flask 프로젝트 완성하고 배포 준비하기AI 생성 이미지

완성 프로젝트의 기본 폴더 구조

앞에서 정리한 애플리케이션 팩토리와 블루프린트를 기준으로 프로젝트를 다시 보면 다음과 같습니다.

flask-board/
├─ board/
│  ├─ __init__.py
│  ├─ db.py
│  ├─ posts.py
│  ├─ auth.py
│  ├─ api.py
│  ├─ templates/
│  │  ├─ base.html
│  │  ├─ posts/
│  │  │  ├─ list.html
│  │  │  ├─ detail.html
│  │  │  └─ create.html
│  │  ├─ auth/
│  │  │  ├─ login.html
│  │  │  └─ register.html
│  │  └─ errors/
│  │     ├─ 404.html
│  │     └─ 500.html
│  └─ static/
│     └─ style.css
├─ instance/
│  └─ board.sqlite3
├─ requirements.txt
└─ README.md

board/ 폴더는 애플리케이션 코드가 들어가는 패키지입니다. instance/ 폴더는 데이터베이스 파일이나 로컬 설정처럼 환경마다 달라질 수 있는 파일을 두는 곳입니다. 공개 저장소에 올릴 때는 instance/ 안의 민감한 파일을 제외하는 습관이 좋습니다.

posts.py는 게시글 목록, 상세, 작성 기능을 담당합니다. auth.py는 회원가입과 로그인처럼 사용자와 관련된 기능을 담당합니다. api.py는 JSON 응답을 제공하는 주소를 담당합니다. 이렇게 나누면 기능이 늘어나도 파일 하나가 지나치게 길어지는 일을 줄일 수 있습니다.

통합의 중심은 create_app()

애플리케이션 팩토리 구조에서는 create_app() 함수가 프로젝트를 조립하는 중심입니다.

# board/__init__.py
import os
from flask import Flask


def create_app():
    app = Flask(__name__, instance_relative_config=True)

    app.config.from_mapping(
        SECRET_KEY=os.getenv("SECRET_KEY", "dev"),
        DATABASE=os.getenv(
            "DATABASE",
            os.path.join(app.instance_path, "board.sqlite3"),
        ),
        DEBUG=os.getenv("FLASK_DEBUG", "0") == "1",
    )

    os.makedirs(app.instance_path, exist_ok=True)

    from . import db
    db.init_app(app)

    from . import posts
    from . import auth
    from . import api

    app.register_blueprint(posts.bp)
    app.register_blueprint(auth.bp)
    app.register_blueprint(api.bp)

    return app

핵심만 보면 세 가지입니다.

첫째, Flask(__name__, instance_relative_config=True)로 앱을 만듭니다. instance_relative_config=True는 인스턴스 폴더를 설정 파일이나 데이터 파일 위치로 쓰기 쉽게 해 줍니다.

둘째, app.config.from_mapping()으로 기본 설정을 넣습니다. 설정은 애플리케이션의 조종판과 같습니다. 비밀키, 데이터베이스 위치, 디버그 여부처럼 실행 환경에 따라 달라지는 값을 이곳에서 관리합니다.

여기서 SECRET_KEY의 기본값을 "dev"로 둔 것은 학습과 로컬 개발을 위한 안전망에 가깝습니다. 운영 환경에서는 절대 "dev" 같은 예측하기 쉬운 값을 그대로 쓰면 안 됩니다. 배포할 때는 반드시 환경변수 SECRET_KEY에 길고 무작위에 가까운 값을 넣어야 합니다.

셋째, db.init_app(app)과 app.register_blueprint()로 데이터베이스와 기능 모듈을 앱에 연결합니다. 파일을 나누었다고 해서 Flask가 자동으로 찾아 쓰지는 않습니다. 마지막에는 반드시 앱에 등록해야 합니다.

로그인 기능은 게시판의 규칙을 만든다

게시판에서 로그인은 단순히 "사용자 이름을 보여 주는 기능"이 아닙니다. 누가 글을 쓸 수 있는지 정하는 규칙입니다.

초보 프로젝트에서는 세션을 이용한 간단한 로그인부터 시작할 수 있습니다. 세션은 브라우저와 서버가 "이 사용자는 로그인했다"는 상태를 기억하게 해 주는 방식입니다. Flask의 기본 세션은 서명된 쿠키를 사용하므로, 사용자가 값을 마음대로 위조하지 못하게 하려면 SECRET_KEY가 꼭 필요합니다. 이 키는 세션 데이터가 위조되지 않았는지 확인하는 데 쓰이므로, 운영 환경에서는 반드시 예측하기 어려운 값으로 바꿔야 합니다. Flask 공식 튜토리얼도 배포 시 개발용 비밀키를 운영용 난수 값으로 교체하라고 안내합니다.1

게시글 작성 라우트에는 다음과 같은 흐름을 넣을 수 있습니다.

# board/posts.py
from functools import wraps
from flask import Blueprint, session, redirect, url_for, flash

bp = Blueprint("posts", __name__, url_prefix="/posts")


def login_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        if session.get("user_id") is None:
            flash("로그인이 필요합니다.")
            return redirect(url_for("auth.login"))
        return view(**kwargs)

    return wrapped_view

이 함수는 문 앞의 안내 직원과 비슷합니다. 사용자가 로그인하지 않았다면 글쓰기 화면으로 보내지 않고 로그인 페이지로 안내합니다. @wraps(view)는 감싼 함수의 이름과 정보를 유지해 주는 장치입니다. 지금 단계에서는 조금 낯설 수 있지만, 직접 장식자 함수를 만들 때 붙여 두면 Flask가 뷰 함수를 더 안정적으로 다룰 수 있습니다.

이제 글쓰기 함수 위에 @login_required를 붙이면 됩니다.

@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
    # 글쓰기 처리 코드
    return "글쓰기 화면"

실제 프로젝트에서는 Flask-Login 같은 확장 라이브러리를 쓰면 로그인 상태를 더 체계적으로 관리할 수 있습니다. 그래도 처음에는 세션이 어떤 역할을 하는지 직접 이해하는 것이 중요합니다.

requirements.txt로 실행 환경을 기록하기

내 컴퓨터에서 잘 돌아가는 프로젝트가 다른 컴퓨터에서도 바로 실행되리라는 보장은 없습니다. Flask 버전이 다르거나 필요한 확장 라이브러리가 설치되어 있지 않을 수 있습니다.

그래서 requirements.txt 파일에 프로젝트 의존성을 기록합니다. 의존성은 "이 프로젝트가 실행되기 위해 필요한 파이썬 패키지 목록"입니다.

현재 가상환경에서 다음 명령을 실행합니다.

python -m pip freeze > requirements.txt

예를 들어 파일 내용은 다음처럼 만들어질 수 있습니다.

blinker==1.9.0
click==8.1.8
Flask==3.1.1
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.2
Werkzeug==3.1.3

다른 환경에서는 프로젝트 폴더를 받은 뒤 가상환경을 만들고 다음 명령을 실행합니다.

python -m pip install -r requirements.txt

이 명령은 requirements.txt에 적힌 패키지를 설치합니다. 즉, 프로젝트의 실행 조건을 문서가 아니라 설치 가능한 목록으로 남기는 것입니다. 나중에 Waitress 같은 운영용 서버 패키지를 추가했다면, 설치 후 다시 pip freeze를 실행해 requirements.txt에 반영해 두어야 합니다.

설정은 코드에서 분리하기

개발할 때는 코드 안에 간단한 설정을 직접 적어도 괜찮아 보입니다.

SECRET_KEY = "dev"
DATABASE = "instance/board.sqlite3"
DEBUG = True

하지만 배포를 준비할 때는 생각이 달라져야 합니다. 비밀키는 공개되면 안 됩니다. 운영 데이터베이스 경로도 환경마다 다를 수 있습니다. 디버그 모드는 운영 환경에서 꺼야 합니다.

그래서 환경변수를 사용합니다. 환경변수는 코드 바깥에서 프로그램에 전달하는 설정값입니다. 비유하면, 같은 전자제품을 다른 나라에서 쓸 때 플러그와 전압을 환경에 맞추는 것과 비슷합니다. 제품의 핵심 회로를 바꾸지 않고 실행 환경만 바꿉니다.

import os

SECRET_KEY = os.environ["SECRET_KEY"]
DATABASE = os.getenv("DATABASE", "instance/board.sqlite3")
DEBUG = os.getenv("FLASK_DEBUG", "0") == "1"

os.environ["SECRET_KEY"]는 값이 없으면 바로 오류를 냅니다. 운영 환경에서 비밀키가 빠진 채 실행되는 것보다, 시작할 때 실패하는 편이 더 안전합니다. 반대로 os.getenv()는 값이 없을 때 기본값을 줄 수 있어 개발용 설정에 적합합니다.

배포 전에는 최소한 다음 값들을 코드에서 분리하는 것이 좋습니다.

  • SECRET_KEY

  • 데이터베이스 경로 또는 접속 주소

  • 디버그 설정

  • 외부 API 키나 이메일 비밀번호가 있다면 해당 값

개발 서버와 운영 서버는 다르다

지금까지는 주로 다음 명령으로 앱을 실행했습니다.

flask --app board --debug run

이 명령은 학습과 개발에 매우 편리합니다. 코드가 바뀌면 서버를 다시 시작해 주고, 오류가 나면 자세한 정보를 보여 줍니다. 하지만 이 서버는 운영용이 아닙니다. Flask 공식 문서는 내장 개발 서버, 디버거, 리로더가 로컬 개발용이며 보안성·안정성·효율성을 갖춘 운영 서버로 설계된 것이 아니라고 설명합니다.2

운영 환경에서는 보통 WSGI 서버를 사용합니다. WSGI는 파이썬 웹 애플리케이션과 서버가 대화하기 위한 약속입니다. Flask 앱은 WSGI 애플리케이션이고, WSGI 서버는 실제 HTTP 요청을 받아 Flask 앱이 이해할 수 있는 형태로 넘긴 뒤 응답을 다시 HTTP 응답으로 바꿔 줍니다.3

대표적인 WSGI 서버로는 Gunicorn, uWSGI, Waitress가 있습니다. 예를 들어 Waitress를 사용하면 다음처럼 실행할 수 있습니다.

python -m pip install waitress
waitress-serve --call "board:create_app"

이 명령은 board 패키지의 create_app() 함수를 호출해 Flask 앱을 만들고, Waitress가 그 앱을 실행하게 합니다. Waitress를 프로젝트 실행에 사용한다면 앞에서 말한 것처럼 requirements.txt에도 포함해 두는 것이 좋습니다. 실제 배포 플랫폼을 사용하면 이 부분을 플랫폼이 대신 처리해 주기도 합니다. 중요한 것은 flask run은 개발용이고, 공개 서비스에는 운영용 실행 방식이 필요하다는 점입니다.

배포 전 마지막 체크리스트

작은 프로젝트라도 배포 전에는 점검표가 필요합니다. 머릿속으로만 확인하면 빠뜨리기 쉽습니다.

보안

  • SECRET_KEY를 dev 같은 기본값으로 두지 않았는가?

  • 비밀키, 비밀번호, API 키를 Git에 올리지 않았는가?

  • 운영 환경에서 DEBUG=False인가?

  • 로그인하지 않은 사용자가 글쓰기, 수정, 삭제 주소에 접근할 수 없는가?

  • 사용자 입력을 템플릿에 출력할 때 자동 이스케이프를 해치지 않았는가?

오류 처리

  • 404 페이지가 준비되어 있는가?

  • 500 페이지에 Traceback이나 내부 경로가 노출되지 않는가?

  • 서버 로그에서 오류를 확인할 수 있는가?

  • 일부러 없는 주소에 접속해 오류 페이지를 확인했는가?

데이터베이스

  • 운영 데이터베이스 위치가 명확한가?

  • 초기 테이블 생성 명령을 문서화했는가?

  • 기존 데이터 백업 방법을 정했는가?

  • 배포 전에 테스트 데이터와 실제 데이터를 구분했는가?

정적 파일과 화면

  • CSS 파일이 정상적으로 불러와지는가?

  • url_for('static', filename='style.css')를 사용하고 있는가?

  • 모바일 화면에서 글 목록과 글쓰기 폼이 크게 깨지지 않는가?

  • 템플릿 경로의 대소문자가 실제 파일명과 일치하는가?

실행 환경

  • 새 가상환경에서 pip install -r requirements.txt로 설치가 되는가?

  • 운영에 필요한 환경변수가 빠졌을 때 오류가 명확히 나는가?

  • WSGI 서버로 실행해도 라우팅, 로그인, 글 작성이 동작하는가?

  • README에 실행 방법, 환경변수, 데이터베이스 초기화 방법을 적었는가?

완성의 기준

처음부터 완벽한 웹 애플리케이션을 만들 필요는 없습니다. 초보자의 첫 Flask 프로젝트에서 중요한 완성 기준은 다음입니다.

사용자가 브라우저에서 페이지를 열 수 있습니다. 글을 작성하면 서버가 입력을 검증합니다. 검증을 통과한 글은 데이터베이스에 저장됩니다. 목록과 상세 화면에서 저장된 글을 볼 수 있습니다. 로그인 규칙이 필요한 곳에 적용됩니다. 오류가 나도 사용자에게 내부 정보가 그대로 보이지 않습니다. 다른 컴퓨터에서도 requirements.txt를 이용해 실행 환경을 다시 만들 수 있습니다.

이 정도면 작은 게시판 프로젝트는 충분히 완성된 결과물입니다.

Flask를 배웠다는 것은 모든 확장 기능을 외웠다는 뜻이 아닙니다. 요청이 들어오고, 라우팅이 함수를 찾고, 함수가 데이터를 처리하고, 템플릿이나 JSON으로 응답한다는 흐름을 이해했다는 뜻입니다. 이 흐름을 알고 있으면 다음 프로젝트에서도 길을 잃지 않습니다.

이제 할 수 있는 가장 좋은 연습은 작은 기능을 하나씩 붙여 보는 것입니다. 글 수정, 글 삭제, 댓글, 검색, 페이지 나누기, 이미지 업로드 중 하나만 골라 추가해 보세요. 기능이 늘어날수록 이 책에서 배운 구조의 이유가 더 분명해질 것입니다.

참고

1Deploy to Production — Flask Documentation

2Deploying to Production — Flask Documentation

3Deploying to Production — Flask Documentation

책 목록

초보자를 위한 파이썬 Flask 학습

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기