Flask

Chapter 4-6 실습_블로그 구현 (Flask-MySQL를 활용) posts_routes.py

Chansman 2025. 4. 22. 14:22

실습_블로그 구현 (Flask-MySQL를 활용)

이번 실습에서는 Flask와 MySQL을 활용하여 간단한 블로그 시스템을 구현합니다. 블로그 시스템은 사용자에게 게시물을 생성, 조회, 수정 및 삭제할 수 있는 기능을 제공합니다. 아래에서는 전체 코드를 제시한 후, 각 프로세스를 단계별로 설명하겠습니다.

프로젝트 구조

my_flask_app/
│
├── app.py                # Flask 애플리케이션 설정 및 라우팅
├── posts_routes.py       # 게시물 관련 라우트 처리
├── db.yaml               # 데이터베이스 연결 정보
└── templates/
    ├── posts.html        # 게시물 리스트 및 작성 폼
    └── post.html         # 개별 게시물 표시

 

이번 코드는 게시글 CRUD (생성, 조회, 수정, 삭제) 기능을 제공하는 API의 라우트와 뷰 함수를 구현한 부분입니다. 이를 위해 Flask와 Flask-Smorest를 사용하여 RESTful API 스타일로 작성되었으며, MySQL 데이터베이스와의 상호작용을 위해 Flask-MySQLdb를 사용하고 있습니다.

아래는 각 기능을 단계별로 설명한 내용입니다.

전체 코드 (posts_routes.py)

from flask import request, jsonify
from flask_smorest import Blueprint, abort


def create_posts_blueprint(mysql):
    posts_blp = Blueprint(
        "posts",
        __name__,
        description="posts api",
        url_prefix="/posts",
    )

    # 게시글 목록 조회 및 생성
    @posts_blp.route("/", methods=["GET", "POST"])
    def posts():
        cursor = mysql.connection.cursor()

        # 게시글 조회
        if request.method == "GET":
            sql = "SELECT * FROM posts"
            cursor.execute(sql)

            posts = cursor.fetchall()
            cursor.close()

            post_list = []

            for post in posts:
                post_list.append(
                    {
                        "id": post[0],
                        "title": post[1],
                        "content": post[2],
                    }
                )
            return jsonify(post_list)

        # 게시글 생성
        elif request.method == "POST":
            title = request.json.get("title")
            content = request.json.get("content")

            if not title or not content:
                abort(400, message="title 또는 content가 없습니다.")

            sql = "INSERT INTO posts(title, content) VALUES(%s, %s)"
            cursor.execute(sql, (title, content))
            mysql.connection.commit()

            return jsonify({"message": "success"}), 201

    # 게시글 상세 조회, 수정 및 삭제
    @posts_blp.route("/<int:id>", methods=["GET", "PUT", "DELETE"])
    def post(id):
        cursor = mysql.connection.cursor()

        # 게시글 상세 조회
        if request.method == "GET":
            sql = f"SELECT * FROM posts WHERE id={id}"
            cursor.execute(sql)
            post = cursor.fetchone()

            if not post:
                abort(404, message="해당 게시글이 없습니다.")
            return {
                "id": post[0],
                "title": post[1],
                "content": post[2],
            }

        # 게시글 수정
        elif request.method == "PUT":
            title = request.json.get("title")
            content = request.json.get("content")

            if not title or not content:
                abort(400, message="title 또는 content가 없습니다.")

            sql = "SELECT * FROM posts WHERE id=%s"
            cursor.execute(sql, (id,))
            post = cursor.fetchone()

            if not post:
                abort(404, message="해당 게시글이 없습니다.")

            sql = "UPDATE posts SET title=%s, content=%s WHERE id=%s"
            cursor.execute(sql, (title, content, id))
            mysql.connection.commit()

            return jsonify({"message": "Successfully updated title & content"})

        # 게시글 삭제
        elif request.method == "DELETE":
            sql = "SELECT * FROM posts WHERE id=%s"
            cursor.execute(sql, (id,))
            post = cursor.fetchone()

            if not post:
                abort(404, message="해당 게시글이 없습니다.")

            sql = "DELETE FROM posts WHERE id=%s"
            cursor.execute(sql, (id,))
            mysql.connection.commit()

            return jsonify({"message": "Successfully deleted post"})

    return posts_blp

1. create_posts_blueprint 함수

이 함수는 Flask 애플리케이션에서 사용할 블루프린트를 정의합니다. 블루프린트는 Flask에서 여러 개의 라우트를 그룹화하여 모듈화할 수 있게 해주는 기능입니다. 이 함수는 mysql 객체를 받아 게시글 관련 라우트를 설정합니다.

Blueprint 설정

posts_blp = Blueprint(
    "posts",
    __name__,
    description="posts api",
    url_prefix="/posts",
)
  • Blueprint: "posts"라는 이름의 블루프린트를 생성하고, /posts라는 URL 프리픽스를 설정합니다. 이후 이 블루프린트에 정의된 모든 라우트는 /posts로 시작합니다.

2. GET 및 POST 요청 처리

게시글 목록 조회 (GET /posts)

