[웹개발 종합반] 5주차 개발일지 - (1) 마이 페이보릿 무비스타

zzzzsb·2022년 3월 2일
0

Sparta

목록 보기
11/15

5주차

5주차 수업목표

  1. Flask 프레임워크를 활용해 API 만들기
  2. '마이 페이보릿 무비스타' 완성
  3. EC2에 내 프로젝트를 올리고, 자랑하기!

01. 5주차 설치

Filezila 설치하기

가비아 가입 & 도메인 구입

  • 접속하기 & 가입하기: https://www.gabia.com
  • 무통장 입금으로, 500원/1년하는 도메인 구입 추천

02. 5주차 오늘 배울 것

미니프로젝트 3: 마이페이보릿무비스타


프로젝트 진행 -> 배포까지 진행해볼 예정


03. [무비스타] - 프로젝트 세팅

🔥 /projects/moviestar 폴더에서 시작

미리보기

프로젝트 준비 - flask 폴더구조 만들기

  • flask, pymongo, bs4, requests 패키지 설치

04. [무비스타] - DB 만들기 (데이터 쌓기)

웹스크래핑으로 프로젝트에서 사용할 데이터 넣기 - init_db.py

기본 세팅

import requests
from bs4 import BeautifulSoup

from pymongo import MongoClient

client = MongoClient('localhost', 27017)
db = client.dbsparta

DB에 저장할 영화인들의 출처 url 가져오기

크롤링 사이트 (네이버 영화인 랭킹)

https://movie.naver.com/movie/sdb/rank/rpeople.naver

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로부터 영화인들의 사진, 이름, 최근작 정보 가져오기 + DB 저장

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():
	# mystar 콜렉션을 모두 삭제
    db.mystar.drop()  
    urls = get_urls()
    for url in urls:
        insert_star(url)

### 실행하기
insert_all()
  • 사용할 데이터를 웹 스크래핑해서, 데이터베이스에 저장하는 코드
  • init_db.py 파일 실행시 mongoDB의 mystar collection에 영화인 정보 저장됨.
  • 저장된 모습

05. [무비스타] - 뼈대 준비

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) {
                        alert(response['msg']);
                    }
                });
            }

            function likeStar(name) {
                $.ajax({
                    type: 'POST',
                    url: '/api/like',
                    data: {sample_give:'샘플데이터'},
                    success: function (response) {
                        alert(response['msg']);
                    }
                });
            }

            function deleteStar(name) {
                $.ajax({
                    type: 'POST',
                    url: '/api/delete',
                    data: {sample_give:'샘플데이터'},
                    success: function (response) {
                        alert(response['msg']);
                    }
                });
            }

        </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 class="card">
                <div class="card-content">
                    <div class="media">
                        <div class="media-left">
                            <figure class="image is-48x48">
                                <img
                                        src="https://search.pstatic.net/common/?src=https%3A%2F%2Fssl.pstatic.net%2Fsstatic%2Fpeople%2Fportrait%2F201807%2F20180731143610623-6213324.jpg&type=u120_150&quality=95"
                                        alt="Placeholder image"
                                />
                            </figure>
                        </div>
                        <div class="media-content">
                            <a href="#" target="_blank" class="star-name title is-4">김다미 (좋아요: 3)</a>
                            <p class="subtitle is-6">안녕, 나의 소울메이트(가제)</p>
                        </div>
                    </div>
                </div>
                <footer class="card-footer">
                    <a href="#" onclick="likeStar('김다미')" class="card-footer-item has-text-info">
                        위로!
                        <span class="icon">
              <i class="fas fa-thumbs-up"></i>
            </span>
                    </a>
                    <a href="#" onclick="deleteStar('김다미')" class="card-footer-item has-text-danger">
                        삭제
                        <span class="icon">
              <i class="fas fa-ban"></i>
            </span>
                    </a>
                </footer>
            </div>
        </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():
    sample_receive = request.args.get('sample_give')
    print(sample_receive)
    return jsonify({'msg': 'list 연결되었습니다!'})

@app.route('/api/like', methods=['POST'])
def like_star():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': 'like 연결되었습니다!'})

@app.route('/api/delete', methods=['POST'])
def delete_star():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': 'delete 연결되었습니다!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

06. [무비스타] - GET연습(보여주기)

문제 분석 - 화면과 동작 살펴보기

만들 기능

  • 영화인 정보를 카드로 보여주기(Read)
  • 화면에 어떤 데이터가, 어떤 부분에 보여지는가?
    • 영화인 이름
    • 영화인 이미지 : 이미지 src 속성
    • 좋아요 개수
    • 최근 작품 내용이 들어가는 부분

카드 하나를 이루는 div

