0. 복습
클라이언트란 휴대폰, 브라우저 등 html, css, javascript, 데이터 등을 서버로부터 요청을 합니다.
서버는 API 창구를 통해 클라이언트와 소통을 합니다. 정해진 규칙에 따라 각 기능에 맞춰서 HTML, CSS, JS 등을 응답하거나 데이터를 전달합니다. 이 때 Json의 형태로 전송합니다. 본 프로젝트에서 서버는 Flask, 데이터베이스는 MongoDB를 활용합니다.
서버와 클라이언트는 컴퓨터의 역할을 의미합니다. 특수한 어떤 장치가 아닌 우리가 흔히 쓰는 컴퓨터임에도 데이터를 요청하는 소프트웨어 혹은 하드웨어를 클라이언트, 응답하는 소프트웨어 혹은 하드웨어를 서버라고 합니다. 이러한 클라이언트와 서버 간의 통신에는 여러가지 방식이 있습니다. HTTP, WebSocket, TCP/IP, UDP, FTP, SMTP, RPC 등이 있으며 특정한 요구사항에 따라 적절한 통신 방법을 선택해 사용해야 합니다.
통신 방법을 프로토콜이라고 합니다. 웹 어플리케이션에서 가장 일반적으로 사용되는 프로토콜인 HTTP 통신은 클라이언트가 요청하면 서버가 응답합니다. 주로 Json 형태의 데이터를 주고 받으며 서버의 API를 거치며 요청과 응답이 이루어집니다. 클라이언트에서 서버로의 요청에는 여러가지 방식이 있으며 이를 HTTP 메서드 라고 합니다. GET, POST, UPDATE, DELETE가 있습니다.
POST는 데이터를 수정할 때, GET은 주로 데이터를 가져올 때 사용되는 요청입니다.
본 프로젝트에서는 Jquery와 ajax를 활용했습니다. 앞서 기본적으로 HTML, CSS, JS를 설명하자면 각각 뼈대, 꾸미기, 동작을 담당합니다. CSS로 꾸미기를 할 때에는 클래스 명과 아이디를 통해 HTML 태그를 지정하며 해당 내용들은 모두 스타일 태그 안에 작성이 됩니다.
JQuery는 자바스크립트의 라이브리로서 HTML을 조작하기 쉽게 도와주고, 이벤트 처리, 애니메이션, Ajax 등의 기능을 쉽게 구현할 수 있도록 도와줍니다. 따라서 많은 웹 개발자들이 JQuery CDN을 Import해 사용합니다. CDN을 import 했다는 것은 jQuery 라이브러리를 로컬에 다운로드하거나 서버에 설치하는 대신 전 세계의 분산된 여러 서버로부터 해당 라이브러리를 다운로드해 사용한다는 뜻입니다. 이를 통해 웹 페이지에서 jQuery를 사용하는 사용자는 로컬에 다운로드하지 않아도 더 빠르게 웹 페이지를 로드할 수 있습니다.
JQuery CDN을 Import 하면 아래와 같이 script 태그를 사용해 jQuery 라이브러리를 로드할 수 있습니다.
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
위 코드에서 src 속성값으로 jQuery CDN의 URL을 지정하면 해당 URL로부터 jQuery 라이브러리를 다운로드해 사용합니다. 이를 통해 로컬에 저장하지 않고도 CDN에서 라이브러리를 로드해서 사용할 수 있습니다.
id 속성값과 class 명을 통해 HTML을 조작하는 가장 대표적인 기능을 사용할 수 있습니다.
$('#my-id-from-input').val()
$('.my-class-from-div')
Ajax(Asynchronous JavaScript and XML)은 비동기적인 웹 어플리케이션의 개발 방식으로서 JavaScript와 XML 등의 웹 기술을 활용해 서버와 브라우저 사이 데이터를 비동기적으로 전송하고 데이터를 받아와 동적으로 웹 페이지를 갱신하는 기술입니다. Ajax를 구현하는 방법에는 다양한 방식이 있으며 대표적으로는 jQuery, Axios, Fetch API 등의 라이브러리와 프레임워크가 있습니다.
$.ajax({
type : "GET",
url : "요청할 URL",
data : {},
success : function (response) {
//서버로부터 받은 데이터 처리
}
})
Flask 는 서버 기능을 담당하는 프레임워크입니다. app.py를 실행시키고 http://localhost:5000/로 접속하면 index.html 파일을 볼 수 있습니다.
from flask import Flask, render_template, jsonify, request
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
나만의 일기장 만들기 - 1
프로젝트 셋팅 :
패키지 : flask, mongodb
몽고디비 확인 (http://localhost:27017)
개발 순서 : HTML+CSS -> 기능명세(포스팅API, 리스팅API) -> 서버-클라이언트 연결코드 -> 포스팅 API개발 -> 리스팅 API 개발
나만의 일기장 만들기 - 2
HTML, CSS 셋팅
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<!-- Bootstrap CSS -->
<link crossorigin="anonymous" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" rel="stylesheet">
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<!--Google font-->
<link href="https://fonts.googleapis.com" rel="preconnect">
<link crossorigin href="https://fonts.gstatic.com" rel="preconnect">
<link href="https://fonts.googleapis.com/css2?family=Jua&display=swap" rel="stylesheet">
<title>스파르타코딩클럽 | 부트스트랩 연습하기</title>
<style>
* {
font-family: 'Jua', sans-serif;
}
.posting-box {
max-width: 500px;
width: 80%;
margin-top: 20px;
}
.wrap {
max-width: 900px;
width: 95%;
margin: auto;
}
</style>
</head>
<body>
<div class="wrap">
<div class="p-5 mb-4 bg-light rounded-3">
<div class="container-fluid py-5">
<h3>나홀로 일기장</h3>
<div class="posting-box">
<div class="mb-3">
<input class="form-control" id="exampleFormControlInput1" placeholder="사진 제목"
type="email">
</div>
<div class="mb-3">
<textarea class="form-control" id="exampleFormControlTextarea1" placeholder="내용 입력"
rows="3"></textarea>
</div>
<button class="btn btn-primary" type="button">저장하기</button>
</div>
</div>
</div>
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card h-100">
<img alt="..." class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to
additional content. This content is a little bit longer.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img alt="..." class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a short card.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img alt="..." class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to
additional content.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img alt="..." class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to
additional content. This content is a little bit longer.</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
나만의 일기장 만들기 - 3
서버-클라이언트 연결 코드
$(document).ready(function () {
listing()
})
function listing() {
$.ajax({
type: "GET",
url: "/diary?sample_give=샘플데이터",
data: {},
success: function (response) {
alert(response['msg'])
}
})
}
function posting() {
$.ajax({
type: "POST",
url: "/diary",
data: {sample_give: '샘플데이터'},
success: function (response) {
alert(response['msg'])
}
})
}
from flask import Flask, render_template, jsonify, request
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/diary', methods=['GET'])
def show_diary():
sample_receive = request.args.get('sample_give')
print(sample_receive)
return jsonify({'msg': 'GET 연결 완료!'})
@app.route('/diary', methods=['POST'])
def save_diary():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg': 'POST 요청 완료!'})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
나만의 일기장 만들기 - 4
posting API 만들기 :
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta_plus_w1
@app.route('/diary', methods=['POST'])
def save_diary():
title_receive = request.form['title_give']
content_receive = request.form['content_give']
doc = {
'title' : title_receive,
'content' : content_receive
}
db.posts.insert_one(doc)
return jsonify({'msg': '게시물 업로드 완료!'})
robo3t를 통해 입력받은 데이터를 확인할 수 있습니다.
나만의 일기장 만들기 - 5
파일 보내기 (HTML + CSS)
<script src="https://cdn.jsdelivr.net/npm/bs-custom-file-input/dist/bs-custom-file-input.js"></script>
<script>
$(document).ready(function () {
bsCustomFileInput.init()
})
</script>
<div class="custom-file">
<input class="custom-file-input" id="customFile" type="file">
<label class="custom-file-label" for="customFile">사진 선택하기</label>
</div>
파일받기(서버)
file = request.files["file_give"]
save_to = 'static/mypicture.jpg'
file.save(save_to)
파일 보내기(클라이언트) : 사진파일은 form data의 형태로 전송이 됩니다.
function posting() {
let title = $('#title').val()
let content = $("#content").val()
let file = $('#file')[0].files[0]
let form_data = new FormData()
form_data.append("file_give", file)
form_data.append("title_give", title)
form_data.append("content_give", content)
$.ajax({
type: "POST",
url: "/diary",
data: form_data,
cache: false,
contentType: false,
processData: false,
success: function (response) {
alert(response["msg"])
window.location.reload()
}
});
}
파일을 로컬에 저장할 때에는 다음과 같이 작성합니다.
file = request.files['file_give']
uuid = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
filename = f'file-{uuid}'
extension = file.filename.split(".")[-1]
save_to = f'static/{filename}.{extension}'
file.save(save_to)
나만의 일기장 만들기 - 6
EC2 Instance를 ubuntu의 t2.micro로 생성합니다.
서버를 셋팅할 때 아래의 쉘 스크립트를 활용합니다.
# initial_ec2.sh :
# UTC to KST : EC2의 시간대를 한국으로 맞춥니다.
sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# python3 -> python : python3 명령어를 python으로 사용할 수 있게 합니다.
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10
# pip3 -> pip
#pip3 설치
sudo apt-get update
sudo apt-get install -y python3-pip
pip3 --version
#pip3 명령어를 pip으로도 사용할 수 있게 합니다.
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
# port forwarding
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000
# MongoDB - install
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo mkdir -p /data/db
# MongoDB - run
# 몽고디비를 실행합니다.
sudo service mongod start
sleep 7
netstat -tnlp
# MongoDB set user, set conf file
# 몽고디비의 접속계정을 생성합니다.
mongo admin --eval 'db.createUser({user: "test", pwd: "test", roles:["root"]});'
# 몽고디비를 외부에 열어줍니다.
sudo sh -c 'echo "security:\n authorization: enabled" >> /etc/mongod.conf'
sudo sed -i "s,\\(^[[:blank:]]*bindIp:\\) .*,\\1 0.0.0.0," /etc/mongod.conf
sudo service mongod stop
sudo service mongod start
sleep 5
netstat -tnlp
EC2에 위의 쉘 스크립트의 권한을 변경합니다. mongodb가 정상적으로 작동하는지 확인합니다.
sudo chmod 755 initial_ec2.sh
./initial_ec2.sh
mongosh
exit
우분투에서 몽고디비 설치하기 : https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/
EC2의 보안그룹 인바운드 규칙 편집에서 5000, 80, 27017을 열어줍니다.
app.py의 mongodb client도 비밀번호와 계정을 설정해 줍니다.
client = MongoClient('mongodb://test:test@localhost', 27017)
app.py, templates, static 을 fileziillar를 활용해 서버로 옮겨준 후 flask, pymongo 패키지를 설치해 줍니다.
pip install flask
pip install pymongo
app.py를 실행합니다. nohup을 쓰면 원격 접속을 끊더라도 서버를 계속 킬 수 있습니다.
# 서버 실행하기
python app.py
# nohup으로 서버 실행하기
nohup python app.py &
# nohup으로 킨 서버 끄기 ps -ef | grep 'app.py' 는 현재 실행 중인 프로세스 중 app.py가 포함된 프로세스를 포함시킵니다.
ps -ef | grep 'app.py'
kill -9 첫번째다섯자리숫자
kill -9 두번째다섯자리숫자
# nohup으로 킨 서버 한 번에 끄기
ps -ef | grep 'python app.py' | awk '{print $2}' | xargs kill'웹 개발 > 웹개발플러스' 카테고리의 다른 글
| 웹 개발 플러스 4 - 회원가입, 로그인 기능 추가하기 (0) | 2023.03.23 |
|---|---|
| 웹 개발 플러스 3 - 맛집지도 만들기 (0) | 2023.03.17 |
| 웹 개발 플러스 2 - 나만의 단어장 (0) | 2023.03.13 |