웹 2차시

이동규·2026년 8월 16일

Layer7과제

목록 보기
19/22

Flask와 SQLite3

Flask와 SQLite3를 사용해서 간단한 웹 백엔드를 만들어본다.

웹서버

요청을 받으면 응답을 돌려주는 프로그램

일반적으로 웹서버는 웹 프레임워크로 개발된다

Flask

Flask는 Python으로 웹 백엔드를 만들 때 사용하는 웹 프레임워크이다.

프레임워크는 프로그램을 더 쉽게 만들 수 있도록 필요한 기능과 구조를 미리 제공하는 도구이다.

Flask는 문법이 간단하고 가벼워서 작은 웹사이트나 백엔드 공부에 많이 사용된다.

uv

uv는 Python의 패키지와 프로젝트 환경을 관리하는 도구이다.

Flask 같은 외부 패키지를 설치할 수 있으며 프로젝트마다 독립된 가상환경을 만들 수 있다.

Rust로 만들어져서 패키지 설치 속도가 빠른 편이다.

가상환경

가상환경은 프로젝트마다 사용하는 Python 패키지를 따로 관리하기 위한 독립된 공간이다.

프로젝트마다 필요한 패키지의 종류와 버전이 다를 수 있기 때문에 가상환경을 사용한다.

가상환경을 사용하면 두 프로젝트의 패키지가 서로 충돌하지 않는다.

Flask 개발 환경 만들기

먼저 원하는 경로에 프로젝트 폴더를 만든다.

VS Code에서 해당 폴더를 연 다음 터미널에 다음 명령어를 입력한다.

uv init
uv add flask

uv init은 현재 폴더를 uv 프로젝트로 초기화한다.

uv add flask는 프로젝트에 Flask를 설치하고 필요한 정보를 프로젝트 설정에 추가한다.

기본 Flask 서버

Flask 서버의 기본적인 형태는 다음과 같다.

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello Flask"

@app.route("/")는 사용자가 / 경로로 요청을 보냈을 때 실행할 함수를 지정한다.

위 코드에서는 /로 요청이 들어오면 index() 함수가 실행되고 Hello Flask를 반환한다.

routing

routing은 특정 경로로 요청이 들어왔을 때 어떤 함수를 실행할지 연결하는 것이다.

@app.route("/경로", methods=["GET"])
def 함수이름():
    return "응답"

예를 들어 /hello로 요청이 들어왔을 때 hello() 함수를 실행하려면 다음과 같이 작성한다.

@app.route("/hello")
def hello():
    return "Hello"

HTTP method 지정하기

하나의 경로에서 허용할 HTTP method를 지정할 수 있다.

@app.route("/login", methods=["GET", "POST"])
def login():
    return "Login Page"

이 코드는 /login 경로에서 GET과 POST 요청을 모두 허용한다.

GET  -> 페이지나 데이터 가져오기
POST -> 서버에 데이터 보내기

request

request는 사용자가 보낸 HTTP 요청의 정보를 확인할 때 사용하는 객체이다.

사용자가 어떤 method로 요청했는지, 어떤 값을 보냈는지, 어떤 form 데이터를 전송했는지 등을 확인할 수 있다.

from flask import request

쉽게 말하면 request는 사용자가 보낸 요청의 내용을 전부 들고 있는 객체이다.

query parameter 가져오기

URL의 query parameter는 request.args로 가져올 수 있다.

/hello?name=hotline

위 주소에서 name=hotline이 query parameter이다.

name = request.args.get("name", "miami")

name이라는 값을 가져오고 값이 없다면 기본값으로 miami를 사용한다.

/hello?name=hotline -> hotline
/hello              -> miami

form 데이터 가져오기

HTML의 form을 통해 전송된 데이터는 request.form으로 가져올 수 있다.

<form method="POST">
    <input type="text" name="username">
    <button type="submit">전송</button>
</form>

Flask에서는 다음과 같이 값을 가져온다.

username = request.form["username"]

HTML input의 name이 username이기 때문에 Flask에서도 username이라는 이름으로 가져온다.

즉 form 데이터를 가져올 때는 HTML의 name 속성이 중요하다.

render_template

render_template은 Flask에서 HTML 파일을 사용자에게 반환할 때 사용한다.

from flask import render_template
@app.route("/")
def index():
    return render_template("index.html")

Flask는 기본적으로 현재 프로젝트의 templates 폴더에서 HTML 파일을 찾는다.

위 코드는 templates/index.html 파일을 화면에 보여준다.