<!-- 다음 코드가 하나의 카드를 이루는 div 입니다. -->
<div class="card">
  <div class="card-content">
    <div class="media">
      <div class="media-left">
        <figure class="image is-48x48">
          <img
            src="https://search.pstatic.net/common/?src=https%3A%2F%2Fssl.pstatic.net%2Fsstatic%2Fpeople%2Fportrait%2F201807%2F20180731143610623-6213324.jpg&type=u120_150&quality=95"
            alt="Placeholder image"
          />
        </figure>
      </div>
      <div class="media-content">
        <a href="https://movie.naver.com//movie/bi/pi/basic.nhn?st=1&code=397373" target="_blank" class="star-name title is-4">김다미 (좋아요: 3)</a>
        <p class="subtitle is-6">안녕, 나의 소울메이트(가제)</p>
      </div>
    </div>
  </div>
  <footer class="card-footer">
    <a href="#" onclick="likeStar('김다미')" class="card-footer-item has-text-info">
      위로!
      <span class="icon">
        <i class="fas fa-thumbs-up"></i>
      </span>
    </a>
    <a href="#" onclick="deleteStar('김다미')" class="card-footer-item has-text-danger">
      삭제
      <span class="icon">
        <i class="fas fa-ban"></i>
      </span>
    </a>
  </footer>
</div>

API 만들고 사용하기 - 영화인 조회 API (Read -> GET)

👉 만들 API

1. 조회(Read) 기능: 영화인 정보 전체를 조회
2. 좋아요(Update) 기능: 클라이언트에서 받은 이름(name_give)으로 찾아서 좋아요(like)를 증가
3. 삭제(Delete) 기능: 클라이언트에서 받은 이름(name_give)으로 영화인을 찾고, 해당 영화인을 삭제

👉 만들 API 정보

A. 요청 정보

  • 요청 URL= /api/list , 요청 방식 = GET
  • 요청 데이터 : 없음

B. 서버가 제공할 기능
: 데이터베이스에 영화인 정보를 조회(Read)하고, 영화인 정보를 응답 데이터로 보냄

C. 응답 데이터
: (JSON 형식) 'stars_list'= 영화인 정보 리스트


1. 클라이언트-서버 연결 확인

app.py

@app.route('/api/list', methods=['GET'])
def show_stars():
    sample_receive = request.args.get('sample_give')
    print(sample_receive)
    return jsonify({'msg': 'list 연결되었습니다!'})

index.html

function showStar() {
	$.ajax({
		type: 'GET',
		url: '/api/list?sample_give=샘플데이터',
		data: {},
		success: function (response) {
			alert(response['msg']);
		}
	});
}

동작 테스트

  • 새로고침 했을때, 'list 연결되었습니다.' 메시지 뜨면 동작하는 것

2. 서버 만들기

  • 영화인 정보 전체를 조회하기 위해 서버가 받을 정보는 없다. 조건없이 모든 정보를 보여줄 것이기 때문에!

서버 로직 단계

  1. mystar 목록 전체를 검색한다. ID제외, like 많은 순으로 정렬.
  2. 성공하면 success 메시지와 함께 movie_stars 목록을 클라이언트에 전달.
@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})
  • 역순 정렬시 .sort('정렬 기준 key 이름', -1)

3. 클라이언트 만들기

클라이언트 로직

  1. #star_box의 내부 html 태그를 모두 삭제한다.
  2. 서버에 1) GET 방식으로, 2) /api/list 라는 주소로 movie_stars를 요청한다.
  3. 서버가 돌려준 movie_stars를 mystars라는 변수에 저장한다.
$(document).ready(function () {
	showStar();
});

function showStar() {
	$.ajax({
		type: 'GET',
		url: '/api/list?sample_give=샘플데이터',
		data: {},
		success: function (response) {
			let mystars = response['movie_stars']
            console.log(mystars)
		}
	});
}
  • 콘솔 출력 결과
  1. for문 활용해 mystars 배열 요소를 차례대로 조회한다.
  2. mystars[i] 요소의 name, url, img_url, recent, like 키 값을 활용해 값을 조회한다.
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']
                console.log(name, img_url, recent, url, like)
			}
		}
    });
}
  • 콘솔 출력결과
  1. temp_html 만들어 #star-box에 붙인다.
...
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="#" 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="#"token interpolation">${name}')" class="card-footer-item has-text-info">
							위로!
							<span class="icon">
								<i class="fas fa-thumbs-up"></i>
							</span>
						</a>
						<a href="#"token interpolation">${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)
...

4. 완성 확인

동작테스트

  • 화면 새로고침시 영화인 정보 조회되는지 확인하기

07. [무비스타] - POST 연습(좋아요+1)

API 만들고 사용하기 - 좋아요 API(Update → POST)

👉 만들 API

1) 조회: 영화인 정보 전체를 조회
2) 좋아요: 클라이언트에서 받은 이름(name_give)으로 찾아서 좋아요(like)를 증가
3) 삭제: 클라이언트에서 받은 이름(name_give)으로 영화인을 찾고, 해당 영화인을 삭제

👉 만들 API 정보

A. 요청 정보

  • 요청 URL= /api/like , 요청 방식 = POST
  • 요청 데이터 : 영화인 이름(name_give)

