[웹개발 종합반] 4주 2일차 개발일지(4주 6-9강)


💻TIL Web.402

‘모두의 책리뷰’ 프로젝트

1. 뼈대 준비하기

bookreview 폴더에 templates, static 폴더와 app.py 파일을 생성한 후, templates 폴더에 index.html 파일까지 만든다. 또한, app.pyindex.html 파일에 아래 시작 코드를 붙여넣는다.

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)


<!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://i.pinimg.com/originals/71/b8/f0/71b8f0548e938fbffb62e1e69b834f9e.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>
</html>


2. 리뷰 저장하기(POST)

💡API 만들고 사용하기 순서

① 클라이언트/서버 연결 확인하기
② 서버 만들기
③ 클라이언트 만들기
④ 완성 확인하기

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

# app.py
@app.route('/review', methods=['POST'])
def write_review():
  sample_receive = request.form['sample_give']
  print(sample_receive)
  return jsonify({'msg': '이 요청은 POST!'})

서버 쪽에 만들어둔 창구는 위와 같다. ‘/review’라는 url 주소로 POST 요청을 받으며, sample_give라는 변수 이름으로 데이터를 받고 있다. 받은 데이터를 sample_receive 변수에 저장해 출력하고, ‘이 요청은 POST!’라는 메세지를 내려준다.


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

...

<body>
  <div class="review">
    <button onclick="makeReview()" type="button" class="btn btn-primary">리뷰 작성하기</button>
  </div>
</body>

클라이언트 쪽으로 가보면, 이 sample_give 변수는 makeReview라는 함수에서 불리고 있다. data에서 ‘샘플데이터’라는 데이터를 가지고 가는 모습도 볼 수 있다. 이렇게 데이터를 잘 가지고 갔으면 responseapp.py에서 write_review 함수가 반환한 {'msg': '이 요청은 POST!'}가 넘어오게 된다. 다시 위 코드를 보면, response["msg"]alert 창으로 띄우므로 ‘이 요청은 POST!’ 메세지가 뜨게 된다. HTML 코드 아래쪽을 보면, 이 makeReview() 함수는 ‘리뷰 작성하기’ 버튼에 붙어 있다. 즉 이 버튼을 누를 때마다 Ajax 콜이 동작한다는 의미다. 이렇게 서버와 클라이언트가 연결되었다.


2) 서버 만들기

이제 서버 쪽을 살펴보자. 입력 창에서는 제목, 저자, 리뷰 데이터가 들어올 테니 해당 데이터를 받아서 DB에 저장해 주어야 한다. 위에서 request.form을 사용해 데이터를 받았으니 이를 그대로 응용하면 된다.

데이터를 받아 이를 DB에 저장하려면 pymongo에서의 데이터 저장(insert), 탐색(find), 업데이트(update), 삭제(delete)를 이용하면 된다. 입력받은 데이터를 저장하는 것이므로 insert를 이용한다. 예시 코드는 아래를 참고하자. insert 부분을 보면 doc라는 딕셔너리를 만든 후 이를 users라는 컬렉션에 저장하고 있다.

# 저장 - 예시
doc = {'name':'bobby','age':21}
db.users.insert_one(doc)

# 한 개 찾기 - 예시
user = db.users.find_one({'name':'bobby'})

# 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
same_ages = list(db.users.find({'age':21},{'_id':False}))

# 바꾸기 - 예시
db.users.update_one({'name':'bobby'},{'$set':{'age':19}})

# 지우기 - 예시
db.users.delete_one({'name':'bobby'})


같은 방식으로 입력받은 데이터 변수를 속성값으로 갖는 딕셔너리 변수를 만들고, 이를 bookreview 컬렉션에 저장한다.

# app.py
@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)

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


3) 클라이언트 만들기

서버에서 각각 제목, 저자, 리뷰 데이터를 받아 DB에 저장한다면, 클라이언트 쪽에서는 각 데이터를 가져와서 서버로 보내줘야 한다. jQuery의 val()을 사용하면 input 박스의 값을 가져올 수 있으므로, 아래와 같이 변수를 설정한 후 Ajax로 데이터를 서버로 보낸다.

function makeReview() {
  // 각 input 박스의 아이디 입력
  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"]);
      // 새로고침(refresh)
      window.location.reload();
    }
  })
}


4) 완성 확인하기

아직 쓰여진 리뷰를 페이지에서 확인할 수는 없지만, 받은 데이터가 DB에 저장된 모습을 Robo 3T에서 확인할 수 있다.


3. 리뷰 보여주기(GET)

GET 요청도 POST 요청과 만드는 방법은 동일하다.

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

‘/review’ 주소에 ‘GET’ 요청을 받는 API(창구)로, sample_give라는 변수명으로 데이터를 받고 있다.


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

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

클라이언트 사이드인 HTML 페이지를 보면, 똑같이 ‘/review’ URL에 데이터를 요청하며, ?key=value 형태로 데이터를 가져가고 있다. GET 요청이므로 data는 빈 상태다. 데이터를 잘 받은 경우 response를 받아 response["msg"]를 alert창에 띄운다. 이 showReview()는 페이지가 로딩되자마자 불리는 함수다. ‘localhost:5000’에 접속하면 alert창이 뜨는 것으로 서버와 클라이언트가 연결되었음을 확인할 수 있다.

index.html파일을 바로 실행시키면 웹 문서만 보인다. 클라이언트 페이지를 서버와 연동하려면 app.py를 실행(Ctrl + Shift + F10)한 다음 ‘localhost:5000’으로 접속해야 한다.



2) 서버 만들기

GET 요청에서, 서버는 클라이언트로부터 받아올 데이터가 없으므로 변수를 설정할 필요가 없다. 단순히 POST 요청에서 DB에 저장되었던 데이터를 가져와서 그대로 내려주면(return) 된다.


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

리뷰를 여러 개 보여주기 때문에 pymongo에서의 find를 이용한다. 조건 없이 모든 데이터를 가져오는 방식을 사용한다. POST 요청에서와 마찬가지로 예시 코드를 참고한다. 이 요청에서는 msg를 내려줄 필요가 없기 때문에, 데이터를 담은 변수 reviews를 반환한다.



3) 클라이언트 만들기

클라이언트 쪽에서는, 서버에서 all_reviews가 리스트 형태로 잘 내려오면(success) 이를 for문을 사용해 붙여주면 된다. 클라이언트가 가져갈 데이터는 없으므로 url에서 ?key=value 부분은 지워준다.

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

위 코드와 같이 reviews라는 새로운 변수를 설정하고 서버에서 내려준 데이터를 가져온다. 먼저 데이터가 잘 전달되었는지 확인하기 위해 console.log로 체크해 본다. ‘localhost:5000’ 페이지의 Console 창을 열면, 서버에 저장된 리스트(Array)가 출력되어 있는 것을 볼 수 있다. 새로운 데이터를 입력한 후 다시 Console 창을 열어보면 Array의 element가 하나 늘어나 있다.



4) 완성 확인하기

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)

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

위와 같이 for 문을 작성하고, 변수를 설정한 다음 먼저 console.log로 데이터를 확인하는 과정을 거친다. 그 다음, temp_html을 이용해 각각의 데이터를 붙여줄 HTML 태그를 작성한다. 페이지 아래쪽에 예시 코드가 있으므로 이를 그대로 가져온 다음 append로 붙여넣는다. 이제 입력창에 제목, 저자, 리뷰를 작성하고 ‘리뷰 작성하기’ 버튼을 누르면 아래쪽에서 입력한 데이터를 바로 조회할 수 있다.


Categories:

Updated:

Leave a comment