본문 바로가기
개발(Develop)/웹(Web) _프로젝트project

웹 / 영화를 기록하는 나만의 메모장 웹 만들기/ 메타(meta)태그 크롤링하기/ 네이버 영화 크롤링해서 웹에 메모하기

by 왁왁s 2021. 6. 7.

이번 글에서는 나만의 영화를 기록하는 메모장을 만들어보자.

 

어떤 식으로 구현하냐면, 

클라이언트가 인상깊었던 영화를 네이버에서 url을 가져오고

해당 영화에 어떤 부분이 재밌었는지 코멘트를 달게 되면

기록이 되는 방식의 메모장이다.

 

프로젝트 개발환경 세팅하기

myonlymovie라는 새프로젝트를 만들어준다.

static 폴더에는 CSS나 이미지 파일들을 담아둘 때 사용할 것이다.

그리고 templates 폴더에는 HTML 파일들을 담아둘 때 사용할 것이다.

 

그리고 서버로 활용할 app.py 파일을 생성해준다.

그리고 미리 templates 폴더에 index.html 파일을 생성해준다.


그리고 라이브러리를 설치해줘야 하는데

우리가 이번 글에서 사용할 것은

데이터베이스, 크롤링, 서버를 사용할 것이다.

 

서버를 위한 flask

 

DB를 위한 pymongo

 

크롤링을 위한 requestsbs4

 

라이브러리를 다운로드 받아준다.


오늘 다룰 '나만의 영화 저장집' 프로젝트

오늘은 새롭게 다른 코드를 작성하는 것이 아니라, 우리가 이전 글에서

다뤘던 HTML 코드 형태를 가져와 활용 것이다.

 

바로 이전 글에서 다뤘던 '나만의 영화 저장집' 사이트이다.

 

관련 내용은 아래 참고 ▼

웹 / 나만의 영화저장 웹 만들기 / 영화 OpenAPI / Ajax와 jQuery를 활용한 예제/ 영화 API 활용한 웹 개발

 

웹 / 나만의 영화저장 웹 만들기 / 영화 OpenAPI / Ajax와 jQuery를 활용한 예제/ 영화 API 활용한 웹 개

웹 / 제이쿼리(jQuery)란 무엇인가/유용한공부사이트/함께해보는예제/쉬운설명/ w3schools 웹 / 제이쿼리(jQuery)란 무엇인가/유용한공부사이트/함께해보는예제/쉬운설명/ w3schools 다음은 자바스크립트

parkjh7764.tistory.com

'포스팅박스 열기' 버튼을 눌렀을 때 열리는 포스팅 박스.


 

API 설계하기

프로젝트를 하기 전에 API를 설계를 해야 한다.

그래야 우리 서비스에 어떤 기능들이 필요하고, 

그 기능을 어떤 순서로 구현할 것인지를 파악하기 때문이다.

 

이번 글에서 활용할 '나만의 영화 저장집'을 보면서 API를 설계해보자.

 

우선 포스팅 API, 리스팅 API 2개로 두 분류로 나눌 수 있다.


포스팅 API

포스팅 박스를 열고 URL과 코멘트를 입력했을 때

데이터베이스에 저장이 되는 방식이다.

 

1. 클라이언트 쪽에서 POST 방식을 써서 URL, 코멘트를 서버로 보낸다.

2. 서버에서는 meta 태그 정보를 바탕으로 영화 제목, 설명, 이미지, URL을 크롤링한다.

3. 이를 모두 DB에 저장한다.


리스팅 API는

데이터베이스에 저장한 데이터를 활용해

영화 포스터, 제목, 코멘트, 링크가 있는 카드형태로 추가 되는 것이다.

 

1. 서버에서 DB에 저장되어 있는 제목, 설명, URL, 이미지URL, 코멘트 정보를 가져온다.

2. DB에 저장되어 있던 데이터를 활용해 카드 형태로 만들어 추가한다.

 



그럼 우선, 포스팅 박스를 보면 URL과 코멘트를 입력하는 칸이 있다.

 

그런데 여기서 URL만 입력해도 

URL을 통해서 영화 사진, 제목, 설명 등에 대한 데이터를 가져올 수 있어야 한다.

 

이를 하기 전에 새롭게 알아야할 내용이 있다.

