웹개발 4주차

yurimLee·2023년 2월 17일
0

프론트 엔드와 백 엔드를 만들어 놓으면 웹 서비스를 만들 수 있다.
백엔드(Python)는 만들어둔 프론트 엔드(html, css, javascript)를 준다.

1. 서버 만들기

서버를 만든다고 하는 것은 사실 굉장히 어려운 일이다.
예를 들어, 떡볶이를 만들 때 고추장, 떡처럼 어느 정도 만들어져있는 재료를 가지고 요리한다.
이때 만들어져 있는 꾸러미들을 프레임워크라고 부른다.
아주 아주 쉽게 생각하면 라이브러리와 결이 같다고 생각해도 무방하다.
서버라는 큰 프로젝트를 만들기 위한 큰 라이브러리라고 생각하면 된다.
프레임워크는 라이브러리와 마찬가지로 메뉴얼대로 따라야 서버를 만들 수 있다.
우리가 쓸 프레임워크는 Flask이다.

1. 원하는 폴더로 이동

2. app.py를 만든다.

3. 터미널을 켠다.

4. 가상환경을 잡는다.

프로젝트별로 라이브러리를 담아두는 통

$ pyhon -m venv venv

5. 프레임워크를 설치한다.

가상환경 venv에 설치됨

$ pip install flask

6. app.py 웹서비스

app.py는 통상적으로 flask 프레임워크를 쓸 때 가장 기본이 되는 python 파일 이름이다.
flask 기본 코드

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
   return 'This is Home!'

@app.route('/mypage')
def mypage():
   return '<button>버튼입니다</button>' # html 결과가 나온다

if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)
  • 브라우저에서 localhost:5000을 입력하면 생성된 서버를 볼 수 있다. 이것은 내 컴퓨터에서 내가 만든 웹 서비스에 접속한 것이다. 즉, 내 컴퓨터가 인터넷 망에 접속해 있지만 아직 그 망에다가 오픈한 것은 아니다. 인터넷 서비스를 만들었지만 나한테만 만들어놓고 나만 들어가서 본 것이다. 여기서 몇 가지 세팅만 더 하면 This is Home!을 내 와이파이에 있는 모든 사람들이 들어와서 볼 수 있다.
  • localhost:5000 → 컴퓨터 주소 ex) 은행 지점, /mypage → ex) 입출금 창구
  • html을 결과로 반환할 수 있다. , , boostrap import하여 한 줄 길게 코딩하면 html을 반환할 수 있지만 힘들다. 프론트엔드가 어떻게 백엔드와 연결될 수 있는지 알아보자

2. HTML 파일 주기

app.py와 같은 경로 내에 'templates' 파일을 만들고 그 안에 index.html 파일을 생성한다.

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.5.1/jquery.min.js"></script>
    <title>Document</title>

    <script>
        function hey(){
            alert('안녕!')
        }
    </script>
</head>
<body>
    <h1>제목을 입력합니다</h1>
    <button onclick="hey()">나는 버튼!</button>
</body>
</html>

app.py

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def home():
   return 'This is Home!'

@app.route('/mypage')
def mypage():
   return render_template('index.html')
   
if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)

Flask라고 하는 파이썬 백엔드 서버가 돌면서 만들어둔 index.html 파일을 가져다 준다. localhost:5000/mypage로 접속하면 Flask 파이썬 백엔드가 돌다가 index.html을 준다. 그럼 브라우저는 html, css, javascript를 보고 그대로 프론트 엔드에 나타낸다. 브라우저가 아닌 앱을 사용하면 앱 개발이 된다.

Flask를 가지고 프론트엔드에서 백엔드로 데이터를 전송하고 데이터를 내려받는 작업을 할 수 있다. 예를 들면, 회원가입을 하기 위해 이름, 이메일, 전화번호를 전송하면 해당 데이터를 데이터베이스에 넣은 후 회원가입 성공을 알려준다. API를 어떻게 만들고 다룰 수 있는지 알아보자.

3. 본격 API 만들기

프론트엔드와 백엔드가 소통하는 방법에는 여러가지가 있다. Fetch를 통해 데이터를 받는 방식은 GET 방식이다. 이번엔 POST 방식을 알아보자.
데이터를 전송하는 방식에는 GET 방식과 POST 방식이 있다. 그렇다면 둘 중에 하나만 쓰면 될 것이라 생각할 수 있다. 하지만 약간의 장단점이 있다.

