1. 학습내용

✔Flask 서버를 만들 때,
Flask 서버를 만들 때, 항상,
프로젝트 폴더 안에,
ㄴstatic 폴더 (이미지, css파일을 넣어둡니다)
ㄴtemplates 폴더 안에 index.html 파일 만들기
ㄴapp.py 파일
세팅(설정)메뉴에 들어가서 python interpretor 가서 + 누른 후, flask 설치

✔[Flask 기초: HTML 파일 불러오기]
1.간단한 index.html 파일을 templates 안에 만들기

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    하나만 입력해 봅시다.
</body>
</html>
  1. app.py에 html 파일 불러오기
from flask import Flask, render_template
app = Flask(__name__)                                                 
                                 ## URL 별로 함수명이 같거나,
@app.route('/')                  ## route('/') 등의 주소가 같으면 안됩니다.
def home():
   return render_template('index.html')

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

***Run 실행 후,
Client가 http://localhost:5000/ 접속해서 요청하면, 하나만 입력해 봅시다. 등장!

즉, Client가 요청한 index.html 파일을 받아와서 브라우저(서버)가 그려준다.

✔[Flask시작하기 - 본격 API 만들기]

***들어가기에 앞서서, Get 과 Post 차이

  • GET → 통상적으로! 데이터 조회(Read)를 요청할 때
    예) 영화 목록 조회
    → 데이터 전달 : URL 뒤에 물음표를 붙여 key=value로 전달
    → 예: google.com?q=북극곰
  • POST → 통상적으로! 데이터 생성(Create), 변경(Update), 삭제(Delete) 요청 할 때
    예) 회원가입, 회원탈퇴, 비밀번호 수정
    → 데이터 전달 : 바로 보이지 않는 HTML body에 key:value 형태로 전달

<1. 예제코드>
(1)index.html 파일에 Ajax코드를 넣는다.

<!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.4.1/jquery.min.js"></script>
    <title>Document</title>
</head>
<body>
    <h1>서버를 만들었다!</h1>
</body>
</html>

Run 실행 후, http://localhost:5000/ 접속 후, 서버를 만들었다! 확인!!!!

(2) app.py에 GET 요청 API 코드를 집어넣는다.

  • [GET 요청 API 코드]
    @app.route('/test', methods=['GET'])
    def test_get():
       title_receive = request.args.get('title_give')
       print(title_receive)
       return jsonify({'result':'success', 'msg': '이 요청은 GET!'})
request, jsonify를 덧붙여준다.

```## 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'])
def test_get():
   title_receive = request.args.get('title_give')
   print(title_receive)
   return jsonify({'result':'success', 'msg': '이 요청은 GET!'})

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

서버에서 받은 html 파일을 http://localhost:5000/ 접속하게 된 상태에서, 콘솔창을 열어본다.

[GET 요청 Ajax 확인 코드]

$.ajax({
    type: "GET",
    url: "/test?title_give=봄날은간다",
    data: {},
    success: function(response){
       console.log(response)
    }
  })

를 넣은 후, Enter 치면,
{msg: "이 요청은 GET!", result: "success"} 이 나올 것이다. 이것은 console.log를 했기 때문에 나타난 것이며, response는 서버에서 내려준 값이다.
즉, 서버 코드 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'])              ---타입은 Get, url은 /test
def test_get():
   title_receive = request.args.get('title_give')  ---title_give로 가져온 값 가져와봐. 즉 '봄날은 간다'일 것이다.
   print(title_receive)                            --- Run 실행 후, 결과가 '봄날은 간다' 나올 것이다. 
   return jsonify({'result':'success', 'msg': '이 요청은 GET!'})  --- 서버에서 내려주는 값대로 콘솔창에서도 결과값이 나올 것이다.

if __name__ == '__main__':
   app.run('0.0.0.0',port=5000,debug=True)
  • [Post 요청 API 코드]
    @app.route('/test', methods=['POST'])
    def test_post():
       title_receive = request.form['title_give']
       print(title_receive)
       return jsonify({'result':'success', 'msg': '이 요청은 POST!'})

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'])
def test_get():
title_receive = request.args.get('title_give')
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 GET!'})

@app.route('/test', methods=['POST'])
def test_post():
title_receive = request.form['title_give'] --- GET 요청과 같은 말이지만, 형식은 다름.
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 POST!'})

if name == 'main':
app.run('0.0.0.0',port=5000,debug=True)

콘솔창에, 
*** [Post 요청 확인 Ajax 코드] ***
> ```
$.ajax({
    type: "POST",
    url: "/test",
    data: { title_give:'봄날은간다' },   --- Post엔 data가 있다. 반드시, 서버쪽(app.py)과 여기에 적혀져 있는 title_give와 상응해야한다! 
    success: function(response){
       console.log(response)
    }
  })

