파이썬 가상환경 생성하기

파이참에서 사용할 폴더를 오픈한다.

 

이렇게 폴더가 딱 하나 있다.

파이참에서, FILE - SETTING -PROJECT INTERPRETER

여기서 다음과 같이 톱니바퀴 - ADD 를 누르면,

 

아래와 같은 화면이 나온다. NEW ENVIRONMENT 가 체크되어야 하고, 경로는 내가 만든 경로인 BOOKREVIEW의 venv 가 되어야 한다. venv가 가상으로  환경정보가 저장되는 곳이다.

즉 bookreview 프로젝트의 가상환경정보가 된다.

 

모두 ok 하면 완료

 

파이썬 패키지, 프레임워크, 라이브러리

패키지보다 큰 게 라이브러리, 라이브러리보다 큰 게 프레임워크(라이브러리+여러개발도구포함) (정확하진 않지만, 대충 이렇게 이해하면 됨)

 

프레임워크는 남이 만들어놓은 틀 안에서 그대로 움직이기 때문에 편함 그래서 디렉토리구조도 만들라는대로만들어야함. 라이브러리는 내맘대로 짜는데 남이 만들어놓은걸 군데군데 가져오는것.

 

 

 

 

API 작동 리뷰

api 작동

1. 클라이언트가, 즉 내 chrome 창에서

2. 요청을 하면, 즉 api의 주소를 입력하고 엔터치면 https://api.thecatapi.com/v1/images/search

3. API의기능, 즉 정해진 동작인 랜덤 고양이 사진 데이터 조회를 실행하고

4. 응답, 즉 고양이 사진 데이터를 클라이언트에 응답한다. JSON으로 응답한다. 이게 무조건 JSON으로 응답하는건 아닐것임. 

 

API 사용 약속

1. 요청정보 : 요청URL, 요청방식(GET, POST 등)

2. 서버가 제공할 기능 : 데이터 조회, 데이터 생성 등

3. 응답할 데이터 형식 : 어떤 KEY로 어떤 데이터를 줄지 등 

- 이런 약속을 모두 API 문서에 적어두는 것이다.

 

 

플라스크(flask)

- 이번에 서버(백엔드) API를 만드는데 사용한다.

- 프레임워크이다. 서버를 만들어주는 프레임워크.

- 웹을 만들고 서버를 구동시키기 편하게 하는 프레임워크, 개발도구이다.

플라스크 공식문서 : https://flask.palletsprojects.com/en/1.1.x/#

플라스크 비공식 한글 번역문서 : https://flask-docs-kr.readthedocs.io/ko/latest/foreword.html

 

 

플라스크를 쓸려면 아래 3가지의 디렉토리 구조를 만들어야 한다.

 

1. app.py 파이썬파일이 하나 필요. 실행의 주체가 됨. 보통 app.py라고 한다.   app.py 안해도 되는데 가급적 그렇게 해라.

2. static 폴더 : 이미지, css 파일 넣음

3. templates 폴더 : html 파일 넣음.  템플릿츠임. 에스빠지면안된다.

 

플라스크에서 위 디렉토리와 파일을 쓸거라고 되어있어서 이건 그냥 약속임. 그대로 해야 함.

이런식.

 

 

예시코드

from flask import Flask



app = Flask(__name__)



@app.route('/')

def hello_world():

    return '<button>나는 버튼이다</button>'



if __name__ == '__main__':

    app.run('0.0.0.0', port=5000, debug=True)

 

코드 실행하면, 이런거 뜨는데 ok 하면 됨

그리고 상태 메시지에서 running on  뜨면 잘 된 것.

이게 서버를 하나 만든것이다.

 

 

이렇게 확인할 수 있다. 웹브라우저에서.

localhost:5000 / 0.0.0.0:5000 (방화벽때문에 잘 안될 수 있음)

 

 

## 플라스크로부터 플라스크 패키지만 임포트함.

from flask import Flask



## 약속이다. 플라스크 개체를 만든 것임. 무조건 넣으면 됨.

app = Flask(__name__)



## 해당 주소의 딱 /까지, 즉 www.naver.com/ 까지, 위 예시에서는 localhost:5000/ 까지 하면, 아래가 실행된다.

## 아래에서 정한 주소 뒤에 / 면 아래를 실행해라! 이런느낌.   그리고 리턴을 하는데, html을 리턴할 수 있음!

@app.route('/')

def hello_world():

    return '<button>나는 버튼이다</button>'