GET 방식

  • 데이터는 url의 ?뒤에 전달해주는 것이 특징이다. 브라우저에 데이터를 칠 수 있는 형태가 GET 방식이다.
  • 통상적으로 데이터를 조회(Read)를 요청할 때 사용한다. ※ 데이터를 건들이지 않고 그대로 가져올 떄 쓰인다.

POST 방식

  • 우리 눈에 보이지 않지만 컴퓨터들끼리 알아들을 수 있는 방식으로 데이터를 왔다갔다 할 수 있다.
  • 통상적으로 데이터를 조작할 때 사용한다. 데이터 생성(Create), 변경(Update), 삭제(Delete)가 있다. 예를 들면 회원가입, 비밀번호 수정이 있다.

GET 요청과 POST 요청 두 가지 방식에 대해 API가 어떻게 소통할까?

시작하기에 앞서, app.py는 백엔드, index.html은 프론트엔드라고 생각하면 된다. 프로트엔드는 실행시켜줘야 하고 백엔드는 저장만 해도 된다.
API를 만들고 사용하는 과정에서 두 가지가 필요하다. 바로 Flask의 request와 jsonify이다.

3. GET 방식, POST 방식 연습하기

GET 방식

  • 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.5.1/jquery.min.js"></script>
    <title>Document</title>

    <script>
        function hey() {
            fetch("/test").then(res => res.json()).then(data => { // '/test'라 하는 url에다 데이터를 보낸다. (fetch)
                console.log(data) // 2. 백엔드에서 받은 데이터를 콘솔 창에 출력한다.
            })
        }
    </script>
</head>

<body>
    <h1>제목을 입력합니다</h1>
    <button onclick="hey()">나는 버튼!</button>
</body>

</html>
  • app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
   return render_template('index.html')

@app.route('/test', methods=['GET']) # 1. '/test'라는 창구로 GET 요청이 들어온다
def test_get():
   title_receive = request.args.get('title_give') # 2. 그떄 'title_give'가 들어오면 해당 데이터를 title_receive 변수에 넣는다.
   print(title_receive) # 3. 터미널에 'None'이 찍힌다.
   return jsonify({'result':'success', 'msg': '이 요청은 GET!'}) # 4. 백엔드에서 프론트엔드로 데이터를 내려준다.

if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)
  • 실행 결과
  1. 프론트엔드에서 '/test'라 하는 url에다 요청을 하면 백엔드에서는 '/test'라는 창구로 GET 요청이 들어온다
  2. 백엔드에서는 요청한 데이터를 프론트엔드에 보낸다. jsonify()
  3. 프론트엔드는 요청한 데이터를 받아 콘솔 창에 출력한다. console.log()

이 방법은 API로 가지고 오던 데이터와 크게 다르지 않다.

POST 방식

  • 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.5.1/jquery.min.js"></script>
    <title>Document</title>

    <script>
        function hey() {
            // POST 방식
            let formData = new FormData(); // 1. 데이터를 실어 보내기 위한 formData 변수를 만든다.
            formData.append("title_give", "블랙팬서"); // 2. formData에 '블랙팬서' 데이터를 싣는다.
            fetch("/test", { method: "POST", body: formData }).then(res => res.json()).then(data => { // 3. '/test'라 하는 url에다 POST 방식으로 formData를 보낸다. (fetch)
                console.log(data) // 4. 백엔드에서 받은 데이터를 콘솔 창에 출력한다.
            })
        }
    </script>
</head>

<body>
    <h1>제목을 입력합니다</h1>
    <button onclick="hey()">나는 버튼!</button>
</body>

</html>
  • app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
   return render_template('index.html')

@app.route('/test', methods=['POST']) # 1. '/test'라는 창구로 POST 요청이 들어온다
def test_post():
   title_receive = request.form['title_give'] # 2. 그떄 'title_give'가 들어오면 해당 데이터를 title_receive 변수에 넣는다.
   print(title_receive) # 3. 터미널에 '블랙팬서'가 찍힌다.
   return jsonify({'result':'success', 'msg': '이 요청은 POST!'}) # 4. 백엔드에서 프론트엔드로 데이터를 내려준다.

if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)
  • 실행결과

서버 종료 ctrl + c

4. 화성땅 공동 구매

프로젝트 세팅

pip install flask
pip install pymongo
pip install dnspython

뼈대 준비하기

  • mongoDB Atlas
    Browse Collection > Collection 지워주기
  • index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }
        function save_order() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data);
                alert(data["msg"]);
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>
  • app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/mars", methods=["POST"])
def mars_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결 완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'msg':'GET 연결 완료!'})

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

