[Flask] 시작하기

Alexandria·2023년 11월 3일
0

Python3 Flask

목록 보기
1/14
post-thumbnail

1. Flask

Flask는 파이썬 웹 프레임워크 중 하나로, 가볍고 간단한 구조로 되어있어 웹 애플리케이션을 쉽고 빠르게 개발할 수 있게 도와줍니다.

  1. 간결하고 쉬운 사용성
    Flask는 진입 장벽이 낮고 간단한 코드로도 웹 애플리케이션을 만들 수 있습니다. 작은 규모의 프로젝트부터 복잡한 프로젝트까지 유연하게 대응할 수 있습니다.

  2. 유연하고 확장 가능한 구조
    개발자는 필요에 따라 선택적으로 라이브러리를 추가하거나 구성할 수 있어서 프로젝트의 요구 사항에 따라 맞춤형 애플리케이션을 개발할 수 있습니다.
    이후 포스팅될 Flask-SQLAlchemy, Flask-WTF, Flask-Login 등과 같은 다양한 확장 라이브러리를 사용하여 개발을 보다 용이하게 할 수 있습니다.

💡 본 글은 예제 코드를 이용하여 설명합니다.

2. 폴더 구조

도커를 활용하여 Flask를 이용한 웹 애플리케이션를 구축하는 예제로써 주요한 디렉토리 구조는 다음과 같습니다.

Flask를 이용하여 웹 애플리케이션을 구축할 때, 아래의 구조와 똑같이 할 필요는 없습니다.

.
└── flask
    └── source
        └── my_app
            ├── forms
            ├── models
            ├── static
            ├── templates
            └── views

디렉토리 별 용도는 아래의 표와 같습니다.

DirectoryDescription
flask웹 서비스를 위한 컨테이너, Flask 관련 코드 등이 존재하는 디렉토리
sourceFlask가 실행되기 위한 코드가 존재하는 디렉토리
formsHTML 폼과 관련된 코드가 존재하는 디렉토리
models데이터베이스와 관련된 코드가 존재하는 디렉토리
staticCSS, JS, Font 등 정적 파일이 존재하는 디렉토리
templatesHTML 파일이 존재하는 디렉토리
viewsHTTP 요청을 처리하는 코드가 존재하는 디렉토리

3. 도커

3.1. Dockerfile

flask 폴더의 Dockerfile을 살펴보겠습니다.

Python 3.10 환경을 기반으로 하며, requirements.txt는 Python 패키지의 의존성 목록을 정의하며, pip을 이용하여 설치합니다.

컨테이너가 실행될 때, wsgi.py를 실행하여 Flask가 구동됩니다.

FROM python:3.10-bullseye

COPY ./requirements.txt /requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

RUN mkdir /source
WORKDIR /source

CMD ["python", "wsgi.py"]

3.2. requirements.txt

flask 폴더의 requirements.txt를 살펴보겠습니다.

코드 실행에 필요한 패키지를 정의합니다.

기능을 추가하기 위해 필요한 패키지가 필요한 경우, 패키지를 추가한 후 docker-compose build를 입력하여 도커 이미지를 빌드하면 됩니다.

Flask==2.3.2
Flask-SQLAlchemy==3.1.1
Flask-WTF==1.2.1
Flask-Login==0.6.3
Flask-JWT-Extended==4.5.3
Flask-Caching==2.0.2
Flask_SocketIO==5.3.6
flask-RESTx==1.2.0
PyMySQL==1.1.0
redis==4.6.0
psutil==5.9.6

💡 pip freeze를 이용하여 현재 설치된 패키지 목록과 버전을 확인할 수 있습니다.

Flask 시리즈에서 사용하게 될 주요 패키지들은 아래의 표와 같습니다.