[예제 문제(1)]

✔[모두의책리뷰] - 프로젝트 세팅
flask 서버 만들 때 의 방법과 똑같다. 그리고 '리뷰' 이기 때문에 pymongo도 설치해준다.
✔[모두의책리뷰] - 뼈대 준비하기
<app.py>

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

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

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

## API 역할을 하는 부분
@app.route('/review', methods=['POST'])
def write_review():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': '이 요청은 POST!'})


@app.route('/review', methods=['GET'])
def read_reviews():
    sample_receive = request.args.get('sample_give')
    print(sample_receive)
    return jsonify({'msg': '이 요청은 GET!'})


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

<index.html>

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

    <head>
        <!-- Webpage Title -->
        <title>모두의 책리뷰 | 스파르타코딩클럽</title>

        <!-- 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=Do+Hyeon&display=swap" rel="stylesheet">

        <script type="text/javascript">

            $(document).ready(function () {
                showReview();
            });

            function makeReview() {
                $.ajax({
                    type: "POST",
                    url: "/review",
                    data: {sample_give:'샘플데이터'},
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                })
            }

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

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

            h1,
            h5 {
                display: inline;
            }

            .info {
                margin-top: 20px;
                margin-bottom: 20px;
            }

            .review {
                text-align: center;
            }

            .reviews {
                margin-top: 100px;
            }
        </style>
    </head>

    <body>
        <div class="container">
            <img src="https://previews.123rf.com/images/maxxyustas/maxxyustas1511/maxxyustas151100002/47858355-education-concept-books-and-textbooks-on-the-bookshelf-3d.jpg"
                 class="img-fluid" alt="Responsive image">
            <div class="info">
                <h1>읽은 책에 대해 말씀해주세요.</h1>
                <p>다른 사람을 위해 리뷰를 남겨주세요! 다 같이 좋은 책을 읽는다면 다 함께 행복해질 수 있지 않을까요?</p>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">제목</span>
                    </div>
                    <input type="text" class="form-control" id="title">
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">저자</span>
                    </div>
                    <input type="text" class="form-control" id="author">
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">리뷰</span>
                    </div>
                    <textarea class="form-control" id="bookReview"
                              cols="30"
                              rows="5" placeholder="140자까지 입력할 수 있습니다."></textarea>
                </div>
                <div class="review">
                    <button onclick="makeReview()" type="button" class="btn btn-primary">리뷰 작성하기</button>
                </div>
            </div>
            <div class="reviews">
                <table class="table">
                    <thead>
                    <tr>
                        <th scope="col">제목</th>
                        <th scope="col">저자</th>
                        <th scope="col">리뷰</th>
                    </tr>
                    </thead>
                    <tbody id="reviews-box">
                    <tr>
                        <td>왕초보 8주 코딩</td>
                        <td>김르탄</td>
                        <td>역시 왕초보 코딩교육의 명가답군요. 따라하다보니 눈 깜짝할 사이에 8주가 지났습니다.</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </body>

app.py에서 Run 실행 후, http://localhost:5000/ 접속 후, 페이지 Come!

✔ [모두의책리뷰] - POST 연습(리뷰 저장)
1. 클라이언트와 서버 확인하기
<서버 코드>app.py