## 이부분이 메인이다. 여기서 실행이 시작임. 이것도 고정된 부분임. app.run 안에 있는 내용이 바뀔 수 있음

## 이게 서버를 만든 것임.

if __name__ == '__main__':

    app.run('0.0.0.0', port=5000, debug=True)
      
# 이 코드를 만나면, 서버가 계속 돈다. 다시말해서 app.route를 여기 if __name 밑으로 가면, 적용안됨. 즉 이 app.run은 맨 마지막에 있어야 한다는거네.

 

서버를 중지시키려면, 붉은버튼 누르면 됨.

중지시키면 당연히 localhost:5000/은 접속안됨

 

이런것들은, 서버에 접속하고 그러면 로그가 발생하는 것임. 실시간으로 발생함.

이게 api를 만든 것임. 주소 뒤에 / 에선 뭔가를 하고, /mypage는 뭔가를 하고.. 그런식이다.

 

 

 

 

플라스크로 url을 만들고, html 띄우기

- templetes 폴더에, index.html을 만들고 아래와 같이 생성한다.  이름이 꼭 index.html 일필요 없음. 바꿔도됨

- 참고 : 이미지는 static에 넣는다. 그리고 위와 같이 경로를 지정해주면 되며, 형식은 원래 이렇게 하는거니까 그대로 한다.

<!DOCTYPE html>
<html lang="en">
<head>
<!DOCTYPE html>
<html lang="ko">
    <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>첫 Flask 프로젝트</title>
    </head>

    <body>
        <h1>서버를 만들었다!</h1>
	 <img src="{{ url_for('static', filename='rome.jpg') }}"/>
    </body>

</html>

 

- 또한, {{url_for('static',filename='rome.jpg')}} 이방식을 그대로 사용한다. 플라스크에서 스태틱폴더에 접근할 때,  rome.jpg라는 파일을 가져와 라는 약속임.

 

- 이제 app.py를 아래와 같이 만든다.

- render_template 라는 함수를 사용한다 함수는 html 그대로 가져와서 보여준다.

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')   ## 여기는 위에서 만든 인덱스를 들어가는 것이다.
def home():  # 함수명 수정 - 이름만 보고 접속되는 페이지를 확인할 수 있게!
    return render_template('index.html')

@app.route('/mypage')  ## 이렇게 주소를 마음대로 만들 수 있다. 
def my_page():
    return 'This is My Page!'

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

- 참고 : @app.route(url경로) 에서, url경로와 함수명은 유일해야 한다.여러개의 함수가 있으면 어떤 함수를 실행해야 할 지 알 수 없기 때문.

 

 

 

get방식의 api만들고, 클라이언트에서 호출하기

 

다시 api를 리마인드해보자

1. 요청정보

2. 서버가 제공할 기능 : 예 : 클라이언트에게 정해진 메시지를 보내는 기능

3. 응답할 메시지 데이터 : json 형식,  'result'= 'success', 'msg'= '이 요청은 get!' 이런걸 보낸다고 정함.

 

구문

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)

@app.route('/')
def home():  # 함수명 수정 - 이름만 보고 접속되는 페이지를 확인할 수 있게!
    return render_template('index.html')

##여기 접속하고 get방식일때, 아래 함수를 작동해라.
@app.route('/test', methods=['GET'])
def test_get():
    # title_give 은 네이버 영화에서 code=xxxxxx 이런것임. 즉 우리가 정의하는 값이다.
    title_receive = request.args.get('title_give')  ### 봄날은 간다 라고 하면, 그게 title_receive로 들어가고 print됨. 즉 요청한걸 잘 받았음.
    ## 이거 프린트는 그냥 콘솔에 나오게 할라고... 별거아님. 
    print(title_receive)
    return jsonify({'result': 'success', 'msg': '이 요청은 GET!'})

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

 

결과

브라우저에서. localhost:5000/test?title_give=원하는값

파이참에서도 출력된다.

여기서 보면, title_give의 value 값으로 봄날은 간다가 들어가서, title_receive 변수에 들어갔고, 아래 print로 프린트된것이다.

 

다시 정리하면, /test에 get 방식이 요청을 하고 title_give라는 값을 명시하면, 그 값에 대해 뭔가를 하면 특정 키값을 구할 수 있는 것.

request.args.get : 정의된 함수임.

 

봄날은간다 이거를 맞춰서 어떤 결과를 출력하도록 코딩할 수 있다.

실제로 문서에 title_give 여기다가 값을 넣어서 요청해라 이런식으로 해야 함.

 