ExampleDescription
Flask웹 애플리케이션을 쉽고 빠르게 개발하기 위한 라이브러리
Flask-WTFFlask와 함께 사용되는 폼(Form) 처리를 위한 라이브러리
Flask-Login사용자 인증 및 세션 관리를 쉽게 구현할 수 있도록 도와주는 라이브러리
Flask-RESTxRESTful API를 쉽게 개발할 수 있도록 도와주는 라이브러리
Flask-Caching간단하고 유연한 캐싱 기능을 제공하는 라이브러리
Flask_SocketIOFlask에서 웹 소켓을 지원하기 위한 라이브러리
Flask-SQLAlchemy데이터베이스 액세스 툴킷 및 ORM 라이브러리
Flask-JWT-ExtendedJWT(JSON Web Tokens)을 구현하고 관리하는 데 사용되는 라이브러리

3.3. docker-compose.yml

docker-compose.yml을 살펴보겠습니다.

flask는 기본적으로 5000번의 포트를 사용합니다. ports를 이용하여 호스트의 80번 포트를 컨테이너의 5000번 포트로 매핑합니다.

환경 변수로는 데이터베이스와 관련된 정보를 넘겨주어 Flask 코드에서 데이터베이스에 접속할 수 있도록 합니다.

  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask
    container_name: flask
    restart: always
    ports:
      - 80:5000
    depends_on:
      - mariadb
      - redis
    volumes:
      - ./flask/source:/source
    environment:
      - MARIADB_PORT=3306
      - MARIADB_USER=user
      - MARIADB_PASSWORD=user
      - MARIADB_DATABASE=mydb
    networks:
      default:
      internal:

4. Flask 구동

4.1. 설정

확장 패키지와 Flask 구동을 위한 설정 값들은 flask/source/config.py에 작성되어 있습니다.

PROPAGATE_EXCEPTIONS은 오류가 뷰에서 발생했을 때 예외를 전파할지 여부를 결정하는 설정입니다. 기본 값은 False이며 오류가 발생하면 Flask가 자동으로 처리하지만, True로 설정하게 되면 오류를 직접 처리할 수 있습니다.

SECRET_KEY는 세션, 확장 패키지들의 보안 토큰(CSRF Token, JWT 등) 등에 대한 무결성을 검증하는 값입니다. 이 값이 짧은 길이 혹은 알려진 문자 등 취약한 값으로 설정된다면 각종 보안 문제가 발생할 수 있으니 안전한 값(난수)으로 설정되어야 합니다.

DEBUG는 애플리케이션을 디버그 모드로 실행할지 여부를 결정하는 값입니다. 이 값이 True이면 500 Server Internal Error가 발생 시 웹 브라우저를 통해 자세한 내용을 확인 할 수 있으며, python prompt도 이용할 수 있습니다. 또한, 개발 과정에서 코드가 변경될 때 서버를 재시작하지 않고도 자동으로 다시 시작하여 수정 사항을 즉시 반영하여 개발자의 작업을 효율적으로 만들어줍니다. 그렇기 때문에 배포 단계에서는 DEBUG를 False로 변경해야 합니다.

import os
import secrets
import datetime

class Config:
    PROPAGATE_EXCEPTIONS = True
    SECRET_KEY = secrets.token_bytes(nbytes=16)

    # 코드 생략

class ProductionConfig(Config):
    DEBUG = False

class DevelopmentConfig(Config):
    DEBUG = True
    SECRET_KEY = "8846fb3651e8f2b6f4c61a3fa4fab7e6"
    JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(days=7)

config  = {
    "development"   : DevelopmentConfig,
    "production"    : ProductionConfig
}

4.2. 초기화

애플리케이션 객체(app)를 초기화하는 코드는 flask/source/my_app/__init__.py에 작성되어 있습니다.

create_app 함수에서는 config.py에서 지정한 설정 값들을 불러와서 애플리케이션의 동작을 구성합니다.

이후, Flask 확장 패키지들에 대한 객체를 초기화하고 Blueprint를 등록합니다.

app.config.from_object에 전달되는 config 클래스를 production으로 변경하면 DEBUG 모드를 False인 상태로 구동할 수 있습니다.

# 코드 생략

app     = Flask(import_name=__name__, template_folder="templates")
db      = SQLAlchemy()
lm      = LoginManager()
jwt     = JWTManager()
cache   = Cache()
sock    = SocketIO()
rc      = StrictRedis(host="redis", port=6379, db=0)

