들어가며
지금부터는 로컬 서버에 앞에서 다룬 내용으로 구현한 웹 페이지를 올려보도록 하겠다. 웹 서버를 구현하기 위한 프레임워크로는 대표적으로 Node.js(Express), Flask, Django 등이 있는데, 스파르타코딩클럽에서는 Flask로 웹 서버를 구현하는 방법에 대해서 다루었다.
Flask 개요
Flask는 서버를 구동시켜주는 프레임워크이다. 서버를 직접 구현할 수도 있지만, 이는 매우 복잡하기 때문에 대부분 웹 서비스를 개발할 때에는 '누군가 이미 개발해놓은' 프레임워크를 사용한다. 이번에도 마찬가지로 venv에 Flask를 설치할 것이므로, 프로젝트를 생성할 때 venv를 추가시켜주도록 하자.
설치
터미널에 아래 명령어를 입력하여 flask를 설치한다.
$ pip install flask
기본 코드
Flask로 서버를 구동시키는 기본 코드는 다음과 같다.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Index Page"
if __name__ == "__main__":
app.run('0.0.0.0', port=5000, debug=True)
코드를 보면, @app.route
데코레이터를 통해 접속할 웹 페이지의 URL를 받은 후 index 함수를 실행하여 'Index Page' 문자열을 반환한다는 것을 알 수 있다. URL을 여러 개로 나누고 싶다면, 아래와 같이 코드를 작성하면 된다.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Index Page"
@app.route('/about')
def index():
return "About Page"
@app.route('/help')
def index():
return "Help Page"
if __name__ == "__main__":
app.run('0.0.0.0', port=5000, debug=True)
서버 실행
프로젝트 폴더에 app.py를 생성하고, 위의 코드를 붙여넣은 후 실행시켜보면 아래와 같이 서버가 실행되고 있는 상태 메시지가 출력된다.
이제 크롬에서 http://localhost:5000/으로 접속하면, 'Index Page' 가 보이는 것을 확인할 수 있다. 서버를 종료시키고 싶다면, 터미널 창을 클릭 후 ctrl + c를 누르거나, 프로그램을 종료(stop)시키면 된다.
Flask 구조
Flask와 같은 프레임워크는 서로 다른 기본적인 구조를 갖추고 있는데, Flask의 폴더의 기본 구조는 아래와 같다.
./Project_Directory
(/venv)
/static
/templates
app.py
static 폴더는 이미지, CSS 파일 등 정적인 리소스를 저장하는 폴더이고, templates 폴더는 html 파일을 보관하는 폴더이다.
HTML 파일 관리
templates 폴더에 index.html을 생성하고, 아래와 같은 코드를 입력해보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<title>Index Page</title>
</head>
<body>
<h1>Index Page</h1>
</body>
위에서는 URL에 접속하면 문자열을 보내주었는데, Flask의 내장함수인 render_template을 통헤 HTML 파일을 보내줄 수도 있다.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template("index.html")
if __name__ == "__main__":
app.run('0.0.0.0', port=5000, debug=True)
위의 코드를 보면 index.html 파일의 경로는 render_template 함수에 의해 /templates로 지정되었다는 것을 유추해볼 수 있다.
API와 HTTP 요청 메소드
API(Application Programming Interface)
API는 서버와 클라이언트 간 소통할 수 있는 일종의 수단이며, HTTP 요청 메소드를 통해 데이터를 주고 받을 수 있다. API URL 작명 요령, RESTful API 원칙 등 API를 설계하는데에는 여러 지식과 정보가 필요하나, 여기에서는 간단하기 API를 구현해보고, 클라이언트와 소통하는 것까지만 다루었다.
HTTP 요청 메소드(GET, POST)
HTTP 요청 메소드는 이전 개발일지에도 남겨두었으나, API를 개발할 때 가장 중요한 개념이므로 다시 한 번 확인하고 넘어가자. HTTP 요청 메소드는 GET, POST, PUT, DELETE가 있으나, 이 중에서 다룰 내용은 GET, POST이므로 이 둘에 대한 내용만 정리하였다.
👉 GET
일반적으로 데이터 조회(Read) 요청 시 사용하는 메소드이며, URL뒤에 쿼리 스트링(Query String)을 입력하여 데이터를 전달할 수 있다. 예를 들어, 한국 영화에 해당하는 데이터를 요청할 때, /movie?country=한국
으로 요청할 수 있으며, 물음표 뒤의 문자열을 쿼리 스트링이라고 한다. 쿼리 스트링에 담긴 데이터는 key=value로 서버에게 전달된다.
# app.py
@app.route('/api/movie', methods=["GET"])
def get_movie():
response = {"success": True}
data = request.args
country = data.get('country')
try:
rows = [] # DB 조회 결과
response["rows"] = rows
except Exception as error:
response["success"] = False
response["error"] = str(error)
return jsonify(response)
<!-- index.html -->
<script>
$.ajax({
type: "GET",
url: "/api/movie?country=한국",
data: {},
success: (res) => {
const {success, rows, error} = res;
if (!success) {
return console.log(error);
}
const movies = $('#movies');
rows.forEach(row => {
const {title, country} = row;
const movie = `<div><h1>${title}><span>${country}</span></div>`;
movies.append(movie)
});
}
});
</script>
👉 POST
일반적으로 데이터 생성(CREATE), 수정(UPDATE), 삭제(DELETE) 요청 시 사용하는 메소드이며, GET 요청과는 다르게 HTML body에 {key: value} 형태로 데이터를 전달한다. GET 요청을 통해서도 데이터 생성, 수정, 삭제가 가능하나, POST 요청을 사용하는 이유로는 URL의 길이 제한, URL에 중요한 데이터가 포함되는 경우 보안 문제 등 여러가지가 있으며, 이에 대한 내용은 꽤 길기 때문에 추후 별도의 글로 정리하여 작성하겠다.
# app.py
@app.route('/api/movie', methods=["POST"])
def add_movie():
response = {"success": True}
data = request.form
doc = {"title": data["title"],
"country": data["country"]}
try:
print("Done") # DB에 저장
except Exception as error:
response["success"] = False
response["error"] = error
return jsonify(response)
<!-- index.html -->
<script>
$.ajax({
type: "POST",
url: "/api/movie",
data: {title: '바람과 함께 사라지다', country: '한국'},
success: (res) => {
const {success, error} = res;
if (!success) {
return console.log(error);
}
alert('저장되었습니다.');
document.location.reload();
}
});
</script>
마치며
4주차에 '데이터 삭제가 안 돼요', '데이터 수정이 안 돼요'라는 질문이 Slack에 자주 보이곤 했는데, 이는 자료형이 서로 다르기 때문에 발생하는 것임을 인지해야 한다. MongoDB에서 데이터를 수정하거나 삭제할 때에는 주로 _id를 사용하는데, 이는 ObjectId라는 또 다른 자료형의 데이터이다. 즉, Ajax를 통해 POST 요청 시 전달받은 문자열 형태의 _id로 MongoDB에서 데이터를 검색하는 경우 해당 _id를 찾을 수 없기 때문에 삭제나 수정을 할 수 없다. 이를 해결하려면 MongoDB를 처리하는 코드에서 전달받은 _id를 ObjectId 자료형으로 변환해주어야 한다. 이를 간단하게 나타낸 코드는 아래와 같으며, 실제로 제출한 과제의 코드는 여기를 클릭하여 확인할 수 있다.
from bson import ObjectId
def delete_movie(_id):
target = {_id: ObjectId(_id)}
db.delete_one(target)
다음 5주차는 마지막주차로, 지금까지 개발한 Flask 웹 서버를 AWS에 업로드하여 실제 서비스를 운영하는 방법에 대해서 다룬다. 실습에 참여하고 싶으나, AWS 계정을 생성한지 1년이 지나서 프리티어(1년간 무료)를 벗어났기 때문에 내용만 확인하였고, AWS, Oracle Cloud 등 클라우드 서비스를 사용해본 경험이 있기 때문에 나중에 Oracle 클라우드로 실습한 내용을 따로 정리하도록 하겠다.
'Development > Web' 카테고리의 다른 글
Docker 포트포워딩과 드라이브 연결 (2) | 2022.02.19 |
---|---|
Container 개념과 Docker 기초 내용 (0) | 2022.02.18 |
Bundle 개념과 WebPack 사용 방법 (0) | 2022.02.17 |
JavaScript와 JQuery, 그리고 Ajax (0) | 2022.02.13 |
HTML + CSS 자주 등장하는 질문 정리 (0) | 2022.02.11 |