## API 역할을 하는 부분
@app.route(**'/review'**, methods=[**'POST'**])
def write_review():
	# 1. 클라이언트가 준 title, author, review 가져오기.
	# 2. DB에 정보 삽입하기
	# 3. 성공 여부 & 성공 메시지 반환하기
    return jsonify({**'result'**: 'success', **'msg'**: '리뷰가 성공적으로 작성되었습니다.'})

<클라이언트 코드>index.html

function makeReview() {
		// 1. 제목, 저자, 리뷰 내용을 가져옵니다.
		// 2. 제목, 저자, 리뷰 중 하나라도 입력하지 않았을 경우 alert를 띄웁니다.
		// 3. POST /review 에 저장을 요청합니다.
    $.ajax({
        type: **"POST"**,
        url:  **"/review"**,
        data: { },
        success: function (response) {
            if (response[**"result"**] == "success") {
                alert(response[**"msg"**] );
                window.location.reload();
            }
        }
    })
}
  1. 서버부터 만들기

    서버가 하는 일
    \1. 클라이언트가 준 title, author, review 가져오기.
    \2. DB에 정보 삽입하기
    \3. 성공 여부 & 성공 메시지 반환하기

## API 역할을 하는 부분
@app.route('/review', methods=['POST'])
def write_review():
    title_receive = request.form['title_give']  ---title_receive로 클라이언트가 준 title 가져오기
    author_receive = request.form['author_give']  ---author_receive로 클라이언트가 준 author 가져오기
    review_receive = request.form['review_give']  ---review_receive로 클라이언트가 준 review 가져오기

    doc = {
        'title':title_receive,
        'author':author_receive,
        'review':review_receive          --- DB에 삽입할 review 만들기
    }
    db.bookreview.insert_one(doc)        ---reviews에 review 저장하기
    
    return jsonify({'msg': '저장 완료!'})  ---성공 여부 & 성공 메시지 반환
  1. 클라이언트 만들기

    클라이언트가 하는 일
    \1. input에서 title, author, review 가져오기
    \2. 입력값이 하나라도 없을 때 alert 띄우기.
    \3. Ajax로 서버에 저장 요청하고, 화면 다시 로딩하기

<input type="text" class="form-control" id="title"> 
<input type="text" class="form-control" id="author">
<textarea class="form-control" id="bookReview"

---각각 해당하는 input박스 값 가져오기

function makeReview() {
                let title = $('#title').val()
                let author = $('#author').val()
                let review = $('#bookReview').val()  ---// 화면에 입력어 있는 제목, 저자, 리뷰 내용을 가져옵니다.(Jquery를 이용)


                $.ajax({
                    type: "POST",
                    url: "/review",
                    data: {title_give:title,author_give:author,review_give:review},   ---POST /review 에 저장(Create)을 요청합니다.
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                })
            }
  1. 확인하기
    제목, 저자, 리뷰를 작성하고 '리뷰 작성하기' 버튼을 눌렀을 때,
    '리뷰가 성공적으로 작성되었습니다.'라는 alert가 뜨는지 확인합니다.
    그리고 Robot3T를 실행하여, Collection > bookreview 더블 클릭한다!

✔[모두의책리뷰] - GET 연습(리뷰 보여주기)
1. 클라이언트와 서버 확인하기
<서버 코드>app.py

@app.route(**'/review'**, methods=[**'GET'**])
def read_reviews():
    sample_receive = request.args.get('sample_give')
    print(sample_receive)
    return jsonify({**'msg'**: '이 요청은 GET!'})

<클라이언트 코드>index.html

function showReview() {
		// 서버의 데이터를 받아오기
		$.ajax({
        type: **"GET"**,
        url: "**/review**?sample_give=샘플데이터",
        data: {},
        success: function (response) {
            alert(response[**"msg"**]);
        }
    })
}
  1. 서버부터 만들기

    서버가 하는 일
    \1. DB에서 리뷰 정보 모두 가져오기
    \2. 성공 여부 & 리뷰 목록 반환하기

