
앞에서는 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 |