B. 서버가 제공할 기능
: 영화인 이름(요청 데이터)과 일치하는 영화인 정보의 좋아요 수를 한 개 증가시켜 데이터베이스에 업데이트하고(Update), 성공했다고 응답 메세지를 보냄

C. 응답 데이터
: (JSON 형식) 'msg'='좋아요 완료!'


1. 클라이언트 - 서버 연결 확인하기

app.py

@app.route('/api/like', methods=['POST'])
def like_star():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': 'like 연결되었습니다!'})

index.html

function likeStar(name) {
    $.ajax({
        type: 'POST',
        url: '/api/like',
        data: {sample_give:'샘플데이터'},
        success: function (response) {
            alert(response['msg']);
        }
    });
}

동작 테스트

  • '위로' 버튼 눌렀을 때, 'like 연결되었습니다!' 내용의 alert 창 뜨면 제대로 연결된 것.

2. 서버 만들기

영화인 카드의 좋아요 수를 증가시키기 위해 서버가 클라이언트에게 전달받아야 하는 정보

  • 영화인의 이름(name_give)

서버 로직

  1. 클라이언트가 전달한 name_give를 name_receive 변수에 넣는다.
  2. mystar 목록에서 find_one으로 name이 name_receive와 일치하는 star를 찾는다.
  3. star의 like에 1을 더해준 new_like 변수를 만든다.
  4. mystar 목록에서 name이 name_receive인 docs의 like를 new_like로 변경한다.
@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': '좋아요 완료!'})

3. 클라이언트 만들기

클라이언트 로직

  1. 서버에
    1) POST 방식으로,
    2) /api/like 라는 url에,
    3) name_give라는 이름으로 name을 전달한다.
    (참고) POST방식이므로 data:{'name_give":name} 사용
  2. '좋아요 완료!' alert 창 띄우기
  3. 변경된 정보 반영하기 위해 새로고침
function likeStar(name) {
    $.ajax({
        type: 'POST',
        url: '/api/like',
        data: {name_give:name},
        success: function (response) {
            alert(response['msg']);
            window.location.reload()
        }
    });
}

4. 완성 확인하기

동작테스트

  • '위로' 버튼을 눌렀을 때 좋아요 수가 증가하고, 영화인 카드 순위 변경되는지 확인하기


08. [무비스타] - POST 연습(삭제하기)

API 만들고 사용하기 - 카드 삭제 API (Delete → POST)

👉 만들 API

1) 조회: 영화인 정보 전체를 조회
2) 좋아요: 클라이언트에서 받은 이름(name_give)으로 찾아서 좋아요(like)를 증가
3) 삭제: 클라이언트에서 받은 이름(name_give)으로 영화인을 찾고, 해당 영화인을 삭제

만들 API 정보

A. 요청 정보

  • 요청 URL= /api/delete , 요청 방식 = POST
  • 요청 데이터 : 영화인 이름(name_give)

B. 서버가 제공할 기능
: 영화인 이름(요청 데이터)와 일치하는 영화인 정보를 데이터베이스에서 삭제(Delete)하고, 성공했다고 응답 메세지를 보냄

C. 응답 데이터
: (JSON 형식) 'msg'='삭제 완료!'


1. 클라이언트-서버 연결 확인하기

app.py

@app.route('/api/delete', methods=['POST'])
def delete_star():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': 'delete 연결되었습니다!'})

index.html

function deleteStar(name) {
    $.ajax({
        type: 'POST',
        url: '/api/delete',
        data: {sample_give:'샘플데이터'},
        success: function (response) {
            alert(response['msg']);
        }
    });
}

동작테스트

  • '삭제' 버튼 눌렀을때 'delete 연결되었습니다!' alert 창 뜨면 연결된 것.

2. 서버 만들기

영화인 카드 삭제를 위해 필요한 정보

  • 영화인의 이름(name_give)

서버 로직

  1. 클라이언트가 전달한 name_give를 name_receive 변수에 넣기
  2. mystar에서 delete_one으로 name이 name_receive와 일치하는 star 제거
  3. 성공하면 success 메시지 반환
@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': '삭제 완료!'})

3. 클라이언트 만들기

클라이언트 로직

  1. 서버에
    1) POST 방식으로,
    2) /api/delete 라는 url에,
    3) name_give라는 이름으로 name을 전달
    (참고) POST 방식이므로 data: {'name_give': name}
  2. '삭제 완료! 안녕!' alert창 띄우기
  3. 변경된 정보를 반영하기 위해 새로고침
function deleteStar(name) {
      $.ajax({
          type: 'POST',
          url: '/api/delete',
          data: {name_give:name},
          success: function (response) {
              alert(response['msg']);
              window.location.reload()
          }
      });
  }

4. 완성 확인

동작테스트

  • 삭제 버튼 눌렀을 때 영화인 카드 삭제되는지 확인.

profile
성장하는 developer

0개의 댓글