@app.route('/review', methods=['GET'])
def read_reviews():
    reviews = list(db.bookreview.find({}, {'_id': False}))  ---DB에서 리뷰 정보 모두 가져오기
    return jsonify({'all_reviews': reviews})  ---성공 여부 & 리뷰 목록 반환하기
  1. 클라이언트 만들기

    클라이언트가 하는 일
    \1. 리뷰 목록을 서버에 요청하기
    \2. 요청 성공 여부 확인하기
    \3. 요청 성공했을 때 리뷰를 올바르게 화면에 나타내기

function showReview() {
                $.ajax({
                    type: "GET",
                    url: "/review",
                    data: {},
                    success: function (response) {
                        let reviews = response['all_reviews']
                        console.log(reviews)
                    }
                })
            }

콘솔창에서 잘 돌아가는지 확인하면서 딕셔너리형태로 나오기 때문에 for 문을 돌아야한다.

function showReview() {
                $.ajax({
                    type: "GET",
                    url: "/review",
                    data: {},
                    success: function (response) {
                        let reviews = response['all_reviews']
                        for (let i = 0; i < reviews.length; i++) {
                            let title = reviews[i]['title']
                            let author = reviews[i]['author']
                            let review = reviews[i]['review']
                            console.log(title,author,review)
                        }
                    }
                })
            }

콘솔창에서 잘 돌아가는지 확인
찍어져야 할 게 아니라, 붙여줘야 한다. temp_html

<tr>
     <td>왕초보 8주 코딩</td>
     <td>김르탄</td>
     <td>역시 왕초보 코딩교육의 명가답군요. 따라하다보니 눈 깜짝할 사이에 8주가 지났습니다.</td>
</tr>

하단에서 붙여줘야 할 부분을 가져온다. ctrl X + Ctrl V

function showReview() {
                $.ajax({
                    type: "GET",
                    url: "/review",
                    data: {},
                    success: function (response) {
                        let reviews = response['all_reviews']
                        for (let i = 0; i < reviews.length; i++) {
                            let title = reviews[i]['title']
                            let author = reviews[i]['author']
                            let review = reviews[i]['review']

                            let temp_html = `<tr>/n' +
                                '       <td>${title}</td>/n'
                                '       <td>${author}</td>/n'
                                '       <td>${review}</td>/n'
                                '        </tr>'`
                            $('#reviews-box').append(temp_html)

                        }
                    }
                })
            }

하단에서 html 부분 중 review-box에 하단(해당하는 부분) 지워도 된다!

  1. 확인하기
    화면을 새로고침 했을 때, DB에 저장된 리뷰가 화면에 올바르게 나타나는지 확인합니다

[예제 문제(2)]

✔ [나홀로메모장] - 프로젝트 세팅
flask, pymongo, requests, bs4 패키지 설치
✔ [나홀로메모장] - API 설계하기

✔ [나홀로메모장] - 조각 기능 구현해보기
URL 에서 페이지 정보 가져오기(meta태그 스크래핑)
meta태그란?

<head></head> 부분에 들어가는, 눈으로 보이는 것(body) 외에 사이트의 속성을 설명해주는 태그들입니다.
예) 구글 검색 시 표시 될 설명문, 사이트 제목, 카톡 공유 시 표시 될 이미지 등


-meta_prac.py를 만들어 [크롤링 기본 코드]

import requests
from bs4 import BeautifulSoup

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

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

# 여기에 코딩을 해서 meta tag를 먼저 가져와보겠습니다.

그린북 페이지에서 검사 누르고
에서 copy selector해서 붙여쓰면 실행이 안될 것이다.
그럴 땐, meta['']안에 property속성을 붙인다.

import requests
from bs4 import BeautifulSoup

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

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

title = soup.select_one('meta[property="og:title"]')['content']  
image = soup.select_one('meta[property="og:image"]')['content']
desc = soup.select_one('meta[property="og:description"]')['content']
print(title,image,desc)

