maino77 2021. 7. 24. 13:47

1. 4주차 배울 내용

이제 서버를 직접 만들어 볼 것이다.

여태까지 우리는 html,css,js를 이용해서 서버에 가져다 줄 파일들을 만들었다. 이제는 요청을 받고 파일을 가져다 주거나 DB에서 작업을 하는 등의 일을 하는 서버를 만들 것이다. 

서버라는 것은 컴퓨터에 돌아가고 있는 하나의 프로그램이라고 생각하면 된다.

 

 

 

2. Flask 시작하기 - 서버만들기

서버를 만들 새로운 파이썬 파일을 만드는데 이름은 통상적으로 app.py라고 쓴다.

코드를 작성하기 전에 파이참에서 Flask를 설치한다. 스크래핑을 했을 때처럼 서버를 만드는데 도움을 주는 라이브러리로 왼쪽 상단에 file을 눌러 세팅으로 들어가 project:prac으로 들어가서 python interpreter로 들어가서 Flask를 설치한다.

Flask란 프레임워크 중 하나로 서버를 구동시켜주는 편한 코드 모음이다. 우리가 일일이 만들지 않고 가져다 쓸 수 있게 도와주는 것이 Flask이다.

💡 라이브러리와 프레임워크의 차이?
프레임워크는 전체적인 흐름을 자체적으로 가지고 있어 프로그래머는 그 안에서 필요한 코드를 작성합니다. 반면에 
라이브러리는 프로그래머가 전체적인 흐름을 가지고 있어 라이브러리를 자신이 원하는 기능을 구현하고 싶을 때 가져다 사용할 수 있다