이거를 ajax를 써서 호출하면?

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

- 이 요청은 get!, success 가 뜰 것이다. 또한 prin(내용) 도 파이참에 나올것이다.

 

 

 

 

post 형태의 api 만들고, 클라이언트에서 호출하기

다시 api를 리마인드해보자

1. 요청정보  : 주소 /test , 방식 : POST, 요청데이터는 없음.

2. 서버가 제공할 기능 : 예 : 클라이언트에게 정해진 메시지를 보내는 기능

3. 응답할 메시지 데이터 : json 형식,  'result'= 'success', 'msg'= '이 요청은 POST!' 이런걸 보낸다고 정함.

 

 

post방식은 ajax에서 url에 포함하지 않고, data에다가 넣어야 함. 즉 링크주소에 보여지지 않고 숨겨져야 하기 때문.

회원가입정보등

 

여기서는 request.form로 값을 가져온다.

post방식에서는 보통 json을 리턴하지 않고, 내부에서 작업한다. 데이터베이스 업데이트 등. 근데 여기 예시에서는 그냥 볼라고..

 

API 제작

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)

## HTML을 주는 부분
@app.route('/')
def home(): #함수명수정-이름만보고접속되는페이지를확인할수있게!
    return render_template('index.html')

## API 역할을 하는 부분
##여기접속하고get방식일때,아래함수를작동해라.
@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_give은네이버영화에서code=xxxxxx이런것임.즉우리가정의하는값이다.
    title_receive = request.form['title_give']
    print(title_receive)
    return jsonify({'result': 'success', 'msg': '이 요청은 POST!'})

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

 

ajax로 요청하기 post방식으로.

여기서는 data 항목에 실제 값을 넣어준다.  get처럼 주소에 넣지 않고.

$.ajax({
    type: "POST",
    url: "/test",
    data: { title_give:"봄날은간다" },   <- 데이터를 주소에 안넣고 여기다가 key:value형태로 준다.
    success: function(response){
       console.log(response)
    }
  })

 

결과

 

이제 보면, 동일한 /test 인데 get방식인지, post 방식인지에 따라 다르게 움직일 수 있는 것임. 코드도 좀 다르고.

여기 예시까지는 요청 데이터를 piint로 출력해서 확인했다. 실전에선 요청 데이터로 여러가지 작업을 할 수 있다. 예를들어, 요청 데이터 title_give인 제목의 영화 정보만 보여준다던지.

 

즉 @app.route( )밑에

받은, 또는 보낼 값을  일단 변수로 다 받고, 그 값을 어떻게 디비로 보낼지 아니면 어찌할지등을 해야 하는것임.

 

 

 

 

api 만드는 순서

이렇게 하나씩 하나씩 잘 진행해야 함.

즉, 먼저 api 부터 정의를 다 하고, 그걸 이제 html에서 받아서 결과를 내는 것임. 이게 순서.

 

* 포스트 api 만들기

1. 클라이언트 서버 상태 확인

2. 서버먼저 만들기

3. 클라이언트 만들기

4. 완성 확인

 

* get api 만들기

1. 클라이언트 서버 상태 확인

2. 서버먼저 만들기

3. 클라이언트 만들기

4. 완성 확인

 

 

 

 

실습 - 모두의 책 리뷰 완성

 

전체적인 틀

홈페이지 : html, css / 여기서는 홈페이지가 클라이언트가 된다.

 

리뷰작성화면 : POST API 호스트가 된다.

- title, author, review 가져오기

- db에 정보 삽입하기

- 성공 메시지 반환하기

 

저장된 리뷰확인화면 :GET API

데이터를 가져오는 것. api에 get을 사용해서 db에서 가져옴

 

리뷰작성 API와 클라이언트

아래 클라이언트와 서버 코드는 서로 쌍을 이룬다.

 

API 서버 : 리뷰작성 API  (POST)

- 요청 url : /review

- 요청방식 : post

- 요청데이터 : title, author, review

- 서버가 제공할 기능 : 클라이언트가 보낸 데이터를 가지고 db에 생성하고, 저장이 성공했다고 응답을 보냄

- 응답 데이터 : (json 형식) 'result'= 'success',  'msg'= '리뷰가 성공적으로 작성되었습니다.'

 

API를 사용할 클라이언트

- 리뷰 작성 시 필요한 정보 : title, author, review

- 클라이언트는 input을 받아 title, author, review를 가져온다.

- 입력값이 하나라도 없으면 alert을 띄운다.