바로 '메타 태그'이다.

 

메타 태그(meta tag)란?

메타 태그는 메타 데이터를 담기 위한 태그이다. 

네이버 사전에 나오는 '메타 데이터'의 정의이다.


간단히 말하면,

'메타(meta) 태그는 HTML을 통해 만든 웹 페이지를 

브라우저가 개괄적으로 판단할 수 있도록 도움을 주기 위해서 사용하는 태그'이다.

 

메타 태그를 통해 웹에 대한 개략적인 정보를 제공하고, 브라우저는 메타 태그를 통해서

보다 체계적으로 분류하고 활용할 수 있다.


예시로, 우리가 카카오톡으로 URL을 보냈을 때의 생김새를 보면 된다.

아래 예시는 내 티스토리 블로그를 카톡으로 전송했을 때의 사진이다.

 

카카오톡은 썸네일을 보여주기 위해

어떤 메타(meta) 태그를 활용했을까?

 

티스토리의 사진 ☞ og:image

HwanE Develop Blog ☞ og:title

남들과는 다르게 ☞ og:description


직접 확인을 해보자.

내 티스토리 블로그를 들어가 마우스 우클릭 후 [검사]를 눌러보자.

메타 태그는 <head></head> 부분에 들어가서 사이트의 속성을 설명해주는 태그이다.

그러면 <head> 부분에서 찾아보자.

 

 

찾았다!

카카오톡에 URL 주소를 보낼 때

노란색 형광펜으로 표시한 부분을 활용한 것이다.

 


카카오톡도 메타(meta) 태그의 속성을 활용했는데, 

그러면 우리도 메타 태그를 스크래핑하여 활용해보자.

 

메타(meta) 태그 크롤링 해보기
크롤링을 위한 requests, bs4 패키지 기본 골격 코드
import requests
from bs4 import BeautifulSoup

url = 'https://movie.naver.com/movie/bi/mi/basic.nhn?code=190726'

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')

requests와 bs4 라이브러리를 사용하기 위해서는 패키지를 다운로드 받아줘야 한다.

 

이전에 자세히 다뤘기 때문에 코드에 대한 자세한 설명은 아래를 참고하자 ▼

네이버 영화 순위 크롤링, 웹스크래핑 해보기/ 파이썬 bs4 패키지 사용법/ 네이버 API 크롤링하기

 

기본 골격 코드에 url 에는 요즘 상영중인 인기 영화 '컨저링3'를 미리 넣어놨다.

https://movie.naver.com/movie/bi/mi/basic.nhn?code=190726 


이전 글을 확인해보면 기존에 했던 copy -> copy selector로 

meta 태그의 셀렉터를 가져와 크롤링하려고 하면 안 되는 경우가 있다.


기존의 방법으로 메타 태그의 셀렉터를 가져오면 안 되는 이유

title = soup.select_one("head > meta:nth-child(9)")

 

셀렉터를 복사해서 붙여 넣어보면 알 수 있다.

head > meta:nth-child(9)

nth-child(9) 순서를 기준으로 메타 태그 셀렉터가 정의가 되어 있다.

 

그 이유는 우리가 URL을 브라우저를 통해 들어갔을 때의 메타(meta) 태그의 순서

파이썬 코드가 접속했을 때메타(meta) 태그 순서가 다르기 때문이다.

 


 그러면 bs4 패키지를 사용해 어떻게 meta 태그를 가져오는가?

이것은 파이썬의 bs4(beautifulsoup) select의 새로운 사용 방법이다.

 

<meta property="og:title"> 부분을 마우스로 더블 클릭하고 ctrl+c 키로 복사한다.


title = soup.select_one('meta[property="og:title"]')

 

그러고는 select_one에 붙여넣어준다.

해당 코드는

'메타(meta) 태그 중에 [ ] 괄호 안에 있는 속성과 일치하는 애를 가져와라'라는 코드이다.

 

위와 같이 코드를 쓰고, print(title)을 통해 title 변수를 출력해주면 아래와 같은 결과가 나온다.

그런데 우리는 "컨저링 3: 악마가 시켰다"라는 영화 content만 가져오고 싶기 때문에

content만 출력하기 위해서는 속성을 지정해준다.

 