그래서 통상적으로 프레임워크는 하나의 프레임워크 안에서 코드를 작성한다.
                                                                                                       [참고: https://juyeop.tistory.com/23]

Flask를 설치하면 기본 코드를 입력해준다.

from flask import Flask
app = Flask(__name__)

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

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

그리고 실행시켜준 다음 localhost:5000을 입력해 실행시킨다.

이런 화면이 뜨면 제대로 작동하고 있는 것이다.

5000은 우리가 코드에서 port로 5000으로 지정했기 때문인데 이것을 바꾸면 주소의 숫자를 바꿀 수 있다. 그러나 이것은 정해져 있기 때문에 바꾸지 않는 것이 좋다.

 

그러면 이제 코드를 바꾸면서 연습해보자

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
   return '<button>나는 버튼이다</button>'

@app.route('/mypage')
def mypage():
   return '나의 첫 페이지'

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

첫 번째 @app에서 home은 버튼을 만드는 코드로 그 안은 '나는 버튼이다'라고 써져있다.

두 번째 @app에서는 mypage로 / 뒤에 붙이고 함수 이름도  mypage로 바꾼 출력되는 내용을 바꾸어보았다.

첫번째 @app의 화면
두 번째 @app의 화면

그 결과 이처럼 나온 것을 확인할 수 있다. 특히 두 번째 @app은 기존의 주소에 /mypage를 입력하여 불러온 것을 확인할 수 있다.

이처럼 함수에 입력하여 화면에 출력할 수 있다는 사실을 알아내었다. 그러나 html을 다 쓰는 것은 너무나 힘든 일이다. 그래서 이를 해결할 방법을 다음 챕터에서 배워보자.

 

 

 

3. Flask 시작하기 - html 파일 주기

flask는 대표적으로 정해진 폴더 구조가 있다.

static과 templates이다. static은 CSS나 이미지 파일들을 담아둘 때 쓰는 폴더이다. templates는 html 파일들을 담아두는 곳이다. 

templates에 index.html 파일을 만든 후 body에 아무 내용을 입력한 후 app.py로 와서 render_template를 다음처럼 입력한다.

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

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


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

첫 줄에 Flask 다음에 입력하고 return 다음에 입력하고 괄호를 친 다음 그 안에 따옴표 후 index.html을 입력하면 localhost:5000에서 index.html 파일 내용이 출력된다.

결과물

우리는 이것을 통해 render_template라는 flask에서 이미 정해둔 함수를 이용하면 자동으로 templates 폴더 안에 있는 index.html파일을 화면에 출력시킨다.

두 번째 사진으로 다시 설명하면 첫 화면에 들어오면 @app.route('/')가 실행된다. (5000 뒤에 /가 생략되어있다)

그러면서 아래 home이라는 함수가 실행되면서 return으로 render_template가 작동해 index.html을 열게 된다.

 

그렇다면 하나 궁금한 것이 생긴다.

html 파일에서 바로 페이지를 연 것과 이렇게 서버를 만들어서 페이지를 연 것. 무슨 차이가 있을까?

내 컴퓨터에서 만든 서버이지만 서버에 요청을 해서 서버가 가지고 있던 index.html 파일을 받아와서 그린 것이다. 반면에 html파일에서 곧바로 연 것은 말 그대로 서버를 거치지 않고 그냥 연 것에 불과하다.

 

 

 

4. Flask 시작하기 - 본격 API 만들기

API를 만들기 전에 간단하게 배웠던 것을 복습하겠다.

API에는 2가지 종류가 있다. Get 방식과 Post방식이 있다. (이외에도 많은 종류가 있지만 우리가 지금 쓸 종류는 2가지!)

GET방식은 통상적으로 데이터 조회를 요청할 때 사용되었다. 예를 들어 통화 목록이나 영화 목록을 조회할 때 사용되었다. 데이터를 전달할 때는 URL뒤에 물음표를 붙이고 Key = Value 형태로 전달한다. (예: google.com?q=북극곰)

POST방식은 통상적으로 데이터 생성, 변경, 삭제 요청을 할 때 사용된다. 예를 들면 회원가입, 회원 탈퇴, 비밀번호 수정을 할 때 상용된다. 데이터를 전달할 때는 보이지 않는 html body에 key:value 형태로 전달된다.

서버에 요청할 때는 Ajax를 이용했었다. 그래서 우리가 api를 만들고 클라이언트에서 Ajax로 콜 해보는 과정을 반복해서 연습할 것이다.

 

먼저 우리가 연습할 것은 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.py에 이렇게 입력하고 서버에 요청해 페이지를 연 다음 콘솔 창에서 다음과 같이 입력한다.

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

그럼 이렇게 나오는데 코드를 간단하게 설명하자면 먼저 콘솔에 입력한 코드는 console.log(respones)를 했기 때문에 결과 값으로 {msg: "이 요청은 GET!", result: "success"}가 나오게 된다. 그리고 title_give라는 이름으로 봄날은 간다라는 값을 가져왔다.

그러면 서버에 입력했던 코드를 뜯어보자. request.arg.get('title_give')는 title_give로 가져온 값을 가지고 오라는 의미이다. 그러면 이것은 봄날은 간다일 것이고 이것은 title_receive가 된다. 이것은 print로 인해 아래 결과 창에 출력될 것이다.

이후 return으로 jsonify의 값인 {'result':'success', 'msg': '이 요청은 GET!'}를 출력한다.

 

정리하면 Ajax로 콜한 것을 API에서 뭔가 처리를 한 다음 response를 주고  그것을 Ajax가 response에서 console.log로 찍어서 볼 수 있게 한다는지 등의 결과를 출력하게 할 수 있다.

 

이번에는 POST방식으로 연습해보겠다.

@app.route('/test', methods=['POST'])
def test_post():
   title_receive = request.form['title_give']
   print(title_receive)
   return jsonify({'result':'success', 'msg': '이 요청은 POST!'})

이번 코드는 methods가 post이다. 타입이 post라는 의미를 갖는다.

앞의 get과 코드가 유사해 보이면서도 title_give 앞에 있는 코드가 다르다.

get에서는 request.args.get으로 썼다면 post에서는 request.form으로 바뀌었음을 알 수 있다. 그러나 tite_give로 가져온 값을 의미한다는 사실은 같다.

요청하는 Ajax코드는 다음과 같다.

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

Ajax로 type은 post이고 url은 /text이며 데이터는 {title_give:'봄날은간다'}로 값을 가진다. 그러면 title_give에 봄날은간다가 담겨있다.

그러면 서버의 코드에서 title_give를 title_receive에 담는다. 그리고 print로 그것을 출력한 뒤 return으로 jsonify를 반환한다.

print로 출력한 결과
jsonify를 return한 결과

 

만약 ajax에서 요청한 것이 title_give2라면 에러가 일어나는데 (INTERNAL SERVER ERROR)라는 문구가 나온다.

이것은 서버 쪽에서 에러가 났다는 의미다. 그래서 app.py의 에러를 확인해야 한다.

출력된 에러 코드를 보니 title_give가 없다고 나온다.

그래서 ajax와 서버의 이름을 맞춰주어야 한다.

 

 

 

5. [모두의 책 리뷰] - POST연습(리뷰저장)

이번 챕터의 순서는 다음과 같다

1. 클라이언트와 서버 확인하기

2. 서버 코드 만들기

3. 클라이언트 코드 만들기

4. 완성된 것 확인하기

이 순서대로 하면 서버에 저장할 수 있게 할 수 있다.

 

1) 클라이언트와 서버 확인하기