✔ [나홀로메모장] - 뼈대 준비하기
<app.py>

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.dbsparta

## 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>

<!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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                         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>

✔ [나홀로메모장] - POST 연습(메모하기)
#API 만들고 사용하기 - 포스팅API (Create → POST)
1) 포스팅API - 카드 생성 (Create) : 클라이언트에서 받은 url, comment를 이용해서 페이지 정보를 찾고 저장하기
1. 클라이언트와 서버 연결 확인하기
<서버 코드> app.py

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

<클라이언트 코드>index.html

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

<button type="button" class="btn btn-primary" onclick="postArticle()">기사저장</button>

'기사저장' 버튼을 클릭했을 때, 'POST 연결되었습니다!' alert창이 뜨면
클라이언트 코드와 서버 코드가 연결 되어있는 것입니다.

  1. 서버부터 만들기

    서버가 하는 일
    \1. 클라이언트로부터 데이터를 받기.(url_give,comment_give)
    \2. meta tag를 스크래핑하기
    \3. mongoDB에 데이터를 넣기

## API 역할을 하는 부분
@app.route('/memo', methods=['POST'])
def saving():
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']   ---(\1)

	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) ---url receive추가

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

    title = soup.select_one('meta[property="og:title"]')['content']
    image = soup.select_one('meta[property="og:image"]')['content']
    desc = soup.select_one('meta[property="og:description"]')['content']     --- (\2)

    doc = {
        'title':title,
        'image':image,
        'desc':desc,
        'url':url_receive,
        'comment':comment_receive
    }
    db.articles.insert_one(doc)          ---(\3)

    return jsonify({'msg':'저장이 완료되었습니다!'})
  1. 클라이언트 만들기

    클라이언트가 하는 일
    \1. 유저가 입력한 데이터를 #post-url과 #post-comment에서 가져오기
    \2. /memo에 POST 방식으로 메모 생성 요청하기
    \3. 성공 시 페이지 새로고침하기

<input id="post-url" class="form-control" placeholder="">
<textarea id="post-comment" class="form-control" rows="2"></textarea>
function postArticle() {
            let url = $('#post-url').val()
            let comment = $('#post-comment').val()

            $.ajax({
                  type: "POST",
                  url: "/memo",
                  data: {url_give:url, comment_give:comment},  ---(\1)
                  success: function (response) { // 성공하면
                      alert(response["msg"]);
                      window.location.reload()
                  }
              })
          }
  1. 완성 확인하기
    https://movie.naver.com/movie/bi/mi/basic.nhn?code=171539 ← 이 URL을 입력하고 기사저장을 눌렀을 때, '포스팅 성공!' alert창이 뜨는지 확인합니다.
    (우리는 스크래핑을 사용해 정보를 저장하고 있으니까, meta tag 가 있는 사이트만 저장이 제대로 되겠죠?)
    그리고 Robot3T를 실행하여, Collection > articles 더블 클릭한다!
    참고!
    지금은 카드가 보이지 않습니다. 아직 카드를 보여주는 리스팅 API 를 만들지 않았기 때문이죠.

✔ [나홀로메모장] - GET 연습(보여주기)
#API 만들고 사용하기 - 리스팅 API (Read → GET)
(2) 리스팅API - 저장된 카드 보여주기 (Read)

1. 클라이언트와 서버 연결 확인하기
<서버 코드>app.py

def read_articles():
    # 1. 모든 document 찾기 & _id 값은 출력에서 제외하기
    # 2. articles라는 키 값으로 영화정보 내려주기
    return jsonify({'result':'success', 'msg':'GET 연결되었습니다!'})
<클라이언트 코드>index.html
function showArticles() {
  $.ajax({
    type: "GET",
    url: "/memo",
    data: {},
    success: function (response) {
      if (response["result"] == "success") {
        alert(response["msg"]);
      }
    }
  })
}