- ajax로 api서버에 연결하여 저장을 요청하고 화면을 새로고침한다.

 

리뷰 목록 가져오는 API와 클라이언트

아래 클라이언트와 서버 코드는 서로 쌍을 이룬다.

 

API 서버 : 리뷰 목록 가져오는 API (GET)

- 요청 URL : /review

- 요청방식 : GET

- 요청 데이터 : 없음

- 서버가 제공할 기능 : 데이터베이스에 리뷰 정보를 조회(READ) 하고, 성공 메시지와 리뷰 정보를 응답 데이터로 보냄.

- 응답 데이터 : (JSON 형식) 'result'= 'success' 'reviews'= 리뷰리스트

 

API를 사용할 클라이언트

- 리뷰를 가져오기 위해 필요한 정보 : title, author, review

- 리뷰 목록을 서버에 요청한다.

- 요청 성공 여부를 확인한다.

- 요청을 성공했을 때, 리뷰를 올바르게 화면에 나타낸다.

 

 

 

 

/templates/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 () {
            $("#reviews-box").html("");
            showReview();
        });

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

            // 2. 제목, 저자, 리뷰 중 하나라도 입력하지 않았을 경우 alert를 띄웁니다.
            // .focus는 커서를 그 위치에 위치시킴.
            if (title == "") {
                alert("제목을 입력해주세요");
                $("#title").focus();
                return;   // 더이상 실행할필요 없으니 리턴하는것임. 아래는 굳이 할 필요 없자너
            } else if (author == "") {
                alert("저자를 입력해주세요");
                $("#author").focus();
                return;
            } else if (review == "") {
                alert("리뷰를 입력해주세요");
                $("#bookReview").focus();
                return;
            }

            // 3. POST /review 에 저장(Create)을 요청합니다.  여기서 이제 웹으로 받은 값을, ajax로  post api를 호출함.
            $.ajax({
                type: "POST",
                url: "/review",
                data: {title_give: title, author_give: author, review_give: review},
                success: function (response) {
                    if (response["result"] == "success") {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                }
            })
        }

        function showReview() {
            // 1. 리뷰 목록을 서버에 요청하기
            $.ajax({
                type: "GET",
                url: "/review",
                data: {},
                success: function (response) {
                    // 2. 요청 성공 여부 확인하기
                    if (response["result"] == "success") {
                        let reviews = response["reviews"];
                        // 3. 요청 성공했을 때 리뷰를 올바르게 화면에 나타내기
                        for (let i = 0; i < reviews.length; i++) {
                            makeCard(reviews[i]["title"], reviews[i]["author"], reviews[i]["review"]);
                        }
                    } else {
                        alert("리뷰를 받아오지 못했습니다");
                    }
                }
            })
        }

        function makeCard(title, author, review) {
            let tempHtml = `<tr>
                        <td>${title}</td>
                        <td>${author}</td>
                        <td>${review}</td>
                    </tr>`;
            $("#reviews-box").append(tempHtml);
        }


        function validateLength(obj) {
            let content = $(obj).val();
            if (content.length > 140) {
                alert("리뷰는 140자까지 기록할 수 있습니다.");
                $(obj).val(content.substring(0, content.length - 1));
            }
        }
    </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자까지 입력할 수 있습니다." onkeyup="validateLength(this)"></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>

</html>

 

app.py

from flask import Flask, render_template, jsonify, request
from pymongo import MongoClient

app = Flask(__name__)

DB_HOST = 'ukdisk.asuscomm.com:32768'
DB_ID = 'root'
DB_PW = '883030aa!'
client = MongoClient('mongodb://%s:%s@%s' % (DB_ID, DB_PW, DB_HOST))
db = client.sparta


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


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

    # DB에 삽입할 review 만들기
    # 몽고디비에 넣으려면 딕셔너리 형태로 만들어야 함.
    review = {
        'title': title_receive,
        'author': author_receive,
        'review': review_receive
    }
    # reviews에 review 저장하기
    db.book_reviews.insert_one(review)
    # 성공 여부 & 성공 메시지 반환 이걸 안해도 되지만, success되었다는걸 클라이언트에게 알려주기 위해서 하는 것임.
    return jsonify({'result': 'success', 'msg': '리뷰가 성공적으로 작성되었습니다.'})



@app.route('/review', methods=['GET'])
def read_reviews():
    # 1. DB에서 리뷰 정보 모두 가져오기
    reviews = list(db.book_reviews.find({}, {'_id': 0}))
    # 2. 성공 여부 & 리뷰 목록 반환하기
    return jsonify({'result': 'success', 'reviews': reviews})



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

 