서버의 코드는 다음과 같다.

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

클라이언트 쪽의 코드는 다음과 같다

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

그러면 과정을 살펴보자.

클라이언트에서 POST타입으로 /review에서 {sample_give: '샘플데이터'}라는 데이터를 건넸다. 그래서 서버에서는 이것을 smaple_recive = request.form['sample_give']로 저장하고 return으로 {'msg':'이 요청은 POST'}를 출력한다. 그러면 이것이 response로 들어가고 alert(response["msg"]);로 인해 "이 요청은 POST"가 알림으로 뜨게 된다.

이것으로 클라이언트와 서버가 무엇을 할지 정해주었다. 그러면 이제 서버가 저장할 수 있도록 만들어보자.

 

2) 서버 코드 만들기

서버가 저장할 것은 제목, 저자, 리뷰이다.

그래서 서버의 코드를 수정해주어야 한다.

## API 역할을 하는 부분
@app.route('/review', methods=['POST'])
def write_review():
    title_receive = request.form['title_give']
    author_receive = request.form['author_give']
    review_receive = request.form['review_give']

    doc= {
        'title':title_receive,
        'author':author_receive,
        'review':review_receive
    }

    db.bookreview.insert_one(doc)

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

입력된 제목, 저자, 리뷰를 받아온 다음 doc에 묶어놓고 bookreview라는 컬렉션의 DB에 저장한 다음 이것이 완료되었다고 return 한다.

 

3) 클라이언트 코드 만들기

클라이언트 코드는 제목, 저자, 리뷰를 가져와서 서버에 보내주면 된다.

function makeReview() {
                let title = $(`#title`).val()
                let author = $(`#author`).val()
                let review = $(`#bookReview`).val()

                $.ajax({
                    type: "POST",
                    url: "/review",
                    data: {title_give:title,author_give:author,review_give:review},
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                })
            }

jQuery를 활용하여 제목, 저자, 리뷰에 주어진 id를 이용해 .val()로 입력된 글들을 가져와 각각의 변수에 저장한 다음 Ajax를 이용해 data로 보내준다.

이렇게 하면 리뷰를 작성해서 DB에 저장시킬 수 있다. 그래서 페이지에서 간단한 리뷰를 작성하고 확인해보자.

 

4) 완성된 것 확인하기

이것을 페이지에서 확인할 수 없지만 Robo 3T를 이용해 확인할 수 있다.

우리가 서버에서 db.bookreview.insert_one(doc)로 저장했기 때문에 bookreview에서 확인을 하면 작성한 리뷰가 저장된 것을 볼 수 있다.

DB에 리뷰가 저장된 모습

 

 

 

6. [모두의책리뷰] - GET연습 (리뷰보기)

이번에는 우리가 저장했던 리뷰를 보여줄 것이다. 순서는 앞서 했던 챕터와 같이 진행할 것이다.

1. 클라이언트와 서버 확인하기

2. 서버 코드 작성하기

3. 클라이언트 코드 작성하기

4. 완성 확인하기

 

1) 클라이언트와 서버 확인하기

서버쪽 코드

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

클라이언트쪽 코드

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

GET방식으로 클라이언트에서 작동하고있다. /review에 get방식으로 "smaple_give=샘플데이터"를 가져가서 요청하고 있다. 

그러면 서버의 코드에서 "smaple_give=샘플데이터"를가져가 저장한 다음 출력하고 return으로 jsonify({'msg': '이 요청은 GET!'})을 반환한다.

다시 클라이언트로 돌아와서 response로 받아 알림창으로 msg의 메세지인 '이 요청은 GET!'을 출력한다.

 

