[Docker] Docker로 CRUD 게시판 만들어보기 1편 - Flask 게시판을 컨테이너로 실행하기

2026. 8. 22. 22:55·개발/Docker
반응형

 

 

앞에서는 Docker의 이미지, 컨테이너, Dockerfile 같은 기본 개념을 살펴봤다.

이번에는 지금까지 공부한 내용을 실제로 써보기 위해 간단한 CRUD 게시판을 Docker로 실행해보자.

이번 실습에서는 다음 기술을 사용한다.

Python
Flask
SQLite
Docker

게시판에서는 가장 기본적인 네 가지 기능을 구현한다.

Create  → 게시글 작성
Read    → 게시글 조회
Update  → 게시글 수정
Delete  → 게시글 삭제

이 네 가지 기능을 묶어서 CRUD라고 부른다.

이번 글의 목표는 게시판 자체보다 다음 흐름을 직접 경험하는 것이다.

웹 애플리케이션 작성
        ↓
Dockerfile 작성
        ↓
Docker Image 생성
        ↓
Container 실행

1. 프로젝트 구조

먼저 프로젝트 폴더를 하나 만든다.

mkdir board
cd board

최종적으로 다음과 같은 구조를 만들 것이다.

board/
├─ app.py
├─ requirements.txt
└─ Dockerfile

각 파일의 역할은 다음과 같다.

app.py
→ 게시판 프로그램

requirements.txt
→ 필요한 Python 라이브러리

Dockerfile
→ Docker 이미지를 만드는 방법

2. Flask 설치 정보 작성하기

먼저 requirements.txt를 만든다.

Flask==3.1.2

Python에서는 requirements.txt에 프로젝트에서 사용하는 라이브러리를 기록해둘 수 있다.

나중에 Dockerfile에서 다음 명령어를 사용한다.

pip install -r requirements.txt

그러면 Docker 이미지 안에 Flask가 설치된다.


3. CRUD 게시판 만들기

이제 app.py를 작성한다.

이번에는 별도의 데이터베이스 서버를 사용하지 않고 Python에 기본적으로 포함된 SQLite를 사용한다.

from flask import Flask, request, redirect, render_template_string
import sqlite3

app = Flask(__name__)

DB_PATH = "/data/board.db"


def get_db():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


def init_db():
    conn = get_db()

    conn.execute("""
        CREATE TABLE IF NOT EXISTS posts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            content TEXT NOT NULL
        )
    """)

    conn.commit()
    conn.close()


HTML = """
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Docker Board</title>

    <style>
        body {
            width: 700px;
            margin: 50px auto;
            font-family: Arial, sans-serif;
        }

        input, textarea {
            width: 100%;
            margin-bottom: 10px;
            padding: 8px;
        }

        textarea {
            height: 100px;
        }

        .post {
            border: 1px solid #ddd;
            padding: 15px;
            margin-top: 15px;
        }

        button {
            padding: 7px 12px;
        }
    </style>
</head>

<body>

<h1>Docker 게시판</h1>

<h2>게시글 작성</h2>

<form action="/posts" method="POST">
    <input
        type="text"
        name="title"
        placeholder="제목"
        required
    >

    <textarea
        name="content"
        placeholder="내용"
        required
    ></textarea>

    <button type="submit">
        작성
    </button>
</form>


<h2>게시글 목록</h2>

{% for post in posts %}

<div class="post">

    <h3>{{ post["title"] }}</h3>
    <p>{{ post["content"] }}</p>

    <form
        action="/posts/{{ post['id'] }}/delete"
        method="POST"
    >
        <button type="submit">
            삭제
        </button>
    </form>

    <br>

    <form
        action="/posts/{{ post['id'] }}/update"
        method="POST"
    >
        <input
            type="text"
            name="title"
            value="{{ post['title'] }}"
        >

        <textarea name="content">{{ post["content"] }}</textarea>

        <button type="submit">
            수정
        </button>
    </form>

</div>

{% endfor %}

</body>
</html>
"""