완료

 

 

 

 

 

메타태그 크롤링

개요

 

특정 사이트의 url만 가져왔는데, 자동으로 사진, 제목, 내용일부 등을 가져올 수 있다.

이러한 값은 해당 사이트의 url 안에 있는 meta 태그의 값들이다.

 

meta 태그는, <head>~</head> 부분에 들어가는, 눈으로 보이는 것(body) 외에 사이트의 속성을 설명해주는 태그들입니다.

예) 구글 검색 시 표시 될 설명문, 사이트 제목, 카톡 공유 시 표시 될 이미지 등

우리는 그 중 og:image / og:title / og:description 을 크롤링 할 예정입니다.

 

og란? open graph

페북에서 만듬. 링크를 놓으면 이미지, 제목, 디스크립션이 자동으로 생기는 것. 썸네일 등.

 

og:image

og:title

og:description

 

암묵적 표준이 되어 meta 태그 안에 대부분 og 정보가 있게 되었다.

메타태그는 head 부분에 들어가는, 눈에 보이는 것(body) 이외에 사이트의 속성을 설명해주는 태그들이다.

 

메타태그 굳이 왜? 있는 이유는

개발자들이 사용하거나, sns에서 공유할때나 등등 특정 사용처가 있기 때문에 넣는 것임. 구글 검색 시 표시될 설명문, 사이트 제목, 등등

 

 

크롬 검사에서 확인

 

기본 예제

 

메타태그를 가져오려면 평소방법과는 좀 다른방법을 사용해야한다.

 

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

메타태그의 property가 og:image인것을 가져와라. 이런식.

 

import requests
from bs4 import BeautifulSoup

url = 'https://platum.kr/archives/120958'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url, headers=headers)

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

# 메타데이터는 카피 셀렉터로 안될 수 있다. 안되면 아래와 같은 방식으로 크롤링한다.
og_image = soup.select_one('meta[property="og:image"]')
og_title = soup.select_one('meta[property="og:title"]')
og_description = soup.select_one('meta[property="og:description"]')

url_image = og_image['content']
url_title = og_title['content']
url_description = og_description['content']

print(url_image)
print(url_title)
print(url_description)

 

참고 : 페이지 새로고침 함수.

window.location.reload();

 

 

 

실습 - 나홀로메모장 완성하기

 

 

기능

- 포스팅하기 : 카드 생성 (create)

- 리스팅하기 : 저장된 카드 보여주기 (read)

 

- url과 코멘트를 서버에 보내면, 서버는 받은 url로 스크래핑하여 이미지,영화제목,소개를 추출해서 db에 코멘트와 함께 저장한다.

- html 카드 형태로 db에 저장한 내용을 출력한다.

 

api 설계 - 카드생성API

클라이언트에서 받은 url, comment를 이용해 페이지 정보를 찾고 저장한다.

 

**A. 요청 정보**
- 요청 URL= `/memo` , 요청 방식 = `POST`
- 요청 데이터 : 제목(url_give), 코멘트(comment_give)

**B. 서버가 제공할 기능** 
- URL의 meta태그 정보를 바탕으로 제목, 설명, 이미지URL 스크래핑
- (제목, 설명, URL, 이미지URL, 코멘트) 정보를 모두 DB에 저장

**C. 응답 데이터**  
- API가 정상적으로 작동하는지 클라이언트에게 알려주기 위해서 성공 메시지 보내기
- (JSON 형식) 'result'= 'success'

 

 

API 설계 - 저장된카드보여주기 API

저장한 카드들을 보여준다.

 

**A. 요청 정보**
- 요청 URL= `/memo` , 요청 방식 = `GET`
- 요청 데이터 : 없음

**B. 서버가 제공할 기능** 
- DB에 저장돼있는 모든 (제목, 설명, URL, 이미지URL, 코멘트) 정보를 가져오기

**C. 응답 데이터**  
- API 동작 잘했다는 성공 메시지, 아티클(기사)들의 정보(제목, 설명, URL, 이미지URL, 코멘트)
- (JSON 형식) 'result'= 'success', 'articles': 아티클 정보

 

 

flask 서버 설계 (api 생성)

 

카드생성api

API 는 약속이라고 했습니다. 위에 미리 설계해 둔 API 정보를 보고 만들어보죠!