title = soup.select_one('meta[property="og:title"]')['content']

같이 작성하면 원하는 타이틀만 가져올 수 있다.


그렇다면, title 말고도 우리가 필요한 디스크립션과 이미지 url을 가져올 수 있을까?

title을 크롤링했던 것과 같은 방식으로 가져오면 된다.

 

soup.select_one으로 영화 제목, 이미지, 디스크립션 크롤링하는 코드
import requests
from bs4 import BeautifulSoup

url = 'https://movie.naver.com/movie/bi/mi/basic.nhn?code=190726'

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')

og_title = soup.select_one('meta[property="og:title"]')['content']
og_image = soup.select_one('meta[property="og:image"]')['content']
og_desc = soup.select_one('meta[property="og:description"]')['content']

print(og_title)
print(og_image)
print(og_desc)



자. 그러면 이제 정말 나만의 영화 메모장을 만들어보자.

나만의 영화 메모장 만들기

우선 우리가 중점으로 볼 것은 네이버 영화 url을 클라이언트가 입력하면,

url 을 통해 크롤링한 정보를 DB에 저장한 후에, 클라이언트에게 시각적으로 제공하는 것에 중점을 둘 것이기 때문에

app.py 서버와 API, HTML 기본 틀에 대한 코드는 제공하겠다.

 

app.py 파일/ 나만의 영화 메모장 서버, API 기본 골격 코드
from flask import Flask, render_template, jsonify, request
app = Flask(__name__)

import requests
from bs4 import BeautifulSoup

from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbhwanMovie

## HTML을 주는 부분
@app.route('/')
def home():
   return render_template('index.html')

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

## API 역할을 하는 부분
@app.route('/memo', methods=['POST'])
def saving():
    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)

index.html 파일 / 나만의 영화 저장집 html 기본 골격 코드 

<!Doctype html>
<html lang="ko">

    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">

        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>

        <!-- 구글폰트 -->
        <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">


        <title>나만의 영화 기록하기</title>

        <!-- style -->
        <style type="text/css">
            * {
                font-family: "Stylish", sans-serif;
            }

            .wrap {
                width: 900px;
                margin: auto;
            }

            .comment {
                color: blue;
                font-weight: bold;
            }

            #post-box {
                width: 500px;
                margin: 20px auto;
                padding: 50px;
                border: black solid;
                border-radius: 5px;
            }
        </style>
        <script>
            $(document).ready(function () {
                showArticles();
            });

            function openClose() {
                if ($("#post-box").css("display") == "block") {
                    $("#post-box").hide();
                    $("#btn-post-box").text("영화 포스팅 열기");
                } else {
                    $("#post-box").show();
                    $("#btn-post-box").text("영화 포스팅 닫기");
                }
            }

            function postArticle() {
                $.ajax({
                    type: "POST",
                    url: "/memo",
                    data: {sample_give:'샘플데이터'},
                    success: function (response) { // 성공하면
                        alert(response["msg"]);
                    }
                })
            }

            function showArticles() {
                $.ajax({
                    type: "GET",
                    url: "/memo?sample_give=샘플데이터",
                    data: {},
                    success: function (response) {
                        alert(response["msg"]);
                    }
                })
            }
        </script>

    </head>

    <body>
        <div class="wrap">
            <div class="jumbotron">
                <h1 class="display-4">나만의 영화 저장집</h1>
                <p class="lead">★봤던 영화들 중 인상 깊었던 영화 저장 공간★</p>
                <hr class="my-4">
                <p class="lead">
                    <button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기
                    </button>
                </p>
            </div>
            <div id="post-box" class="form-post" style="display:none">
                <div>
                    <div class="form-group">
                        <label for="post-url">URL</label>
                        <input id="post-url" class="form-control" placeholder="">
                    </div>
                    <div class="form-group">
                        <label for="post-comment">영화 코멘트</label>
                        <textarea id="post-comment" class="form-control" rows="2"></textarea>
                    </div>
                    <button type="button" class="btn btn-primary" onclick="postArticle()">영화 메모</button>
                </div>
            </div>
            <div id="cards-box" class="card-columns">
                <div class="card">
                    <img class="card-img-top"
                         src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSd7VF0vJve5JA9nv45Uq7fGT1u9OVzE7Q9-A&usqp=CAU"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="https://i0.wp.com/kiramonthly.com/wp-content/uploads/2020/02/1.jpg?fit=1000%2C1429"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSd7VF0vJve5JA9nv45Uq7fGT1u9OVzE7Q9-A&usqp=CAU"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="https://i0.wp.com/kiramonthly.com/wp-content/uploads/2020/02/1.jpg?fit=1000%2C1429"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSd7VF0vJve5JA9nv45Uq7fGT1u9OVzE7Q9-A&usqp=CAU"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="https://i0.wp.com/kiramonthly.com/wp-content/uploads/2020/02/1.jpg?fit=1000%2C1429"
                         alt="Card image cap">
                    <div class="card-body">
                        <a target="_blank" href="#" class="card-title">영화 제목</a>
                        <p class="card-text">영화의 디스크립션이 들어갑니다...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
            </div>
        </div>
    </body>