2) 서버 코드 작성하기

서버는 모든 리뷰를 DB에서 가져와 클라이언트에서 전달만 하면 된다. 그래서 따로 클라이언트에서 받을 것은 없고 리뷰만 DB에서 가져오면 된다.

그래서 코드를 다음과 같이 작성한다.

@app.route('/review', methods=['GET'])
def read_reviews():
    reviews = list(db.bookreview.find({},{'_id':False})
    return jsonify({'all_reviews': reviews})

DB에서 모두 찾아 가져온 다음 클라이언트로 return한다.

 

3) 클라이언트 코드 작성하기

all_reviews에 리뷰가 담기면 그것을 이용해 for문으로 리뷰를 붙여주면 된다.

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

클라이언트가 가져갈 데이터가 없기 때문에 기존에 있었던 "?sample_give=샘플데이터" 를 제거한다.

그리고나서 response로 잘 가져왔는지 console.log로 확인한다.

잘 출력된 모습

확인이 되었다면 이제 각각의 변수에 담아 출력하도록 만들어보자.

 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>
                                                <td>${title}</td>
                                                <td>${author}</td>
                                                <td>${review}</td>
                                            </tr>`
                            $('#reviews-box').append(temp_html)
                        }
                    }
                })
            }

우리는 앞서 서버에 title, author, review를 저장했었다. 그것을 response로 가져왔고 이제 title, author, review로 분리하여 웹페이지에 각각 틀에 맞게 $('#reviews-box').append(temp_html)로 나타나도록 하였다.

 

4) 완성 확인하기

완성된 모습

 

*데이터를 저장하고 불러와 보여주지까지의 서버와 클라이언트의 코드를 한 번에 확인해보자

서버 코드

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

## API 역할을 하는 부분
@app.route('/review', methods=['POST'])
def write_review():
    title_receive = request.form['title_give']
    author_receive = request.form['author_give']
    review_receive = request.form['review_give']

    doc= {
        'title':title_receive,
        'author':author_receive,
        'review':review_receive
    }

    db.bookreview.insert_one(doc)

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


@app.route('/review', methods=['GET'])
def read_reviews():
    reviews = list(db.bookreview.find({},{'_id':False}))
    return jsonify({'all_reviews': reviews})

클라이언트 코드

        <script type="text/javascript">

            $(document).ready(function () {
                $("#reviews-box").html("");
                showReview();
            });

            function makeReview() {
                let title = $(`#title`).val()
                let author = $(`#author`).val()
                let review = $(`#bookReview`).val()

                $.ajax({
                    type: "POST",
                    url: "/review",
                    data: {title_give:title,author_give:author,review_give:review},
                    success: function (response) {
                        alert(response["msg"]);
                        window.location.reload();
                    }
                })
            }

            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>
                                                <td>${title}</td>
                                                <td>${author}</td>
                                                <td>${review}</td>
                                            </tr>`
                            $('#reviews-box').append(temp_html)
                        }
                    }
                })
            }
        </script>

 

 

 

7. [나홀로메모장] - 프로젝트 세팅

file에서 뉴 프로젝트를 눌러 새로운 폴더에 파일을 생성한 다음 templates, static 폴더를 생성한다. 그리고 templates 안에 index.html을 만든다.

패키지 설치를 위해 file에서 setting을 눌러 project에서 python inerpreter에서 flask와 pymongo를 설치한다.

그리고 이번에는 url을 입력하면 크롤링으로 사진과 설명을 가져오기 때문에 크롤링을 위한 requests와 bs4를 깔아야 한다.

 

 

 

8. [나홀로메모장] - API 설계하기

모든 프로젝트를 하기 전에는 API를 설계하는 일이 가장 처음에 해야할 일이다.

그래야지 서비스에 어떤 기능이 필요하고 어떤 순서로 구현을 할 건지 계획을 잡을 수 있다. 이런 설계를 처음에 해야 한다.

우리가 만들 나홀로메모장 페이지에서는 2가지 기능을 구현해야 한다.

나홀로메모장 페이지

1. URL과 코멘트를 서버에 보내서 서버에서 그 데이터를 저장하는 것.

2. DB에 저장된 데이터를 보여주기 (이미지, 제목, 링크, 줄거리, 코멘트)

 

이것들을 좀 더 세부적으로 살펴보면 다음과 같다.

1. 카드 생성(URL과 코멘트를 서버에 보내서 서버에서 그 데이터를 저장하는 것.)

A. 요청정보

  • 요청 URL= /memo, 요청 방식 = POST
  • 요청 데이터 : URL(url_give), 코멘트(comment_give)

B. 서버가 제공하라 기능

  • URL의 meta태그 정보를 바탕으로 제목, 설명, 이미지URL 스크래핑
  • (제목, 설명, URL, 이미지URL, 코멘트) 정보를 모두 DB에 저장

C. 응답 데이터

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

 

2. 저장된 카드 보여주기(DB에 저장된 데이터를 보여주기 (이미지, 제목, 링크, 줄거리, 코멘트)) 

  - 페이지를 불러오자마자(로딩이 끝나면) 바로 실행되도록 설정

A. 요청 정보

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

B. 서버가 제공할 기능

  • DB에 저장되어 있는 모든 (이미지, 제목, 링크, 줄거리, 코멘트) 정보를 가져오기

C. 응답 데이터

  • 아티클(기사)들의 정보 (이미지, 제목, 링크, 줄거리, 코멘트)를 카드로 만들어서 붙이기
  • (JSON 형식) 'articles': 아티클 정보

 

이렇게 설계를 한 다음 api를 구현해보자.

 

 

 

9. [나홀로메모장] - 조각 기능 구현해보기

API를 구상했다면 이미지, 제목, 줄거리를 어디서 가져올지에 대해 의문이 든다.

그래서 프로젝트를 진행하기 전에 이런 기능들을 미리 구현해서 테스트를 해본다. 우리는 이것을 조각 기능이라 부를 것이다.

우리는 이미지, 제목, 줄거리를 url만을 이용해 크롤링을 해야 한다. 이것을 위해서는 meta태그를 이용해서 스크래핑 할 것이다.

head 안에 있는 meta태그에 우리가 원하던 데이터가 모두 있다.

meta태그란 우리가 카카오톡이나 페이스북에 공유할 때 자동으로 이미지, 타이틀, 요약이 나오는 경우가 있다.

이런 식으로 나온다

meta 태그에다가 og:image / og:title / og:description 으로 넣어놓으면 어느 사이트이든 자동으로 긁어갈 수 있도록 해놓았다. 그래서 이것들을 긁어와서 이미지와 타이틀 등을 보여준다.

 반대로 이것을 보여주는 입장이면 og:image / og:title / og:description 을 잘 넣어놓아야 공유를 할 때 이미지, 제목, 설명이 함께 보여질 것이다.

어쨌든 우리는 meta 태그에서 og:image / og:title / og:description 라고 되어있는 부분을 스크래핑 할 것이다.

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('')를 입력하고 ''안에 og:title 부분을 Copy selector해서 넣어준다. 그리고 이것이 제대로 출력되는지 확인을 위해 print(title)을 입력한다. 그 결과 제대로 출력되지 않는 것을 확인했다.

그 이유는 우리가 console창에서 보는 meta태그의 순서와 파이썬 코드로 접속했을 때 나오는 meta태그의 순서가 다르기 때문이다.

 이를 해결하기 위해서는 코드를 다음과 같이 바꿀 수 있다.

전) title = soup.select_one('head > meta:nth-child(9)')
후) title = soup.select_one('meta[property="og:title"]')

meta 태그 중에서 이 속성이 일치하는 애를 가져오라는 의미다.

이렇게 코드를 입력하고 실행시키면 크롤링이 된 것을 확인할 수 있다.

여기서 우리가 필요한 것은 content이기 때문에 코드에 추가로 입력한다.

print(title['content'])

제목만 출력되었다

그러면 이것을 수정해서 title = soup.select_one('meta[property="og:title"]')['content']로 작성하고 print(title)하면 같은 결과가 출력된다.

이후 image와 description도 이에 맞게 코드를 작성해준다.

* 전체 코드

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를 먼저 가져와보겠습니다.

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)

 

 

10. [나홀로메모장] - POST연습(메모하기)

API를 만들고 사용하기 위해서는 포스팅해야 한다. (Creacte -> POST)

그 전에 우리가 만들 API는 총 2가지로

1) 포스팅 API - 카드를 생성: 클라이언트에서 받은 url, coment를 이용해서 페이지 정보를 찾고 저장한다.

2) 리스팅 API - 저장된 카드를 보여준다

 

순서는 다음과 같이 진행된다.

1) 클라이언트와 서버 연결을 확인한다.

2) 서버부터  코드를 작성한다.

3) 클라이언트 코드를 작성한다.

4) 완성을 확인한다.

 

1) 클라이언트와 서버 연결을 확인한다.

서버의 코드를 먼저 확인하겠다.

## API 역할을 하는 부분
@app.route('/memo', methods=['POST'])
def saving():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결되었습니다!'})

서버에서 /memo로 POST 요청하는 것을 확인한다. 이후 sample_give를 받아 sample_receive에 저장한다.

클라이언트 코드도 확인해보자

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

postArticle()함수 안에 Ajax가 들어가 있다. /memo에 POST를 요청하고 있고 sample_give로 샘플데이터라는 값을 주고 있다. response를 받아서 알림창에 msg의 값이 뜨도록 설정해놓았다.

그리고 postArticle이란 함수가 어디 붙어있나 확인해보니 기사저장이라는 버튼에 onClick으로 실행하게 되어있는 것을 확인하였다.

그럼 전체적인 구동을 확인해보자.

기사저장 버튼을 누르면 postArticle함수가 작동해서 Ajax가 실행되면서 POST형식으로 데이터를 보내고 서버에서 데이터를 받고 잘 되었으면 return으로 jsonify를 돌려준다. 그러면 response로 받아 알림창으로 msg의 값인 POST 연결되었습니다!가 출력된다.

 

2) 서버부터  코드를 작성한다.

서버에서 해야 할 일은 클라이언트에서 데이터를 받아야 한다. 여기서는 url과 코멘트가 해당된다. 이렇게 받은 url로 크롤링하여 DB에 저장하는 것까지가 서버에서 해야 할 일이다.

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

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

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

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

    doc = {
        'title':title,
        'image':image,
        'desc':desc,
        'url':url_receive,
        'comment':comment_receive
    }

    db.articles.insert_one(doc)

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

클라이언트에서 받은 url과 comment를 저장한 다음 9챕터에서 만든 크롤링 코드를 아래에 붙여준다. 이후 doc에 객체로 정리하여 DB에 저장시킨다.

즉, url과 comment를 저장한 다음 받은 url로 가서 크롤링 후 doc에 필요한 데이터를 DB에 저장한다.

 

3) 클라이언트  코드를 작성한다.

클라이언트는 서버로 입력된 url과 comment를 넘겨야 한다. 그래서 코드에 이 둘이 있어야 한다.

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

                $.ajax({
                    type: "POST",
                    url: "/memo",
                    data: {url_give:url, comment_give:comment},
                    success: function (response) { // 성공하면
                        alert(response["msg"]);
                    }
                })
            }

먼저 입력된 url, comment를 가져와야하기 때문에 제이쿼리로 가져온 다음 Ajax를 이용해 서버로 넘겨준다.

이렇게 코드를 작성한 후 결과를 확인한다.

 

4) 완성을 확인한다.

코멘트 작성한 모습

코멘트를 작성했더니 알림창이 뜨면서 DB에 저장된 것을 확인할 수 있다. 그러나 문제는 작성한 글이 사라지지 않았다. 이는 화면이 새로고침되지 않았기 때문인데 이를 위해 코드를 추가했다.

window.location.reload()

이 코드를 클라이언트에서 alert가 끝나고 넣어줌으로써 입력창에 남아있던 글이 사라지게 된다.

결과를 확인해보니 알림창이 뜨고 확인을 누르면 화면이 새로고침 되었다.

이제 Robo 3T로 가서 DB를 확인해보자

잘못해서 같은 코멘트를 두 번 저장했다....

제대로 DB에 저장된 것을 확인할 수 있다.

 

 

 

11. [나홀로메모장] - GET연습(보여주기)

이번에는 리스팅API(저장된 카드 보여주기)를 만들어보겠다.

앞서 했던 챕터처럼 이번에도 같은 순서로 진행하겠다.

1) 클라이언트와 서버 연결 확인하기

2) 서버 코드 작성하기

3) 클라이언트 코드 작성하기

4) 완성 확인하기

 

1) 클라이언트와 서버 연결 확인하기

서버의 코드는 다음과 같다.

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

/memo에 GET방식으로 요청하고 있다.

클라이언트 코드는 다음과 같다.

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

/memo에다가 GET방식으로 작동하여 샘플데이터를 주고 있다.

이것이 제대로 작동한다면 클라이언트에서 주고 서버에서 받은 다음 response로 돌려주고 알림창에 GET 연결되었습니다!가 출력된다.

 

또한 showArticles는 로딩이 다 되면 실행하도록 해놓았다.

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

그래서 잘 연결이 되었다면 페이지에 들어가자마자 'GET 연결되었습니다!'라는 알림창이 뜨게 된다.

GET 알림창이 뜬다

 

2) 서버 코드 작성하기

이번에는 클라이언트에서 받을 것은 없다. 대신 가지고 있는 데이터를 전달해야 한다.

@app.route('/memo', methods=['GET'])
def listing():
    articles= list(db.articles.find({},{'_id':False}))
    return jsonify({'all_articles':articles})

DB에 저장된 데이터를 articles에 담아 response로 return해준다.

 

3) 클라이언트 코드 작성하기

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

가져갈 데이터가 없음으로 url에서 ? 뒤에 있는 것들을 지운다.

그리고 articles가 제대로 불러져오는지 확인하기 위해서 console.log를 이용해 확인한다.

제대로 받아오고 있다.

이제 이것들을 출력하기 위해서는 for을 이용해야 한다. 그래서 title, image, url, desc, comment를 출력할 수 있게 만든다.

            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]['comment']
                            let image = articles[i]['image']
                            let url = articles[i]['url']
                            let desc = articles[i]['desc']
                            let comment = articles[i]['comment']

그리고 반복문을 이용해서 카드를 작성해야 함으로 아래서 카드의 틀을 가져온 뒤 title, image, url, desc, comment를 넣어 작성한 다음 제이쿼리의 append를 이용하여 카드 위치에 삽입해준다.

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]['comment']
                            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)
                        }
                    }
                })
            }

이렇게 하면 모든 것이 완성되었다.

 

4) 완성 확인하기

결과물을 확인하기 위해서 마블의 블랙위도우의 url과 코멘트를 넣어 입력한 뒤 그 결과를 살펴보았다.

제대로 추가되었다.

 

우리가 예상한 결과대로 블랙 위도우가 추가된 것을 확인할 수 있었다.

 

 

* 배우면서 느낀점

그동안 프론트쪽만 공부해서 이 부분은 아예 지식이 없어서 어려움이 있었다. 그래서 메이킹 챌린지를 같이 하는 팀원에게 POST와 GET방식에 대해 질문하여 이해하는데 도움을 받았다.

반복작업을 하면서 이것을 어떻게 사용해야 할 지에 대해 감을 잡아가는 느낌이라 조금 더  연습하면 될 것 같다.

그리고 몇 가지 부분은 내가 따로 공부하여 파야할 부분도 보았다. 그 부분은 나중에 추가적으로 더 공부할 생각이다.

 

* 참고자료

제이쿼리를 할 때 어떤 때는 중괄호, 어떤 때는 소괄호를 쓰길래 찾다가 참고한 자료

https://daeguowl.tistory.com/151

 

javascript 중괄호 , 대괄호, 소괄호 사용 정리

대괄호 : [] , 중괄호 : {} , 소괄호 : () 대괄호 =>[] list를 나열 할 때 list의 인덱스를 지정해줄 때 ex) mise[0], mise[1] 중괄호 => {} dictionary를 만들 때 {'key': 'value'} style tag를 적용해줄 떄 ex)..

daeguowl.tistory.com

 

서버를 다른 곳에 연결 후 프로젝트를 바꿔 다시 연결해도 바뀐 프로젝트 페이지가 열리지 않을 때 참고한 자료

https://somjang.tistory.com/entry/Python-OSError-Errno-48-Address-already-in-use-Flask

 

[Python] OSError: [Errno 48] Address already in use ( Flask )

Flask로 API를 만들던 중 (venv) (base) DongHyunui-MacBook-Pro:ElasticsearchAPI donghyunjang$ python3 app.py * Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a deve..

somjang.tistory.com

 

728x90