새로고침했을 때, 'GET 연결되었습니다!' alert창이 뜨면
클라이언트 코드와 서버 코드가 연결 되어있는 것입니다.

  1. 서버부터 만들기

    서버가 하는 일
    \1. mongoDB에서 _id 값을 제외한 모든 데이터 조회해오기 (Read)
    \2. articles라는 키 값으로 articles 정보 보내주기

@app.route('/memo', methods=['GET'])
def listing():
    articles = list(db.articles.find({}, {'_id': False}))  --(\1)
    return jsonify({'all_articles': articles})             --(\2)
  1. 클라이언트 만들기

    클라이언트가 하는 일
    \1. /memo에 GET 방식으로 메모 정보 요청하고 articles로 메모 정보 받기
    \2. , makeCard 함수를 이용해서 카드 HTML 붙이기
    (→ 2주차 Ajax 연습과 같습니다!)

function showArticles() {
                $.ajax({
                    type: "GET",
                    url: "/memo?",
                    data: {},
                    success: function (response) {
                        let articles = response['all_articles']
                        console.log(articles)
                    }
                })
            }

콘솔창에서 잘 돌아가는지 확인하면서 딕셔너리형태로 나오기 때문에 for 문을 돌아야한다.

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

                            console.log(title,image,url,desc,comment)
                            }
                    }
                })
            }

콘솔창에서 잘 돌아가는지 확인
찍어져야 할 게 아니라, 붙여줘야 한다. temp_html

	<div class="card">
         <img class="card-img-top"
              src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
 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>

하단에서 붙여줘야 할 부분을 가져온다.

function showArticles() {
                $.ajax({
                    type: "GET",
                    url: "/memo?",
                    data: {},
                    success: function (response) {
                        let articles = response['all_articles']
                        for (let i = 0; i < articles.length; i ++) {
                            let title = articles[i]['title']
                            let image = articles[i]['image']
                            let url = articles[i]['url']
                            let desc = articles[i]['desc']
                            let comment = articles[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)
                            }
                    }
                })
            }

그리고 하단에서 html 부분 중 card-columns에 하단(해당하는 부분) 지워도 된다!

  1. 완성 확인하기
    새로고침했을 때, 앞 포스팅 API를 만들고 테스트했던 메모가 보이면 성공입니다.

참고!
card가 정렬되는 순서는 위에서 아래로 채워지고, 왼쪽부터 오른쪽으로 순서대로 채워집니다. 부트스트랩 컴퍼넌트 페이지에 적혀있어요. "Cards are ordered from top to bottom and left to right." (컴퍼넌트 페이지 링크)

4주차 과제

힌트 : <모두의책리뷰>랑 아주아주 유사하답니다!
아래 주문정보를 붙일 때에는, 부트스트랩 Table(링크)을 이용하세요!

<서버 코드>app.py

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)

from pymongo import MongoClient

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


## HTML 화면 보여주기
@app.route('/')
def homework():
    return render_template('index.html')


# 주문하기(POST) API
@app.route('/order', methods=['POST'])
def save_order():
    name_receive = request.form['name_give']
    count_receive = request.form['count_give']
    address_receive = request.form['address_give']
    phone_receive = request.form['phone_give']

    doc = {
        'name': name_receive,
        'count': count_receive,
        'address': address_receive,
        'phone': phone_receive
    }
    db.orders.insert_one(doc)

    return jsonify({'result': 'success', 'msg': '주문 완료!'})


# 주문 목록보기(Read) API
@app.route('/order', methods=['GET'])
def view_orders():
    orders = list(db.orders.find({}, {'_id': False}))
    return jsonify({'result': 'success', 'orders': orders})


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

<클라이언트 코드>index.html

<!doctype html>
<html lang="en">