</html>

기본 골격 코드에 대한 것은 제공했으니 복사 붙여넣기 하자.

이제 우리가 중점적으로 볼 서버, 데이터베이스, 클라이언트 부분을

수정하면서 공부해보자 !

 

우리가 위에서 봤던 API 설계를 참고하면서 코드를 작성해보자.

 


포스팅 API

포스팅 박스를 열고 URL과 코멘트를 입력했을 때

데이터베이스에 저장이 되는 방식이다.

 

1. 클라이언트 쪽에서 POST 방식을 써서 URL, 코멘트를 서버로 보낸다.

2. 서버에서는 meta 태그 정보를 바탕으로 영화 제목, 설명, 이미지, URL을 크롤링한다.

3. 이를 모두 DB에 저장한다.


리스팅 API는

데이터베이스에 저장한 데이터를 활용해

영화 포스터, 제목, 코멘트, 링크가 있는 카드형태로 추가 되는 것이다.

 

1. 서버에서 DB에 저장되어 있는 제목, 설명, URL, 이미지URL, 코멘트 정보를 가져온다.

2. DB에 저장되어 있던 데이터를 활용해 카드 형태로 만들어 추가한다.

 


자 그러면 우선 포스팅 API 먼저 구성을 해보자.

 

클라이언트 쪽에서 URL과 코멘트를 작성하여 버튼을 눌렀을 때 서버로 보내는 코드를 먼저 작성해보자.

서버 쪽을 먼저 건드려보자.


app.py 파일 /서버, API 부분 ( POST 방식 )

우리가 받아야 할 것은 클라이언트가 입력한 URL과 Comment이다. 

그리고 받아온 URL에서 영화의 타이틀, 이미지, 디스크립션을 크롤링해서 데이터를 뽑아내야 한다.

 

그럼 app.py 파일 API 부분에 위에서 잠깐 다뤘던 크롤링하는 코드를 넣어주면 된다.


app.py 파일 / POST 방식의 API 작성 완료한 코드
## API 역할을 하는 부분
@app.route('/memo', methods=['POST'])
def saving():
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']

    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_receive, headers=headers)
    soup = BeautifulSoup(data.text, 'html.parser')

    og_title = soup.select_one('meta[property="og:title"]')['content']
    og_image = soup.select_one('meta[property="og:image"]')['content']
    og_desc = soup.select_one('meta[property="og:description"]')['content']

    db_insert = {
        'title':og_title,
        'image':og_image,
        'desc':og_desc,
        'url': url_receive,
        'comment':comment_receive
    }
    db.movies_memo.insert_one(db_insert)

    return jsonify({'msg':'영화 저장이 완료되었습니다.'})

POST 방식이기 때문에 request.form[' ']으로 사용자가 입력한 URL과 Comment를 받아올 것이다.

그리고 위에 올려다보면 아까 다뤘던 select_one으로 크롤링하는 코드에서 사용하는 부분을

복사해서 붙여넣어준다.

 

영화 타이틀, 이미지, 디스크립션을 크롤링og_title, og_image, og_desc 변수에 넣는다.

그리고 mongoDB 데이터베이스에 크롤링해서 가져온 영화 타이틀, 이미지, 디스크립션과 함께

사용자로부터 입력받은 URL과 코멘트를 함께 

 

db.movies_memo.insert_one(db_insert) 코드로