Jinja Template

Jinja는 HTML 안에서 Flask가 전달한 변수, 조건문, 반복문 등을 사용할 수 있게 해주는 템플릿 엔진이다.

Flask에서 HTML로 값을 전달할 수 있다.

@app.route("/")
def index():
    username = "hotline"
    return render_template("index.html", username=username)

HTML에서는 다음과 같이 값을 출력한다.

<h1>{{ username }}</h1>

{{ }}는 변수의 값을 출력할 때 사용한다.

{{ 변수 }}

조건문이나 반복문 같은 로직은 {% %}를 사용한다.

{% if username %}
    <p>Hello {{ username }}</p>
{% endif %}

반복문도 사용할 수 있다.

{% for user in users %}
    <p>{{ user }}</p>
{% endfor %}

session

session은 로그인한 사용자와 같은 정보를 서버에서 기억하기 위해 사용한다.

Flask에서는 다음과 같이 session을 사용한다.

from flask import session

session을 사용하려면 secret key를 설정해야 한다.

app.secret_key = "secret-key"

session에 값을 저장하는 방법은 다음과 같다.

session["username"] = "hotline"

문자열뿐만 아니라 숫자나 딕셔너리 같은 값도 저장할 수 있다.

session["user"] = {
    "id": 1,
    "username": "hotline"
}

저장된 값은 다음과 같이 가져온다.

username = session.get("username")

로그아웃할 때는 session의 값을 제거한다.

session.pop("username", None)

DB

DB는 Database의 줄임말로 데이터를 저장하고 관리하는 공간이다.

서버 프로그램의 변수에만 데이터를 저장하면 서버를 종료했을 때 데이터가 사라진다.

데이터베이스에 저장하면 서버를 껐다가 다시 실행해도 데이터가 남아 있다.

회원 정보, 게시글, 댓글과 같은 데이터를 저장할 때 데이터베이스를 사용한다.

SQLite3

SQLite3는 가볍고 사용하기 쉬운 관계형 데이터베이스이다.

별도의 데이터베이스 서버를 실행하지 않아도 되며 데이터가 하나의 파일에 저장된다.

Python에 기본적으로 포함되어 있어서 따로 패키지를 설치하지 않아도 사용할 수 있다.

작은 프로젝트나 데이터베이스 공부에 사용하기 좋으며 Flask와 함께 사용하기 편하다.

table

table은 데이터베이스에서 데이터를 저장하는 표이다.

예를 들어 사용자 정보를 저장하는 users 테이블을 만들 수 있다.

id  name     age
1   bob      18
2   alice    14

table은 행과 열로 이루어져 있다.

행과 열

열은 어떤 종류의 데이터를 저장할지 나타낸다.

행은 한 대상에 대한 실제 데이터 하나를 나타낸다.

자료형

SQLite3의 대표적인 자료형에는 TEXT, INTEGER, BLOB 등이 있다.

TEXT    -> 문자열
INTEGER -> 정수
BLOB    -> 이미지나 파일 같은 바이너리 데이터

예를 들어 사용자의 이름은 TEXT로 저장하고 나이는 INTEGER로 저장할 수 있다.

제약조건

제약조건은 데이터가 저장될 때 반드시 지켜야 할 규칙이다.

대표적으로 PRIMARY KEY, NOT NULL, UNIQUE, AUTOINCREMENT가 있다.

NOT NULL

값을 비워 둘 수 없다는 의미이다.

username TEXT NOT NULL

username에는 반드시 값이 들어가야 한다.

UNIQUE

중복된 값을 저장할 수 없다는 의미이다.

username TEXT UNIQUE

이미 존재하는 username과 같은 값을 다시 저장할 수 없다.

회원가입에서 아이디 중복을 막을 때 사용할 수 있다.

PRIMARY KEY

각 행을 구분하기 위한 기본키이다.

PRIMARY KEY에는 중복된 값이나 빈 값이 들어갈 수 없다.

쉽게 말하면 UNIQUE와 NOT NULL의 특징을 가지고 있다.

id INTEGER PRIMARY KEY

사용자의 이름은 중복될 수 있지만 id는 각 사용자를 구분해야 하기 때문에 중복되면 안 된다.

AUTOINCREMENT

새로운 데이터가 추가될 때 숫자를 자동으로 증가시킨다.

id INTEGER PRIMARY KEY AUTOINCREMENT

대기표 번호가 자동으로 증가하는 것과 비슷하다.