@app.route("/")
def index():
    conn = get_db()

    posts = conn.execute(
        "SELECT * FROM posts ORDER BY id DESC"
    ).fetchall()

    conn.close()

    return render_template_string(
        HTML,
        posts=posts
    )


@app.route("/posts", methods=["POST"])
def create_post():
    title = request.form["title"]
    content = request.form["content"]

    conn = get_db()

    conn.execute(
        "INSERT INTO posts (title, content) VALUES (?, ?)",
        (title, content)
    )

    conn.commit()
    conn.close()

    return redirect("/")


@app.route("/posts/<int:id>/update", methods=["POST"])
def update_post(id):
    title = request.form["title"]
    content = request.form["content"]

    conn = get_db()

    conn.execute(
        """
        UPDATE posts
        SET title = ?, content = ?
        WHERE id = ?
        """,
        (title, content, id)
    )

    conn.commit()
    conn.close()

    return redirect("/")


@app.route("/posts/<int:id>/delete", methods=["POST"])
def delete_post(id):
    conn = get_db()

    conn.execute(
        "DELETE FROM posts WHERE id = ?",
        (id,)
    )

    conn.commit()
    conn.close()

    return redirect("/")


if __name__ == "__main__":
    init_db()

    app.run(
        host="0.0.0.0",
        port=5000
    )

코드는 조금 길어 보이지만 구조는 단순하다.


4. CRUD 구조 살펴보기

게시글 작성은 INSERT를 사용한다.

INSERT INTO posts (title, content)
VALUES (?, ?)

즉 CRUD의 Create다.

게시글 조회는 다음 SQL을 사용한다.

SELECT * FROM posts

CRUD의 Read에 해당한다.

수정은 UPDATE를 사용한다.

UPDATE posts
SET title = ?, content = ?
WHERE id = ?

CRUD의 Update다.

마지막으로 삭제는 다음과 같다.

DELETE FROM posts
WHERE id = ?

CRUD의 Delete에 해당한다.

결국 게시판에서 사용하는 기능과 SQL은 다음처럼 연결된다.

CRUD 기능 SQL
Create 게시글 작성 INSERT
Read 게시글 조회 SELECT
Update 게시글 수정 UPDATE
Delete 게시글 삭제 DELETE

5. Dockerfile 작성하기

이제 이 프로그램을 Docker에서 실행해보자.

프로젝트 폴더에 Dockerfile을 만든다.

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .

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

COPY app.py .

RUN mkdir -p /data

EXPOSE 5000

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

프로젝트 구조는 다음과 같다.

board/
├─ app.py
├─ requirements.txt
└─ Dockerfile

하나씩 보면 먼저 기반 이미지를 지정한다.

FROM python:3.12-slim

Python 3.12 환경을 기반으로 새로운 이미지를 만든다는 의미다.

작업 디렉터리는 /app으로 설정한다.

WORKDIR /app

그다음 requirements.txt를 복사한다.

COPY requirements.txt .

필요한 라이브러리를 설치한다.

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

그리고 애플리케이션 코드를 복사한다.

COPY app.py .

SQLite 파일이 들어갈 /data 디렉터리도 만들어준다.

RUN mkdir -p /data

마지막으로 컨테이너가 실행될 때 다음 명령어를 실행한다.

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

6. Docker 이미지 만들기

이제 Dockerfile을 기반으로 이미지를 만들어보자.

터미널에서 board 폴더로 이동한 상태에서 실행한다.

docker build -t docker-board .

여기서

docker build
→ 이미지 생성

-t docker-board
→ 이미지 이름 지정

.
→ 현재 폴더를 Build Context로 사용

이라는 의미다.

전체 흐름은 다음과 같다.

Dockerfile
    │
    │ docker build
    ↓
docker-board Image

만들어진 이미지는 다음 명령어로 확인한다.

docker images