데이터베이스 movies_memo 컬렉션에 저장한다.


클라이언트 부분 ( POST 방식 )

클라이언트 부분에서는 중점적으로 클라이언트로부터 URL과 코멘트를 입력받아

서버로 넘겨주는 작업을 해주어야 한다.

 

index.html 파일 / 클라이언트로부터 URL, 코멘트를 입력받아 서버로 넘겨주는 코드
            function postArticle() {
                let client_url = $("#post-url").val()
                let client_comment = $("#post-comment").val()

                $.ajax({
                    type: "POST",
                    url: "/memo",
                    data: {'url_give':client_url, 'comment_give':client_comment},
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload()
                    }
                })
            }

 

function postArticle( ) 함수는 영화 포스팅 박스에서

[영화 메모]라는 버튼을 클릭했을 때 실행되는 함수이다.

 

사용자가 URL과 영화 코멘트를 입력하고 영화 메모 버튼을 누르면

$.ajax 코드가 실행이 되어 작성한 데이터가 서버로 넘어가게 된다.

 

window.location.reload() 코드는 새로고침(F5) 기능을 한다.


그럼 클라이언트가 입력하는 POST 방식의 코드가 완성되었다.

정상적으로 작동하는지 확인해보자.

 

영화 URL을 가져온다. 나는 요즘 핫한 귀멸의 칼날 영화 url을 준비했다.

https://movie.naver.com/movie/bi/mi/basic.nhn?code=196051 

 

영화 포스팅 창에 URL

영화 메모 버튼을 누르게 되면 '영화 저장이 완료되었습니다'라는 alert 창이 뜬다.


영화가 정말 저장이 완료되었는지, Robo 3T를 열어

데이터베이스 movies_memo 컬렉션을 클릭해 저장되었는지 확인한다.

정상적으로 5가지의 데이터, 영화 제목, 이미지, 디스크립션, URL, 코멘트가 들어간 것을 확인할 수 있다.


app.py 파일 / 서버, API ( GET 방식 )

 

이제는 데이터베이스에(DB)에 저장된 데이터를 가지고 와서

웹에 리스팅하는 작업을 해줄 것이다. app.py 파일을 먼저 보자.

 

우리는 mongoDB 데이터베이스에서 모든 도큐먼트(document)를 가져오는데

id 값은 출력에서 제외시켜서 가져올 것이다.

 

그리고 서버에서 키(key) 값을 정해 데이터를 ajax에서 사용할 수 있도록 전송해준다.

 

app.py 파일 / 서버 부분 GET 방식 코드
@app.route('/memo', methods=['GET'])
def listing():
    movies_data = list(db.movies_memo.find({},{"_id":False}))
    return jsonify({'all_movies':movies_data})

index.html 파일 / 클라이언트 ( GET 방식 )

 

이제 클라이언트 부분ajax 코드를 살펴보겠다.

ajax 코드에서 해줄 것은 서버에 있는 API에서 보낸 데이터를

영화 제목, 이미지, 디스크립션, URL, 코멘트로 분류한 후에

카드 형태로 사용자가 작성할 때마다 추가해주는 것이다.

 

서버 API에서 "all_movies" 키(key) 값으로 전달했기에 키 값을 사용해

데이터를 받고, for 반복문을 돌려서 필요한 속성들만 빼와 카드 형태로 추가해주는 코드이다.

index.html 파일 / ajax 코드 (GET 방식)
function showArticles() {
                $.ajax({
                    type: "GET",
                    url: "/memo",
                    data: {},
                    success: function (response) {
                        movies = response["all_movies"]
                        for(let i = 0; i < movies.length; i++){
                            let title = movies[i]["title"]
                            let image = movies[i]["image"]
                            let desc = movies[i]["desc"]
                            let url = movies[i]["url"]
                            let comment = movies[i]["comment"]

                            let temp_html = `<div class="card">
                                                <img class="card-img-top"
                                                     src="${image}"
                                                     alt="Card image cap">
                                                <div class="card-body">
                                                    <a target="_blank" href="${url}" class="card-title">${title}</a>
                                                    <p class="card-text">${desc}</p>
                                                    <p class="card-text comment">${comment}</p>
                                                </div>
                                            </div>`

                            $("#cards-box").append(temp_html)

                        }
                    }
                })
            }

