드뎌 5주차 입성!!!!
벌써 5주차라니,,, 요새 학교를 안가서 그런가
시간이 왜이리 빨리도 가는지 ㅋㅋㅋ
마지막 회고를 써보도록 하겠습니다..! 8ㅅ8
수업 목표
- Flask 프레임워크를 활용해서 API를 만들 수 있다.
- '마이 페이보릿 무비스타'를 완성한다.
- EC2에 내 프로젝트를 올리고 자랑한다!
배운 것
FileZilla 설치 및 도메인 구입
- FileZilla : 오픈소스 크로스플랫폼 무료 FTP 소프트웨어이다.
- 필자는 설치 단계에서 문제가 생겨 아래 글을 참고하여 해결하였다.
- 가비아는 IaaS형 클라우드 서비스와 PaaS, 그리고 SaaS형 그룹웨어 솔루션 등 비즈니스 IT에 필요한 서비스 전반을 제공하는 클라우드 기업이다. 사이트에 가입한 후, 원하는 도메인을 검색해 도메인을 구매한다!
- .shop 도메인이 할인 중(500원/1년)이니 그것으로 선택 후 무통장 입금으로 결제를 진행하였다.
DB 만들기 (영화인 정보 웹 스크래핑)
- init_db.py : 사용할 데이터를 웹 스크래핑 해서 DB에 저장하는 코드
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta
# DB에 저장할 영화인들의 출처 url을 가져옵니다.
def get_urls():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rpeople.nhn', headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
trs = soup.select('#old_content > table > tbody > tr')
urls = []
for tr in trs:
a = tr.select_one('td.title > a')
if a is not None:
base_url = 'https://movie.naver.com/'
url = base_url + a['href']
urls.append(url)
return urls
# 출처 url로부터 영화인들의 사진, 이름, 최근작 정보를 가져오고 mystar 콜렉션에 저장합니다.
def insert_star(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
name = soup.select_one('#content > div.article > div.mv_info_area > div.mv_info.character > h3 > a').text
img_url = soup.select_one('#content > div.article > div.mv_info_area > div.poster > img')['src']
recent_work = soup.select_one(
'#content > div.article > div.mv_info_area > div.mv_info.character > dl > dd > a:nth-child(1)').text
doc = {
'name': name,
'img_url': img_url,
'recent': recent_work,
'url': url,
'like': 0
}
db.mystar.insert_one(doc)
print('완료!', name)
# 기존 mystar 콜렉션을 삭제하고, 출처 url들을 가져온 후, 크롤링하여 DB에 저장합니다.
def insert_all():
db.mystar.drop() # mystar 콜렉션을 모두 지워줍니다.
urls = get_urls()
for url in urls:
insert_star(url)
### 실행하기
insert_all()
무비스타 API 설계
- 영화인 정보(이름, 이미지, 좋아요 수, 최근 작품 내용)를 카드로 보여주는 API를 작성해야 함!
- 조회(Read) 기능: 영화인 정보 전체를 조회
- 좋아요(Update) 기능: 클라이언트에서 받은 이름(name_give)으로 찾아서 좋아요 수 증가
- 삭제(Delete) 기능: 클라이언트에서 받은 이름(name_give)으로 영화인을 찾고, 해당 데이터를 삭제
GET 연습 (무비스타 - 보여주기)
- 서버코드 - app.py
@app.route('/api/list', methods=['GET'])
def show_stars():
movie_star = list(db.mystar.find({}, {'_id': False}).sort('like', -1))
return jsonify({'movie_stars': movie_star})
- 클라이언트 코드 - index.html
function showStar() {
$.ajax({
type: 'GET',
url: '/api/list?sample_give=샘플데이터',
data: {},
success: function (response) {
let mystars = response['movie_stars']
for (let i = 0; i < mystars.length; i++) {
let name = mystars[i]['name']
let img_url = mystars[i]['img_url']
let recent = mystars[i]['recent']
let url = mystars[i]['url']
let like = mystars[i]['like']
let temp_html = `<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img
src="${img_url}"
alt="Placeholder image"
/>
</figure>
</div>
<div class="media-content">
<a href="${url}" target="_blank" class="star-name title is-4">${name} (좋아요: ${like})</a>
<p class="subtitle is-6">${recent}</p>
</div>
</div>
</div>
<footer class="card-footer">
<a href="#" onclick="likeStar('${name}')" class="card-footer-item has-text-info">
위로!
<span class="icon">
<i class="fas fa-thumbs-up"></i>
</span>
</a>
<a href="#" onclick="deleteStar('${name}')" class="card-footer-item has-text-danger">
삭제
<span class="icon">
<i class="fas fa-ban"></i>
</span>
</a>
</footer>
</div>`
$('#star-box').append(temp_html)
}
}
});
}
POST 연습 (무비스타 - 좋아요 기능)
- 서버 코드 - app.py
@app.route('/api/like', methods=['POST'])
def like_star():
name_receive = request.form['name_give']
target_star = db.mystar.find_one({'name': name_receive})
current_like = target_star['like']
new_like = current_like + 1
db.mystar.update_one({'name': name_receive}, {'$set': {'like': new_like}})
return jsonify({'msg': '좋아요 완료!'})
- 클라이언트 코드 - index.html
function likeStar(name) {
$.ajax({
type: 'POST',
url: '/api/like',
data: {name_give:name},
success: function (response) {
alert(response['msg']);
window.location.reload()
}
});
}
POST 연습 (무비스타 - 삭제 기능)
- 서버 코드 - app.py
@app.route('/api/delete', methods=['POST'])
def delete_star():
name_receive = request.form['name_give']
db.mystar.delete_one({'name': name_receive})
return jsonify({'msg': '삭제 완료!'})
- 클라이언트 코드 - index.html
function deleteStar(name) {
$.ajax({
type: 'POST',
url: '/api/delete',
data: {name_give:name},
success: function (response) {
alert(response['msg']);
window.location.reload()
}
});
}
- 전체 코드
<!--index.html-->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>마이 페이보릿 무비스타 | 프론트-백엔드 연결 마지막 예제!</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css"/>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<style>
.center {
text-align: center;
}
.star-list {
width: 500px;
margin: 20px auto 0 auto;
}
.star-name {
display: inline-block;
}
.star-name:hover {
text-decoration: underline;
}
.card {
margin-bottom: 15px;
}
</style>
<script>
$(document).ready(function () {
showStar();
});
function showStar() {
$.ajax({
type: 'GET',
url: '/api/list?sample_give=샘플데이터',
data: {},
success: function (response) {
let mystars = response['movie_stars']
for (let i = 0; i < mystars.length; i++) {
let name = mystars[i]['name']
let img_url = mystars[i]['img_url']
let recent = mystars[i]['recent']
let url = mystars[i]['url']
let like = mystars[i]['like']
let temp_html = `<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img
src="${img_url}"
alt="Placeholder image"
/>
</figure>
</div>
<div class="media-content">
<a href="${url}" target="_blank" class="star-name title is-4">${name} (좋아요: ${like})</a>
<p class="subtitle is-6">${recent}</p>
</div>
</div>
</div>
<footer class="card-footer">
<a href="#" onclick="likeStar('${name}')" class="card-footer-item has-text-info">
위로!
<span class="icon">
<i class="fas fa-thumbs-up"></i>
</span>
</a>
<a href="#" onclick="deleteStar('${name}')" class="card-footer-item has-text-danger">
삭제
<span class="icon">
<i class="fas fa-ban"></i>
</span>
</a>
</footer>
</div>`
$('#star-box').append(temp_html)
}
}
});
}
function likeStar(name) {
$.ajax({
type: 'POST',
url: '/api/like',
data: {name_give:name},
success: function (response) {
alert(response['msg']);
window.location.reload()
}
});
}
function deleteStar(name) {
$.ajax({
type: 'POST',
url: '/api/delete',
data: {name_give:name},
success: function (response) {
alert(response['msg']);
window.location.reload()
}
});
}
</script>
</head>
<body>
<section class="hero is-warning">
<div class="hero-body">
<div class="container center">
<h1 class="title">
마이 페이보릿 무비스타😆
</h1>
<h2 class="subtitle">
순위를 매겨봅시다
</h2>
</div>
</div>
</section>
<div class="star-list" id="star-box">
</div>
</body>
</html>
## app.py
from pymongo import MongoClient
from flask import Flask, render_template, jsonify, request
app = Flask(__name__)
client = MongoClient('localhost', 27017)
db = client.dbsparta
# HTML 화면 보여주기
@app.route('/')
def home():
return render_template('index.html')
# API 역할을 하는 부분
@app.route('/api/list', methods=['GET'])
def show_stars():
movie_star = list(db.mystar.find({}, {'_id': False}).sort('like', -1))
return jsonify({'movie_stars': movie_star})
@app.route('/api/like', methods=['POST'])
def like_star():
name_receive = request.form['name_give']
target_star = db.mystar.find_one({'name': name_receive})
current_like = target_star['like']
new_like = current_like + 1
db.mystar.update_one({'name': name_receive}, {'$set': {'like': new_like}})
return jsonify({'msg': '좋아요 완료!'})
@app.route('/api/delete', methods=['POST'])
def delete_star():
name_receive = request.form['name_give']
db.mystar.delete_one({'name': name_receive})
return jsonify({'msg': '삭제 완료!'})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
프로젝트 서버에 올리기 (웹서비스 런칭)
- 언제든지 클라이언트 요청에 응답하기 위해 필요한 조건
- 컴퓨터가 항상 켜저있고 프로그램이 실행되어있어야 함.
- 모두가 접근할 수 있는 공개 주소인 IP 주소(Public IP Address)로 접근할 수 있도록 해야 함.
- 용어 설명
- DNS(Domain Name System): 사람이 알아볼 수 있는 도메인 이름(URL)을 컴퓨터가 읽을 수 있게 IP 주소로 변환하는 시스템이다.
- IP 주소(Internet Protocol address): 컴퓨터가 통신할 수 있도록 컴퓨터마다 가지는 고유한 주소. 서버는 하나의 주소를 가지고 있다.
- 포트(Port): OS 통신의 종단점. 하나의 IP에 여러 포트가 있으며, 하나의 포트에 하나의 프로그램을 실행시킬 수 있다.
AWS 서버 구매하기
- AWS EC2 콘솔페이지
- EC2 서버 종료하는 방법 (1년 후 자동결제 방지) : 대상 인스턴스에 마우스 우클릭 → '인스턴스 상태' 를 클릭 → 중지 또는 종료 중 하나를 클릭하면 명령을 실행함.
EC2 접속하기
- SSH(Secure Shell Protocol): 다른 컴퓨터에 접속할 때 쓰는 프로그램. 다른 프로그램에 비해 상대적으로 보안이 뛰어나다.
- Mac OS는 ssh가 있어서 터미널에서 바로 접근이 가능하지만, Window는 ssh가 없으므로 git bash를 사용해 접근한다.
# ssh -i 받은키페어를끌어다놓기 ubuntu@AWS에적힌내아이피
예시) ssh -i /path/my-key-pair.pem ubuntu@13.125.250.20
- 간단한 리눅스 명령어
ls: 내 위치의 모든 파일을 보여준다.
pwd: 내 위치(폴더의 경로)를 알려준다.
mkdir: 내 위치 아래에 폴더를 하나 만든다.
cd [갈 곳]: 나를 [갈 곳] 폴더로 이동시킨다.
cd .. : 나를 상위 폴더로 이동시킨다.
cp -r [복사할 것] [붙여넣기 할 것]: 복사 붙여넣기
rm -rf [지울 것]: 지우기
sudo [실행 할 명령어]: 명령어를 관리자 권한으로 실행한다.
sudo su: 관리가 권한으로 들어간다. (나올때는 exit으로 나옴)
서버 세팅하기
- 파일질라 실행 후 File → Site Manager → New Site를 클릭한다.
- Host에 EC2 서버 IP를, User에 ubuntu를 입력한 후 OK 하면 서버의 파일들을 열람하거나 업로드 할 수 있게 된다.
- initial_ec2.sh : 서버 환경(업그레이드, DB 설치, 명령어 등)을 통일하는 코드로, 실제 업무에서는 인프라 엔지니어 또는 개발 팀장님이 작성하는 경우가 많으므로 맥락만 이해해두자.
# 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 설치 후 pip3 명령어를 pip으로 사용할 수 있게 설정
sudo apt-get update
sudo apt-get install -y python3-pip
pip3 --version
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
# port forwarding : 포트포워딩, 80포트로 들어오는 요청을 5000포트로 넘겨주는 명령어
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000
# MongoDB - install : mongoDB 설치
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 : mongoDB 실행
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
- 위 파일을 파일질라에 업로드 후 git bash에서 아래 코드를 순서대로 입력하면 세팅이 완료된다.
sudo chmod 755 initial_ec2.sh
./initial_ec2.sh
- Robo3T에서 접속 정보 세팅하기: 좌측 상단 아이콘 클릭 → Create 클릭 → 접속 정보 세팅 (Name, Address) → Authentication (Perform authentication 체크박스 클릭, 계정 아이디와 비번 입력 후 save)
flask 서버 실행
- flask 설치
pip install flask
- app.py : 기초 flask 서버 파일
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
- 파일질라를 통해 EC2에 업로드 한 후 실행
python app.py
- 서버 실행이 되면 크롬에서 접속 시도 (아직 실행되지 않음. AWS에서 설정이 더 필요하다!)
http://[내 EC2 IP]:5000/
AWS에서 포트 열어주기
- AWS에서 5000포트 열어주기: EC2 관리 콘솔 → 보안그룹(launch-wizard-1) → 해당 보안그룹 클릭 → Edit inbound rules → 80포트(HTTP 접속 기본 포트), 5000포트(flask 기본 포트), 27017포트(외부에서 mongoDB 접속 하기위한 포트) 추가
- app.py 코드 수정 : pymongo 계정 접속 코드
# client = MongoClient('mongodb://아이디:비밀번호@localhost', 27017)
client = MongoClient('mongodb://test:test@localhost', 27017)
- 파일질라에서 homework 폴더를 EC2 인스턴스의 home/ubuntu 폴더에 업로드
# pymongo 패키지 설치
pip install pymongo
# home 디렉토리로 이동
cd ~
# 해당 폴더로 이동해서 아래 코드를 실행합니다.
python app.py
# 서버 파일 실행
python app.py
- 브라우저에서 접속해보기
http://내AWS아이피:5000/
포트포워딩(port forwarding)
- 포트 번호를 떼고 접속하기 : 현재 5000포트에서 웹 서비스가 실행되고 있어 :5000를 붙인 주소로 접근하고 있는데, 포트 번호를 입력하지 않아도 자동으로 접속하기 위해 포트포워딩(port forwarding)을 사용한다. (HTTP 요청에서는 80포트가 기본이라 :80을 붙이지 않아도 자동으로 연결된다.)
- 리눅스에서 기본으로 제공하는 포트포워딩을 사용한다.
nohup 설정하기
- SSH 원격 접속을 종료하더라도 서버가 계속 돌아가게 하기
nohup python app.py &
- 서버 강제 종료하기
# 아래 명령어로 미리 pid 값(프로세스 번호)을 본다
ps -ef | grep 'app.py'
# 아래 명령어로 특정 프로세스를 죽인다
kill -9 [pid값]
도메인 구입하기
- 도메인을 구입한다는 것은 네임서버를 운영해주는 업체에 IP와 도메인 매칭 유지비용을 내는 것.
- 도메인 구입 후 DNS 관리 툴 클릭
- 네임서버에 내 도메인-IP가 매칭되는 시간이 필요해 10분 정도 기다린다.
- http://내도메인/ 으로 접속하면 끝!!!!!
og태그 설정
- 카카오톡, 페이스북, 슬랙 등 SNS에 공유했을 때 예쁘게 나오도록 꾸며보기!
- static 폴더 아래에 이미지 파일(800x400)을 넣고, 프로젝트 html의 <head>태그 사이에 아래 내용 작성!
<meta property="og:title" content="내 사이트의 제목" />
<meta property="og:description" content="보고 있는 페이지의 내용 요약" />
<meta property="og:image" content="{{ url_for('static', filename='ogimage.png') }}" />
이미지를 바꾼 후 이전 이미지가 그대로 나온다면?
- 페이스북 og 태그 초기화 하기: https://developers.facebook.com/tools/debug/
- 카카오톡 og 태그 초기화 하기: https://developers.kakao.com/tool/clear/og
만든 것
좋았던 것
웹 프로젝트는 종종 해봤어도 배포는 처음 해보는 거였는데, 강의를 듣고 배포를 손쉽게 진행해 볼 수 있어서 뜻깊은 경험이었다!
메이킹 챌린지도 신청해서 열심히 참여해보겠다 !!!!
'Programming > Web' 카테고리의 다른 글
내일배움단 프로젝트 챌린지 15일차 개발일지 (0) | 2022.02.25 |
---|---|
내일배움단 프로젝트 챌린지 14일차 개발일지 (0) | 2022.02.23 |
[개발일지/회고] 스파르타코딩클럽 웹개발 종합반 - 4주차 (0) | 2022.01.27 |
[개발일지/회고] 스파르타코딩클럽 웹개발 종합반 - 3주차 (0) | 2022.01.20 |
[개발일지/회고] 스파르타코딩클럽 웹개발 종합반 - 2주차 (0) | 2022.01.15 |