POST 주문 데이터 저장하기

기본적으로 조회, 저장 기능이 있으면 저장 먼저 프로그래밍한다. 조회 먼저 하면 데이터가 없기 때문에 잘 조회된 건지 알 수 없기 때문이다.

  1. 데이터 명세: 어떤 데이터들을 서버로 넘길지 파악
  • 이름, 주소, 평수 입력
  1. 클라이언트(프론트엔드)와 서버(백엔드) 연결하기
  • 잘 작동하는 지 확인
    • 주문하기 버튼 클릭
    • 저장 완료 알림
  1. 서버 만들기
  • 서버 역할: 데이터를 입력 받아서 DB에 넣어준다. 클라이언트에서 요청하는 데이터를 DB에서 내려다 준다.
  • app.py
# API (클라이언트 서버 통신)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

# 망고DB 연결
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.mmgalfj.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/mars", methods=["POST"])
def mars_post():
    # 데이터 넘겨받음
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']

    # 넘겨받은 데이터 입력
    doc = {
        'name': name_receive,
        'address': address_receive,
        'size': size_receive
    }
    db.mars.insert_one(doc)
    
    # 클라이언트로 넘겨줌
    return jsonify({'msg':'저장 완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'result':'mars_data'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)
  1. 클라이언트 만들기
  • 클라이언트 역할: 이름에 대한 input 값, 주소에 대한 input값, 평수에 대한 input값에 대한 값을 가져온 후 변수에 담아서 서버에 넘겨준다.
  • index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                console.log(data)
      			alert(data["msg"]);
            })
        }
        function save_order() {
            // 데이터 불러오기
            let name = $('#name').val();
            let address = $('#address').val()
            let size = $('#size').val()

            // 데이터 넘겨주기
            let formData = new FormData();
            formData.append("name_give", name);
            formData.append("address_give", address);
            formData.append("size_give", size);

            // 데이터를 서버로 넘겨줌
            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data["msg"]);
                window.location.reload() // 페이지 refresh
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />  <!-- name 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />  <!-- address 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">  <!-- size 데이터 불러오기 포인트 아웃  -->
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>

GET 주문 데이터 조회하기

콘솔창에 출력

  • index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });

        // 데이터 조회
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                let rows = data['result']
                rows.forEach((a)=>{
                    console.log(a)
                })
            })
        }

        // 데이터 입력 및 저장
        function save_order() {
            // 데이터 불러오기
            let name = $('#name').val();
            let address = $('#address').val()
            let size = $('#size').val()

            // 데이터 넘겨주기
            let formData = new FormData();
            formData.append("name_give", name);
            formData.append("address_give", address);
            formData.append("size_give", size);

            // 데이터를 서버로 넘겨줌
            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data["msg"]);
                window.location.reload() // 페이지 refresh
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />  <!-- name 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />  <!-- address 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">  <!-- size 데이터 불러오기 포인트 아웃  -->
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>
  • app.py
# API (클라이언트 서버 통신)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

# 망고DB 연결
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.mmgalfj.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/mars", methods=["POST"])
def mars_post():
    # 데이터 넘겨받음
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']

    # 넘겨받은 데이터 입력
    doc = {
        'name': name_receive,
        'address': address_receive,
        'size': size_receive
    }
    db.mars.insert_one(doc)
    
    # 클라이언트로 넘겨줌
    return jsonify({'msg':'저장 완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    # 데이터 전체 조회
    mars_data = list(db.mars.find({}, {'_id': False}))
    
    return jsonify({'result' : mars_data})

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

프론트 엔드에 출력

  • index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });

        // 데이터 조회
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                let rows = data['result']
                $('#order-box').empty()

                rows.forEach((a)=>{
                    let name = a['name']
                    let address = a['address']
                    let size = a['size']

                    let temp_html = `<tr>
                                        <td>${name}</td>
                                        <td>${address}</td>
                                        <td>${size}</td>
                                    </tr>`

                    $('#order-box').append(temp_html)
                })
            })
        }

        // 데이터 입력 및 저장
        function save_order() {
            // 데이터 불러오기
            let name = $('#name').val();
            let address = $('#address').val()
            let size = $('#size').val()

            // 데이터 넘겨주기
            let formData = new FormData();
            formData.append("name_give", name);
            formData.append("address_give", address);
            formData.append("size_give", size);

            // 데이터를 서버로 넘겨줌
            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data["msg"]);
                window.location.reload() // 페이지 refresh
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />  <!-- name 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />  <!-- address 데이터 불러오기 포인트 아웃  -->
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">  <!-- size 데이터 불러오기 포인트 아웃  -->
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>