7. 컨테이너 실행하기

이제 만든 이미지를 실행해보자.

docker run -d \
  --name board-container \
  -p 5000:5000 \
  docker-board

각 옵션은 다음과 같다.

-d
→ 백그라운드 실행

--name board-container
→ 컨테이너 이름 지정

-p 5000:5000
→ 호스트 포트와 컨테이너 포트 연결

구조로 보면 다음과 같다.

내 PC

localhost:5000
       │
       │ Port Mapping
       ↓
Container

5000
 │
 ↓
Flask

웹 브라우저에서 다음 주소로 접속하면 된다.

http://localhost:5000

게시판이 나타나면 컨테이너가 정상적으로 실행된 것이다.


8. 실행 중인 컨테이너 확인하기

현재 실행 중인 컨테이너는 다음 명령어로 확인한다.

docker ps

컨테이너 로그를 확인하고 싶다면 다음 명령어를 사용할 수 있다.

docker logs board-container

컨테이너를 중지하려면

docker stop board-container

다시 실행하려면

docker start board-container

을 사용하면 된다.


9. 그런데 문제가 하나 있다

게시판에서 게시글을 몇 개 작성해보자.

Docker 공부
Dockerfile 정리
컨테이너 실행 테스트

현재 게시글 데이터는 SQLite의 다음 위치에 저장된다.

Container

/data
└─ board.db

문제는 이 데이터가 컨테이너 내부에 존재한다는 것이다.

컨테이너를 삭제해보자.

docker rm -f board-container

그러면 컨테이너 안의 board.db도 같이 사라진다.

Container 삭제
      ↓
/data/board.db 삭제
      ↓
게시글 데이터도 삭제

컨테이너는 언제든 삭제하고 새로 만들 수 있는 실행 환경이기 때문에 중요한 데이터를 컨테이너 내부에만 저장하면 곤란하다.

여기서 앞에서 공부했던 Docker Volume이 필요해진다.

다음 글에서는 게시판의 SQLite 데이터를 Docker Volume에 저장해서 컨테이너를 삭제해도 게시글이 유지되도록 만들어본다.

반응형

'개발 > Docker' 카테고리의 다른 글

[Docker] Docker로 CRUD 게시판 만들어보기 2편 - Volume과 Bind Mount 적용하기  (0) 2026.08.22
[Docker] Dockerfile이란?  (0) 2026.08.20
[Docker] Docker 볼륨과 바인드 마운트의 차이  (0) 2026.08.20
[Docker] Docker 이미지와 컨테이너 개념 정리  (0) 2026.08.20
[Docker] Docker는 무엇일까  (3) 2025.08.16
'개발/Docker' 카테고리의 다른 글
  • [Docker] Docker로 CRUD 게시판 만들어보기 2편 - Volume과 Bind Mount 적용하기
  • [Docker] Dockerfile이란?
  • [Docker] Docker 볼륨과 바인드 마운트의 차이
  • [Docker] Docker 이미지와 컨테이너 개념 정리
danieLee
danieLee
개발일지
  • danieLee
    Code log
    danieLee
  • 전체
    오늘
    어제
    • 분류 전체보기 (77)
      • 개발 (76)
        • C++ (3)
        • java (6)
        • JavaScript (9)
        • python (0)
        • AWS (2)
        • Docker (6)
        • git (0)
        • 백엔드 (4)
        • Spring (7)
        • Django (2)
        • AI (3)
        • 코테 준비 (13)
        • 알고리즘 (6)
        • SKALA 4기 (13)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    프로그래머스
    spring
    대학생
    4기
    백엔드
    프론트
    js
    skala
    Ai
    알고리즘
    개념
    JavaScript
    파이썬
    API
    vue.js
    코테
    java
    개발자
    개발
    서버
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.1
danieLee
[Docker] Docker로 CRUD 게시판 만들어보기 1편 - Flask 게시판을 컨테이너로 실행하기
상단으로

티스토리툴바