@posts_blp.route("/", methods=["GET", "POST"])
def posts():
    cursor = mysql.connection.cursor()

    if request.method == "GET":
        sql = "SELECT * FROM posts"
        cursor.execute(sql)
        posts = cursor.fetchall()
        cursor.close()

        post_list = []

        for post in posts:
            post_list.append(
                {
                    "id": post[0],
                    "title": post[1],
                    "content": post[2],
                }
            )
        return jsonify(post_list)
  • 목표: MySQL 데이터베이스에서 모든 게시글을 조회하여 JSON 형식으로 반환합니다.
  • 동작:
    • GET 요청 시, SELECT * FROM posts 쿼리를 실행하여 모든 게시글을 조회합니다.
    • 조회된 게시글들을 post_list에 담고, 이를 jsonify로 JSON 형태로 반환합니다.

게시글 생성 (POST /posts)

elif request.method == "POST":
    title = request.json.get("title")
    content = request.json.get("content")

    if not title or not content:
        abort(400, message="title 또는 content가 없습니다.")

    sql = "INSERT INTO posts(title, content) VALUES(%s, %s)"
    cursor.execute(sql, (title, content))
    mysql.connection.commit()

    return jsonify({"message": "success"}), 201
  • 목표: 클라이언트로부터 게시글의 제목과 내용을 받아 새로운 게시글을 생성합니다.
  • 동작:
    • POST 요청 시, 요청 본문에서 title과 content 값을 받아옵니다.
    • 제목 또는 내용이 없으면 400 상태 코드와 함께 오류 메시지를 반환합니다.
    • INSERT INTO posts 쿼리를 실행하여 게시글을 추가하고, 변경 사항을 커밋한 후 성공 메시지를 반환합니다.

3. 게시글 상세 조회, 수정, 삭제 (GET /posts/<int:id>, PUT /posts/<int:id>, DELETE /posts/<int:id>)

게시글 상세 조회 (GET /posts/<id>)

@posts_blp.route("/<int:id>", methods=["GET", "PUT", "DELETE"])
def post(id):
    cursor = mysql.connection.cursor()

    if request.method == "GET":
        sql = f"SELECT * FROM posts WHERE id={id}"
        cursor.execute(sql)
        post = cursor.fetchone()

        if not post:
            abort(404, message="해당 게시글이 없습니다.")
        return {
            "id": post[0],
            "title": post[1],
            "content": post[2],
        }
  • 목표: 특정 게시글을 조회합니다.
  • 동작:
    • GET 요청 시, SELECT * FROM posts WHERE id={id} 쿼리를 실행하여 특정 게시글을 조회합니다.
    • 해당 게시글이 없으면 404 오류를 반환하고, 존재하면 게시글 데이터를 JSON 형식으로 반환합니다.

게시글 수정 (PUT /posts/<id>)

elif request.method == "PUT":
    title = request.json.get("title")
    content = request.json.get("content")

    if not title or not content:
        abort(400, message="title 또는 content가 없습니다.")

    sql = "SELECT * FROM posts WHERE id=%s"
    cursor.execute(sql, (id,))
    post = cursor.fetchone()

    if not post:
        abort(404, message="해당 게시글이 없습니다.")

    sql = "UPDATE posts SET title=%s, content=%s WHERE id=%s"
    cursor.execute(sql, (title, content, id))
    mysql.connection.commit()

    return jsonify({"message": "Successfully updated title & content"})
  • 목표: 특정 게시글의 제목과 내용을 수정합니다.
  • 동작:
    • PUT 요청 시, 요청 본문에서 title과 content 값을 받아옵니다.
    • 해당 게시글이 존재하지 않으면 404 오류를 반환합니다.
    • 게시글이 존재하면 UPDATE 쿼리를 실행하여 제목과 내용을 수정하고, 성공 메시지를 반환합니다.

게시글 삭제 (DELETE /posts/<id>)

elif request.method == "DELETE":
    sql = "SELECT * FROM posts WHERE id=%s"
    cursor.execute(sql, (id,))
    post = cursor.fetchone()

    if not post:
        abort(404, message="해당 게시글이 없습니다.")

    sql = "DELETE FROM posts WHERE id=%s"
    cursor.execute(sql, (id,))
    mysql.connection.commit()

    return jsonify({"message": "Successfully deleted post"})
  • 목표: 특정 게시글을 삭제합니다.
  • 동작:
    • DELETE 요청 시, 해당 게시글이 존재하지 않으면 404 오류를 반환합니다.
    • 존재하면 DELETE 쿼리를 실행하여 게시글을 삭제하고, 성공 메시지를 반환합니다.

마무리

이 코드는 게시글 CRUD 기능을 제공하는 API의 라우트와 뷰 함수입니다. Flask의 Blueprint를 사용하여 게시물 관련 라우트를 모듈화하고, MySQL 데이터베이스와 상호작용하여 게시물을 생성, 조회, 수정 및 삭제할 수 있게 구현했습니다. Flask-Smorest의 abort 함수를 사용하여 유효하지 않은 요청에 대한 오류 처리를 하고, 각 요청에 대해 적절한 HTTP 상태 코드와 메시지를 반환합니다.