5. 스파르타 피디아

프로젝트 세팅

pip install flask pymongo dnspython requests bs4

조각 기능 구현해보기 (크롤링)

  • meta 태그

    • 눈에 보이는 것 이외의 웹의 속성을 설명하는 태그이다.
    • 공유하고 싶은 웹을 링크를 담아서 카톡에 공유했을 때 딸려오는 정보들이 meta 태그의 한 예시이다.

      섬네일 사진: og:image
      섬네일 제목: og:title
      섬네일 설명: og:description

  • 아래 사진처럼 안에 태그들이 많이 있는 것을 확인할 수 있다.

  • 제목, 설명, 사진 크롤링 하기

  • meta_prac.py

import requests
from bs4 import BeautifulSoup

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

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

ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content'] 
ogimage = soup.select_one('meta[property="og:image"]')['content'] 

print(ogtitle, ogdesc, ogimage)
보스 베이비 2 베이비 주식회사의 레전드 보스 베이비에서 인생 만렙 CEO가 된 ‘테드’.베이비인 줄 알았던 조카 ‘티... https://movie-phinf.pstatic.net/20210622_174/1624324910624JhEq2_JPEG/movie_image.jpg?type=m665_443_2

뼈대 준비하기

  • index.html
<!doctype html>
<html lang="en">

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

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <title>스파르타 피디아</title>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mytitle {
            width: 100%;
            height: 250px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mytitle>button {
            width: 200px;
            height: 50px;

            background-color: transparent;
            color: white;

            border-radius: 50px;
            border: 1px solid white;

            margin-top: 10px;
        }

        .mytitle>button:hover {
            border: 2px solid white;
        }

        .mycomment {
            color: gray;
        }

        .mycards {
            margin: 20px auto 0px auto;
            width: 95%;
            max-width: 1200px;
        }

        .mypost {
            width: 95%;
            max-width: 500px;
            margin: 20px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px gray;

            display: none;
        }

        .mybtns {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-top: 20px;
        }

        .mybtns>button {
            margin-right: 10px;
        }
    </style>
    <script>
        $(document).ready(function () {
            listing();
        });

        function listing() {
            fetch('/movie').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }

        function posting() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }

        function open_box() {
            $('#post-box').show()
        }
        function close_box() {
            $('#post-box').hide()
        }
    </script>
</head>

<body>
    <div class="mytitle">
        <h1>내 생애 최고의 영화들</h1>
        <button onclick="open_box()">영화 기록하기</button>
    </div>
    <div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>
        <div class="mybtns">
            <button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
            <button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
        </div>
    </div>
    <div class="mycards">
        <div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>
  • app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

# 1. API
@app.route("/movie", methods=["POST"])
def movie_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결 완료!'})

# 2. API
@app.route("/movie", methods=["GET"])
def movie_get():
    return jsonify({'msg':'GET 연결 완료!'})

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

POST - 영화 카드 포스팅하기

  • app.py
# 백엔드(서버)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

# 크롤링
import requests
from bs4 import BeautifulSoup

# 몽공DB
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.mmgalfj.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

@app.route('/')
def home():
    return render_template('index.html')

# 1. API: POST
@app.route("/movie", methods=["POST"])
def movie_post():
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']

    # 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_receive, headers=headers)

    soup = BeautifulSoup(data.text, 'html.parser')

    ogtitle = soup.select_one('meta[property="og:title"]')['content']
    ogdesc = soup.select_one('meta[property="og:description"]')['content'] 
    ogimage = soup.select_one('meta[property="og:image"]')['content']

    doc = {
        'title': ogtitle,
        'desc': ogdesc,
        'image': ogimage,
        'comment': comment_receive
    }
    db.movies.insert_one(doc)

    return jsonify({'msg':'저장 완료!'})