메모를 작성하기 위해 서버가 전달받아야하는 정보는 다음 두 가지 입니다.

  • URL(url_give)
  • 코멘트(comment_give)

그리고 URL를 meta tag를 스크래핑해서 아래 데이터를 저장(Create)합니다.

  • URL(url)
  • 제목(title)
  • 설명(desc)
  • 이미지URL(image)
  • 코멘트(comment)

따라서 서버 로직은 다음 단계로 구성되어야 합니다.

  1. 클라이언트로부터 데이터를 받기.
  2. meta tag를 스크래핑하기
  3. mongoDB에 데이터를 넣기

저장된카드보여주는api

메모를 보여주기 위해 서버가 추가로 전달받아야하는 정보는 없습니다. 조건없이 모든 메모를 보여줄 꺼니까요!

따라서 서버 로직은 다음 단계로 구성되어야 합니다.

 

1. mongoDB에서 _id 값을 제외한 모든 데이터 조회해오기 (Read)

2. articles라는 키 값으로 articles 정보 보내주기

 

 

api를 사용할 클라이언트 설계

 

카드생성 클라이언트
메모를 작성하기 위해 서버에게 주어야하는 정보는 다음 두 가지 입니다.
- URL (url_give) : meta tag를 가져올 url
- comment (comment_give) : 유저가 입력한 코멘트

따라서 클라이언트 로직은 다음 단계로 구성되어야 합니다.
1. 유저가 입력한 데이터를 #post-url과 #post-comment에서 가져오기
2. /memo에 POST 방식으로 메모 생성 요청하기
3. 성공 시 페이지 새로고침하기

 

저장된카드보여주는 클라이언트

 

메모를 작성하기 위해 서버에게 주어야하는 정보는 없습니다. 조건없이 모든 메모를 가져오기 때문입니다.

따라서 클라이언트 로직은 다음 단계로 구성되어야 합니다.

1. /memo에 GET 방식으로 메모 정보 요청하고 articles로 메모 정보 받기

2. , makeCard 함수를 이용해서 카드 HTML 붙이기 (→ 2주차 Ajax 연습과 같습니다!)

 

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 () {
                $("#cards-box").html("");
                showArticles();
            });

            function openClose() {
                // id 값 post-box의 display 값이 block 이면(= 눈에 보이면)
                if ($("#post-box").css("display") == "block") {
                    // post-box를 가리고
                    $("#post-box").hide();
                    // 다시 버튼을 클릭하면, 박스 열기를 할 수 있게 텍스트 바꿔두기
                    $("#btn-post-box").text("포스팅 박스 열기");
                } else {
                    // 아니면(눈에 보이지 않으면) post-box를 펴라
                    $("#post-box").show();
                    // 다시 버튼을 클릭하면, 박스 닫기를 할 수 있게 텍스트 바꿔두기
                    $("#btn-post-box").text("포스팅 박스 닫기");
                }
            }

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

                // 2. memo에 POST 방식으로 메모 생성 요청하기
                $.ajax({
                    type: "POST", // POST 방식으로 요청하겠다.
                    url: "/memo", // /memo라는 url에 요청하겠다.
                    data: {url_give: url, comment_give: comment}, // 데이터를 주는 방법
                    success: function (response) { // 성공하면
                        if (response["result"] == "success") {
                            alert("포스팅 성공!");
                            // 3. 성공 시 페이지 새로고침하기
                            window.location.reload();
                        } else {
                            alert("서버 오류!")
                        }
                    }
                })
            }

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

            function makeCard(image, url, title, desc, comment) {
                let tempHtml = `<div class="card">
                        <img class="card-img-top" src="${image}" alt="Card image cap">
                        <div class="card-body">
                        <a href="${url}" target="_blank" class="card-title">${title}</a>
                        <p class="card-text">${desc}</p>
                        <p class="card-text comment">${comment}</p>
                        </div>
                    </div>`;
                $("#cards-box").append(tempHtml);

            }
        </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 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 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 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 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 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 href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
            </div>
        </div>
    </body>

</html>

 

app.py

from flask import Flask, render_template, jsonify, request
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient  # pymongo를 임포트 하기(패키지 인스톨 먼저 해야겠죠?)

app = Flask(__name__)

client = MongoClient('localhost', 27017)  # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta_ninework  # 'dbsparta'라는 이름의 db를 만들거나 사용합니다.


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