<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">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <title>스파르타코딩클럽 | 부트스트랩 연습하기</title>

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

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

        .item-img {
            width: 500px;
            height: 300px;

            background-image: url("https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMTAyMDNfMjYz%2FMDAxNjEyMzM2NDAzOTk2.52e32yWdGvP-K-YFPqN4zUyQAB04LnSL2t_d0N5xWYcg.buuAsWLurzvigKPdpY7BC4OyCC5Hmhh6DNuoeGfe_Dog.JPEG.vortexrho98%2F%25C1%25FC%25BA%25BC7.jpg&type=sc960_832");
            background-position: center;
            background-size: cover;
        }

        .price {
            font-size: 20px;
        }

        .item-desc {
            width: 500px;
            margin-top: 20px;
            margin-bottom: 20px;
        }

        .item-order {
            width: 500px;
            margin-bottom: 50px;
        }

        .btn-order {
            margin: auto;
            width: 100px;
            display: block;
        }

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

        .rate {
            color: blue;
        }
    </style>

    <script>
        $(document).ready(function () {
            get_rate();
            listing();
        });

        function listing() {
            $.ajax({
                type: "GET",
                url: "/order",
                data: {},
                success: function (response) {
                    if (response["result"] == "success") {
                        let orders = response['orders'];
                        for (let i = 0; i < orders.length; i++) {
                            let name = orders[i]['name']
                            let count = orders[i]['count']
                            let address = orders[i]['address']
                            let phone = orders[i]['phone']

                            let temp_html = `<tr>
                                                <th scope="row">${name}</th>
                                                <td>${count}</td>
                                                <td>${address}</td>
                                                <td>${phone}</td>
                                              </tr>`
                            $('#orders-box').append(temp_html)
                        }
                    }
                }
            })
        }

        function get_rate() {
            $.ajax({
                type: "GET",
                url: "https://api.manana.kr/exchange/rate.json",
                data: {},
                success: function (response) {
                    let now_rate = response[1]['rate'];
                    $('#now-rate').text(now_rate);
                }
            })
        }

        function order() {
            let name = $('#order-name').val();
            let count = $('#order-count').val();
            let address = $('#order-address').val();
            let phone = $('#order-phone').val();

            $.ajax({
                type: "POST",
                url: "/order",
                data: {name_give: name, count_give: count, address_give: address, phone_give: phone},
                success: function (response) {
                    if (response["result"] == "success") {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                }
            })
        }
    </script>
</head>

<body>
<div class="wrap">
    <div class="item-img"></div>
    <div class="item-desc">
        <h1>짐볼을 팝니다 <span class="price">가격:17,900원/개</span></h1>
        <p>스트라이프 타입, 마사지 타입, 멀티 타입 3가지 타입으로 다양한 운동이 가능합니다.</p>
        <p class="rate">달러-원 환율: <span id="now-rate">1219.15</span></p>
    </div>
    <div class="item-order">
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text">주문자이름</span>
            </div>
            <input id="order-name" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <label class="input-group-text" for="inputGroupSelect01">수량</label>
            </div>
            <select id="order-count" class="custom-select">
                <option selected>-- 수량을 선택하세요 --</option>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
            </select>
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text">주소</span>
            </div>
            <input id="order-address" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text">전화번호</span>
            </div>
            <input id="order-phone" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <button type="button" onclick="order()" class="btn btn-primary btn-order">주문하기</button>
    </div>
    <table class="table">
        <thead>
        <tr>
            <th scope="col">이름</th>
            <th scope="col">수량</th>
            <th scope="col">주소</th>
            <th scope="col">전화번호</th>
        </tr>
        </thead>
        <tbody id="orders-box">
    </table>
</div>
</body>

</html>

2. 학습소감

이 4주차 과정이 이때까지 배웠던 거를 다 종합해서 적어내다 보니깐 막 혼란스럽다... 뭔가 한번 더 정리복습을 해줘야 할 것같다.
그리고 4주차 과제를 하는데, 클라이언트와 서버 연결 확인이 잘 안되는데, ????

다시... 오류를 잘 확인하여 다시 과제 마침. 코드 짜는 것도 여러가지가 들어가있고 그렇고 오류를 잘 확인해야되겠다. 코드모양에 익숙해지기로.....

profile
신입개발자가 되기 위한 노력 프로젝트

0개의 댓글