# 2. API: GET
@app.route("/movie", methods=["GET"])
def movie_get():
    return jsonify({'msg':'GET 연결 완료!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)
  • index.html
<!doctype html>
<html lang="en">

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

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <title>스파르타 피디아</title>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mytitle {
            width: 100%;
            height: 250px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mytitle>button {
            width: 200px;
            height: 50px;

            background-color: transparent;
            color: white;

            border-radius: 50px;
            border: 1px solid white;

            margin-top: 10px;
        }

        .mytitle>button:hover {
            border: 2px solid white;
        }

        .mycomment {
            color: gray;
        }

        .mycards {
            margin: 20px auto 0px auto;
            width: 95%;
            max-width: 1200px;
        }

        .mypost {
            width: 95%;
            max-width: 500px;
            margin: 20px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px gray;

            display: none;
        }

        .mybtns {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-top: 20px;
        }

        .mybtns>button {
            margin-right: 10px;
        }
    </style>
    <script>
        $(document).ready(function () {
            listing();
        });

        function listing() {
            fetch('/movie').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }

        function posting() {
            let url = $('#url').val()
            let comment = $('#comment').val()

            let formData = new FormData();
            formData.append("url_give", url);
            formData.append("comment_give", comment);

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data['msg'])
                window.location.reload()
            })
        }

        function open_box() {
            $('#post-box').show()
        }
        function close_box() {
            $('#post-box').hide()
        }
    </script>
</head>

<body>
    <div class="mytitle">
        <h1>내 생애 최고의 영화들</h1>
        <button onclick="open_box()">영화 기록하기</button>
    </div>
    <div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>
        <div class="mybtns">
            <button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
            <button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
        </div>
    </div>
    <div class="mycards">
        <div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

GET - 영화 카드 조회하기

  • index.html
<!doctype html>
<html lang="en">

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

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <title>스파르타 피디아</title>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mytitle {
            width: 100%;
            height: 250px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mytitle>button {
            width: 200px;
            height: 50px;

            background-color: transparent;
            color: white;

            border-radius: 50px;
            border: 1px solid white;

            margin-top: 10px;
        }

        .mytitle>button:hover {
            border: 2px solid white;
        }

        .mycomment {
            color: gray;
        }

        .mycards {
            margin: 20px auto 0px auto;
            width: 95%;
            max-width: 1200px;
        }

        .mypost {
            width: 95%;
            max-width: 500px;
            margin: 20px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px gray;

            display: none;
        }

        .mybtns {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-top: 20px;
        }

        .mybtns>button {
            margin-right: 10px;
        }
    </style>
    <script>
        $(document).ready(function () {
            listing();
        });

        function listing() {
            fetch('/movie').then((res) => res.json()).then((data) => {
                let rows = data['result']
                $('#cards-box').empty()
                rows.forEach((a)=>{
                    let comment = a['comment']
                    let title = a['title']
                    let desc = a['desc']
                    let image = a['image']

                    let tmep_html = `<div class="col">
                                        <div class="card h-100">
                                            <img src="${image}"
                                                class="card-img-top">
                                            <div class="card-body">
                                                <h5 class="card-title">${title}</h5>
                                                <p class="card-text">${desc}</p>
                                                <p>⭐⭐⭐</p>
                                                <p class="mycomment">${title}</p>
                                            </div>
                                        </div>
                                    </div>`
                    $('#cards-box').append(tmep_html)
                })
            })
        }

        function posting() {
            let url = $('#url').val()
            let comment = $('#comment').val()

            let formData = new FormData();
            formData.append("url_give", url);
            formData.append("comment_give", comment);

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data['msg'])
                window.location.reload()
            })
        }

        function open_box() {
            $('#post-box').show()
        }
        function close_box() {
            $('#post-box').hide()
        }
    </script>
</head>

<body>
    <div class="mytitle">
        <h1>내 생애 최고의 영화들</h1>
        <button onclick="open_box()">영화 기록하기</button>
    </div>
    <div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>
        <div class="mybtns">
            <button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
            <button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
        </div>
    </div>
    <div class="mycards">
        <div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>
  • app.py
# 백엔드(서버)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

# 크롤링
import requests
from bs4 import BeautifulSoup

# 몽공DB
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.mmgalfj.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

@app.route('/')
def home():
    return render_template('index.html')

# 1. API: POST
@app.route("/movie", methods=["POST"])
def movie_post():
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']

    # 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_receive, headers=headers)

    soup = BeautifulSoup(data.text, 'html.parser')

    ogtitle = soup.select_one('meta[property="og:title"]')['content']
    ogdesc = soup.select_one('meta[property="og:description"]')['content'] 
    ogimage = soup.select_one('meta[property="og:image"]')['content']

    doc = {
        'title': ogtitle,
        'desc': ogdesc,
        'image': ogimage,
        'comment': comment_receive
    }
    db.movies.insert_one(doc)

    return jsonify({'msg':'저장 완료!'})

# 2. API: GET
@app.route("/movie", methods=["GET"])
def movie_get():
    all_movies = list(db.movies.find({}, {'_id': False}))
    return jsonify({'result':all_movies})

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

0개의 댓글