모든 코드가 완성되었다. 그러면 바로 확인을 해보자.

 

localhost:5000으로 들어가게 되면 이전에 데이터베이스에 넣어놨던

'귀멸의 칼날'이라는 영화가 저장된 것을 볼 수 있다. 

영화의 이미지, 영화 제목, 제목을 클릭했을 때 영화 사이트에 접속하는 하이퍼링크,

디스크립션, 내가 작성한 코멘트까지 잘 들어간 것을 확인할 수 있다.


포스팅 박스를 열어서, 네이버 영화 URL을 붙이고,

영화 코멘트를 작성한 후 [영화 메모] 버튼을 눌러보자


저장이 완료되었다는 알림창(Alert)가 뜬다.

5개의 영화를 추가해봤다. 코멘트 색깔을 눈에 더 잘 띄게 빨간색으로 바꾸었다.

이렇게 나만의 영화 저장집이 완성되었다.


Robo 3T를 통해 데이터베이스를 확인해보면

데이터도 맞게 저장된 것을 확인할 수 있다.


나만의 영화 저장집 완성


index.html 파일 / html 전체 코드
<!Doctype html>
<html lang="ko">

    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">

        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>

        <!-- 구글폰트 -->
        <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">


        <title>나만의 영화 기록하기</title>

        <!-- style -->
        <style type="text/css">
            * {
                font-family: "Stylish", sans-serif;
            }

            .wrap {
                width: 900px;
                margin: auto;
            }

            .comment {
                color: red;
                font-weight: bold;
            }

            #post-box {
                width: 500px;
                margin: 20px auto;
                padding: 50px;
                border: black solid;
                border-radius: 5px;
            }
        </style>
        <script>
            $(document).ready(function () {
                showArticles();
            });

            function openClose() {
                if ($("#post-box").css("display") == "block") {
                    $("#post-box").hide();
                    $("#btn-post-box").text("영화 포스팅 열기");
                } else {
                    $("#post-box").show();
                    $("#btn-post-box").text("영화 포스팅 닫기");
                }
            }

            function postArticle() {
                let client_url = $("#post-url").val()
                let client_comment = $("#post-comment").val()

                $.ajax({
                    type: "POST",
                    url: "/memo",
                    data: {'url_give':client_url, 'comment_give':client_comment},
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload()
                    }
                })
            }

            function showArticles() {
                $.ajax({
                    type: "GET",
                    url: "/memo",
                    data: {},
                    success: function (response) {
                        movies = response["all_movies"]
                        for(let i = 0; i < movies.length; i++){
                            let title = movies[i]["title"]
                            let image = movies[i]["image"]
                            let desc = movies[i]["desc"]
                            let url = movies[i]["url"]
                            let comment = movies[i]["comment"]

                            let temp_html = `<div class="card">
                                                <img class="card-img-top"
                                                     src="${image}"
                                                     alt="Card image cap">
                                                <div class="card-body">
                                                    <a target="_blank" href="${url}" class="card-title">${title}</a>
                                                    <p class="card-text">${desc}</p>
                                                    <p class="card-text comment">${comment}</p>
                                                </div>
                                            </div>`

                            $("#cards-box").append(temp_html)

                        }
                    }
                })
            }
        </script>

    </head>

    <body>
        <div class="wrap">
            <div class="jumbotron">
                <h1 class="display-4">나만의 영화 저장집</h1>
                <p class="lead">★봤던 영화들 중 인상 깊었던 영화 저장 공간★</p>
                <hr class="my-4">
                <p class="lead">
                    <button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기
                    </button>
                </p>
            </div>
            <div id="post-box" class="form-post" style="display:none">
                <div>
                    <div class="form-group">
                        <label for="post-url">URL</label>
                        <input id="post-url" class="form-control" placeholder="">
                    </div>
                    <div class="form-group">
                        <label for="post-comment">영화 코멘트</label>
                        <textarea id="post-comment" class="form-control" rows="2"></textarea>
                    </div>
                    <button type="button" class="btn btn-primary" onclick="postArticle()">영화 메모</button>
                </div>
            </div>
            <div id="cards-box" class="card-columns">
            </div>
        </div>
    </body>

</html>

댓글