@app.route('/memo', methods=['POST'])
def post_article():
    # 1. 클라이언트로부터 데이터를 받기
    url_receive = request.form['url_give']  # 클라이언트로부터 url을 받는 부분
    comment_receive = request.form['comment_give']  # 클라이언트로부터 comment를 받는 부분

    # 2. meta tag를 스크래핑하기
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
    data = requests.get(url_receive, headers=headers)
    soup = BeautifulSoup(data.text, 'html.parser')

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

    url_title = og_title['content']
    url_description = og_description['content']
    url_image = og_image['content']

    article = {'url': url_receive, 'title': url_title, 'desc': url_description, 'image': url_image,
               'comment': comment_receive}

    # 3. mongoDB에 데이터를 넣기
    db.articles.insert_one(article)

    return jsonify({'result': 'success'})


@app.route('/memo', methods=['GET'])
def read_articles():
    # 1. mongoDB에서 _id 값을 제외한 모든 데이터 조회해오기 (Read)
    result = list(db.articles.find({}, {'_id': 0}))
    # 2. articles라는 키 값으로 article 정보 보내주기
    return jsonify({'result': 'success', 'articles': result})


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

 

 

참고.

card가 정렬되는 순서는 위에서 아래로 채워지고, 왼쪽부터 오른쪽으로 순서대로 채워집니다. 부트스트랩 컴퍼넌트 페이지에 적혀있어요. "*Cards are ordered from top to bottom and left to right.*" ([컴퍼넌트 페이지 링크](https://getbootstrap.com/docs/4.1/components/card/#card-columns))

 

 

 

 

숙제 - 원페이지 쇼핑몰에 api 가능 사용하기

  1. 주문하기(POST): 정보 입력 후 '주문하기' 버튼클릭 시 주문목록에 추가
  2. 주문내역보기(GET): 페이지 로딩 후 하단 주문 목록이 자동으로 보이기

이 숙제는 내가 직접 코드를 짠 것임.

 

 

 

static 디렉토리

decorate.css

/*site font (google)*/
* {
    font-family: 'Noto Serif KR', serif;
}

/*Entire form design*/
.wrap {
    margin: auto;
    width: 750px;
}

/*Every div design*/
.form_desgin {
    padding: 10px;
}

/*image design*/
.image_design {
    /*display: block, margin:audo 2개를 넣으니 가운데 정렬 됨*/
    display: block;
    margin: auto;
    width: 650px;
    background-size: cover;
    background-position: center;
}

/* 달러-원 환율 색깔 */
.emphasize {
    color: blue;
}

/* 주문하기 버튼 */
.center {
    display: block;
    margin: auto;
}

ipadpro.jpg 파일

 

 

 

templates 디렉토리

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

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

    <!--Google Font-->
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR&display=swap" rel="stylesheet">

    <!--Custom CSS-->
    <link rel="stylesheet" type="text/css" href="../static/decorate.css"/>

    <!--Custom JavaScript-->
    <script>

        function order_complete() {

            // insert data in the select boxes
            let custName = $('#customerName').val();
            let prodCounter = $('#inputGroupSelect01 option:selected').val();
            let custAddr = $('#customerAddress').val();
            let custPhone = $('#customerPhone').val();

            if (custName == "") {
                alert("이름을 입력해 주세요.")
            } else if (prodCounter == "-- 수량을 선택하세요 --") {
                alert("수량을 선택해 주세요.")
            } else if (custAddr == "") {
                alert("주소를 입력해 주세요.")
            } else if (custPhone == "") {
                alert("전화번호를 입력해 주세요.")
            } else if (!checkPhone(custPhone)) {
                alert('잘못된 휴대폰 번호입니다. 010-XXXX-XXXX 형식을 사용해 주세요.')
            } else {
                // insert data to database
                $.ajax({
                    type: "POST",
                    url: "/request",
                    data: {ordername: custName, ordercount: prodCounter, orderaddr: custAddr, orderphone: custPhone},
                    success: function (response) {
                        if (response["result"] == "success") {
                            alert(response["msg"]);
                            window.location.reload();
                        } else {
                            alert("뭔가 문제가 있는 것 같습니다. 다시 시도해 주세요.")
                        }
                    }
                })
            };

            // clear select boxes
            $('#customerName').val('');
            $('#inputGroupSelect01').val('');
            $('#customerAddress').val('');
            $('#customerPhone').val('');
        };


        function checkPhone(number) {
            var regPhone = /010[-]\d{4}[-]\d{4}$/g;
            return regPhone.test(number);
        };

        // 주문하기 버튼 누르면, 값들 읽어와서 디비에 저장, 해당 사이트에들어가면, 주문내역이 db를 통해 로딩됨. 자동으로.

        function addTable(a,b,c,d) {

        }

        $(document).ready(function () {
            $('#dwrate').empty()
            $.ajax({
                type: "GET",
                url: "https://api.manana.kr/exchange/rate.json",
                data: {},
                success: function (response) {
                    let check = response[1]["rate"]
                    let addrate = `<h6>달러-원 환율 : ${check}</h6>`
                    $('#dwrate').append(addrate);
                }
            })
            $('#addOrders').empty()
            $.ajax({
                type: "GET",
                url: "/request",
                data: {},
                success: function (response) {
                    let data = response['orderdata']
                    for (let i = 0; i < data.length; i++) {
                        let custNameOrder = data[i]['name']
                        let custCountOrder = data[i]['count']
                        let custAddrOrder = data[i]['address']
                        let custPhoneOrder = data[i]['phone']
                        let addData = `<tr><td>${custNameOrder}</td><td>${custCountOrder}</td><td>${custAddrOrder}</td><td>${custPhoneOrder}</td></tr>`
                        $('#addOrders').append(addData);
                    }
                }
            })
        });
    </script>

    <!--Site title-->
    <title>애플 쇼핑몰</title>

</head>

<body>

<!--entire site attribute-->
<div class="wrap">

    <!--image-->
    <div class="form_desgin">
                <img src="../static/ipadpro.jpg" class="image_design"/>
    </div>

    <!--description-->
    <div class="form_desgin">
        <h1>iPad Pro (12.9) Gen1 128GB Cellular</h1>
        <h3>가격: 499,000원</h3>
        <h6>리퍼 아이패드 프로 1세대 12.9 입니다. 동영상 편집이나 프로그래밍에는 맞지 않지만, 넷플릭스 머신이나 인터넷 서핑용, 수업 필기용으로 적당합니다. 사은품으로 애플펜슬(1세대)를 함께 드립니다. </h6>
    </div>

    <!--order form-->
    <div class="form_desgin">

        <!--doller-won rate-->
        <div class="emphasize" id="dwrate">
        </div>

        <!--order form main information-->
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text" id="inputGroup-sizing-default">주문자 성함: </span>
            </div>
            <input id="customerName" 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 class="custom-select" id="inputGroupSelect01">
                <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" id="inputGroup-sizing-default">주소: </span>
            </div>
            <input id="customerAddress" 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" id="inputGroup-sizing-default">전화번호: </span>
            </div>
            <input id="customerPhone" type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default">
        </div>

        <button onclick="order_complete()" type="button" class="center btn btn-outline-secondary">주문하기</button>

    </div>

    <!--order list-->
    <div class="form_desgin">
        <table class="table">
            <thead>
            <tr style="text-align: center">
                <th scope="col">주문자 성함</th>
                <th scope="col">수량</th>
                <th scope="col">주소</th>
                <th scope="col">전화번호</th>
            </tr>
            </thead>
            <tbody id="addOrders" style="text-align: center">
            </tbody>
        </table>
    </div>

</div>

</body>

</html>

 

루트 디렉토리

app.py

# 라이브러리 import
from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient

## 데이터베이스 연결 정보
client = MongoClient('localhost', 27017)
db = client.sparta

app = Flask(__name__)


### 본문 시작 ##################################

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

@app.route('/request', methods=['POST'])
def insert_order_info():
    order_name = request.form['ordername']
    order_count = request.form['ordercount']
    order_addr = request.form['orderaddr']
    order_phone = request.form['orderphone']
    db.week4_homework.insert_one({'name': order_name, 'count': order_count, 'address': order_addr, 'phone': order_phone})
    return jsonify({'result': 'success', 'msg': '주문이 정상적으로 완료되었습니다.'})

@app.route('/request', methods=['GET'])
def read_order_info():
    order_data = list(db.week4_homework.find({}, {'_id': 0}))
    return jsonify({'result': 'success', 'orderdata': order_data})

# 본문 끝 ###################################

# 서버 관련 정보
if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

 

 

결과

'SCC 9기' 카테고리의 다른 글

[SCC 9기] - Project 개발 일지 : 프로젝트 기획안  (0) 2020.08.13
[SCC 9기] 5주차  (0) 2020.08.11
[SPC 9기] 3주차  (0) 2020.07.25
[SPC 9기] 2주차  (0) 2020.07.24
[SPC 9기] 1주차 - 프론트엔드  (0) 2020.07.16

+ Recent posts