SQL

SQL은 관계형 데이터베이스에 명령을 내릴 때 사용하는 언어이다.

SQL을 사용해서 테이블을 만들거나 데이터를 조회, 삽입, 수정, 삭제할 수 있다.

이 네 가지 작업을 CRUD라고 한다.

Create -> 데이터 생성
Read   -> 데이터 조회
Update -> 데이터 수정
Delete -> 데이터 삭제

테이블 생성

테이블은 CREATE TABLE을 사용해서 만든다.

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    password TEXT NOT NULL
);

users라는 테이블을 만들고 id, username, password 열을 생성한다.

SQL에서는 각 열을 쉼표로 구분한다.

데이터 조회

데이터는 SELECT를 사용해서 조회한다.

SELECT username FROM users;

users 테이블에서 모든 사용자의 username을 가져온다.

모든 열을 가져오고 싶다면 *을 사용한다.

SELECT * FROM users;

특정 조건을 만족하는 데이터만 가져오고 싶다면 WHERE를 사용한다.

SELECT * FROM users WHERE username = 'hotline';

username이 hotline인 사용자만 가져온다.

비교할 때는 다음과 같은 연산자를 사용할 수 있다.

=  -> 같다
!= -> 다르다
>  -> 크다
<  -> 작다

데이터 삽입

새로운 데이터는 INSERT INTO를 사용해서 삽입한다.

INSERT INTO users (username, password)
VALUES ('hotline', '1234');

users 테이블의 username과 password 열에 새로운 값을 저장한다.

데이터 수정

기존 데이터는 UPDATE를 사용해서 수정한다.

UPDATE users
SET password = '5678'
WHERE username = 'hotline';

username이 hotline인 사용자의 비밀번호를 5678로 변경한다.

WHERE를 작성하지 않으면 테이블의 모든 행이 수정될 수 있기 때문에 주의해야 한다.

데이터 삭제

데이터는 DELETE FROM을 사용해서 삭제한다.

DELETE FROM users
WHERE username = 'hotline';

username이 hotline인 행을 삭제한다.

마찬가지로 WHERE를 작성하지 않으면 테이블의 모든 데이터가 삭제될 수 있기 때문에 주의해야 한다.

Python에서 SQLite3 사용하기

Python에서는 sqlite3 모듈을 불러와서 사용한다.

import sqlite3

데이터베이스에 연결한다.

conn = sqlite3.connect("database.db")

database.db 파일이 없다면 새로 만들고 이미 존재한다면 해당 데이터베이스에 연결한다.

SQL을 실행하기 위한 cursor를 생성한다.

cursor = conn.cursor()

SQL을 실행한다.

cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    password TEXT NOT NULL
)
""")

변경 사항을 실제 데이터베이스 파일에 저장한다.

conn.commit()

데이터베이스 연결을 종료한다.

conn.close()

데이터 안전하게 삽입하기

Python에서 사용자가 입력한 값을 SQL에 넣을 때는 문자열을 직접 이어 붙이지 않고 ?를 사용해야 한다.

username = "hotline"
password = "1234"

cursor.execute(
    "INSERT INTO users (username, password) VALUES (?, ?)",
    (username, password)
)

다음과 같이 문자열을 직접 합치는 방식은 사용하면 안 된다.

cursor.execute(
    "SELECT * FROM users WHERE username = '" + username + "'"
)

사용자의 입력이 SQL 명령어로 해석되면서 SQL Injection 취약점이 발생할 수 있기 때문이다.

fetchall

fetchall()은 SQL을 실행한 결과를 모두 가져온다.

cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()

print(rows)

테이블에 bob과 alice가 있다면 다음과 같은 결과가 나온다.

[(1, "bob", 18), (2, "alice", 14)]

결과가 여러 개이기 때문에 tuple들이 들어 있는 list 형태로 반환된다.

fetchone

fetchone()은 SQL을 실행한 결과 중 첫 번째 행 하나만 가져온다.

cursor.execute("SELECT * FROM users")
row = cursor.fetchone()

print(row)

결과는 다음과 같다.

(1, "bob", 18)

조건에 맞는 데이터가 없다면 None을 반환한다.

로그인할 사용자가 존재하는지 확인할 때 사용할 수 있다.

cursor.execute(
    "SELECT * FROM users WHERE username = ? AND password = ?",
    (username, password)
)

user = cursor.fetchone()

if user is None:
    print("로그인 실패")
else:
    print("로그인 성공")

0개의 댓글