def create_app():
    app.config.from_object(obj=config["development"])

    with app.app_context():
        from my_app.views.index import bp_index
        from my_app.views.board import bp_board
        from my_app.views.comment import bp_comment
        from my_app.views.dashboard import bp_dashboard
        from my_app.views.cache import bp_cache
        from my_app.api.v1 import bp_api

        app.register_blueprint(blueprint=bp_index)
        app.register_blueprint(blueprint=bp_board)
        app.register_blueprint(blueprint=bp_comment)
        app.register_blueprint(blueprint=bp_dashboard)
        app.register_blueprint(blueprint=bp_cache)
        app.register_blueprint(blueprint=bp_api)

        db.init_app(app=app)
        db.create_all()

        lm.login_view       = "index.login"
        lm.login_message    = "로그인이 필요합니다."
        lm.init_app(app=app)

        jwt.init_app(app=app)

        cache.init_app(app=app)
        cache.clear()

        sock.init_app(app=app)
        
        return app

4.3. 실행

__init__.py에서 설정된 애플리케이션 객체를 통해 실행하는 코드는 flask/source/wsgi.py에 작성되어 있습니다.

기본적으로는 app.run()을 이용하지만, 본 시리즈에서는 Flask-SocketIO를 이용한 웹 소켓 예제가 포함되어 있기 때문에 sock.run()으로 실행됩니다.

from my_app import create_app, sock

app = create_app()

if __name__ == "__main__":
    sock.run(app=app, host="0.0.0.0", port=5000, allow_unsafe_werkzeug=True)

docker-compose.yml이 존재하는 디렉토리에서 docker-compose up을 입력하여 이미지를 빌드하고 애플리케이션을 실행합니다.

flask      |  * Debug mode: on
flask      | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
flask      |  * Running on all addresses (0.0.0.0)
flask      |  * Running on http://127.0.0.1:5000
flask      |  * Running on http://172.20.0.3:5000
flask      | Press CTRL+C to quit
flask      |  * Restarting with stat
flask      | Werkzeug appears to be used in a production deployment. Consider switching to a production web server instead.
flask      |  * Debugger is active!
flask      |  * Debugger PIN: 103-183-404

웹 브라우저를 통해 http://서버 IP/로 접속하면 아래와 같은 페이지가 출력될 것입니다.

5. HTTPS

Flask를 이용하여 웹 애플리케이션을 구성할 경우, 기본적으로는 HTTP 기반으로 작동합니다.

인증서를 통해 HTTPS로 구동하기 위한 방법을 알아보겠습니다.

flask/source 디렉토리에 cert라는 폴더를 생성 후 인증서와 개인키를 넣습니다.

.
├── cert
│   ├── cert.crt
│   ├── cert.csr
│   └── private.key

본 글에서는 자체 서명 인증서를 이용합니다.

openssl req -nodes -newkey rsa:2048 -keyout private.key -out cert.csr -subj "/C=KR/ST=Seoul/L=Seoul/O=Alexandria/OU=Alexandria/CN=192.168.61.142"
openssl req -x509 -key private.key -in cert.csr -out cert.crt -days 365

5.1. docker-compose.yml

HTTPS에 맞게 443번 포트를 매핑합니다.

  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask
    container_name: flask
    restart: always
    ports:
      - 443:5000
    depends_on:
      - mariadb
      - redis
    volumes:
      - ./flask/source:/source
    environment:
      - MARIADB_PORT=3306
      - MARIADB_USER=user
      - MARIADB_PASSWORD=user
      - MARIADB_DATABASE=mydb
    networks:
      default:
      internal:

5.2. 실행

애플리케이션을 구동하는 wsgi.py에서 다음과 같이 SSL 설정을 합니다. Flask-SocketIO를 사용하지 않을 경우, app.run(ssl_context=ssl_context)로 사용하면 됩니다.

import ssl
from my_app import create_app, sock

app = create_app()

if __name__ == "__main__":
    ssl_context=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ssl_context.load_cert_chain(certfile="cert/cert.crt", keyfile="cert/private.key")
    sock.run(app=app, host="0.0.0.0", port=5000, allow_unsafe_werkzeug=True, ssl_context=ssl_context)
profile
IT 도서관

0개의 댓글