wisePocket

[Flask] Flask framework 미니프로젝트(project pedia) 08 (Backend 리뷰 목록 보기 기능 구현 / DB연결) 본문

Python&Flask Tutorials, AWS EB/Flask_project_pedia

[Flask] Flask framework 미니프로젝트(project pedia) 08 (Backend 리뷰 목록 보기 기능 구현 / DB연결)

ohnyong 2023. 7. 13. 21:54

07번 글에서 POST 연결 기록하기 기능을 구현했으니 DB에 데이터가 누적되어 저장된다.
이제 해당 웹 페이지가 로드될 때 DB에 저장된 기록된 리뷰 목록을 GET 방식으로 불러와서 content 부분에 보여주고자 한다.
DB에 등록된 리뷰 영화의 사진(url을 통해 이미지로), 영화 제목, 영화 설명, 별점, 리뷰 내용을 받아서 View 페이지에 넣어주는 기능을 작성해야 한다.

#### 리뷰목록확인
- DB에 저장된 기록된 리뷰 데이터 받기(find(==read))
- 받은 데이터를 content 하단 부분에 4열 카드 형태로 출력

1. 데이터 명세

  • DB : MongoDB
  • Collection : movie
  • Document :
    • 영화 URL : 'url':'url_receive' / from Frontend #url-> formData 'url_give':'value'
    • 별점 : 'star':'star_receive' / from Frontend #star-> formData 'star_give':'value'
    • 리뷰 내용 : 'comment':'comment_receive' / from Frontend formData #comment-> 'comment_give':'value'
    • 영화 제목 : 'title':ogtitle / from URL <meta [property="og:title"]['content']> 'value'
    • 영화 설명 : 'desc':ogdesc / from URL <meta[property="og:description"]['content']> 'value'
    • 영화 이미지 URL : 'image':ogimage / from URL <meta[property="og:image"]['content']> 'value' 

2. 서버 만들기 및 MongoDB 연결

기록하기 POST 요청 부분을 작성하면서 서버 및 MongoDB 연결은 이미 작성되어 있다.

# 라이브러리 임포트
# Flask Framework
# view페이지 렌더링을 위한 render_template 메서드
# 요청 데이터에 접근 할 수 있는 flask.request 모듈
# dictionary를 json형식의 응답 데이터를 내보낼 수 있는 jsonify 메서드
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

...

# app이라는 메인 함수 
# if __name__ == "__main__" 의 의미는 메인 함수의 선언, 시작을 의미
# 이 파이썬 스크립트가 직접 실행될 때에는 main() 함수를 실행하라
if __name__ == '__main__':
    app.run('0.0.0.0', port=5001, debug=True)
# [POST-8] MongoDB사용을 위한 pymongo와 certifi 임포트
from pymongo import MongoClient
import certifi
# [POST-9] DB 커넥션 구성
ca = certifi.where()
client = MongoClient('mongodb+srv://<account>:test@cluster0.lu7mz8j.mongodb.net/?retryWrites=true&w=majority',tlsCAFile=ca)
db = client.dbsparta

3. 기능 구현을 위한 app.py 부분 수정 및 작성 DB find(Read) 작성

이전 테스트로 서버 시작 및 브라우저로 URL 입력하여 메인 View 페이지 요청에서 GET 요청, 응답 연결되는 것을 확인했기 때문에
우선 app.py부분부터 테스트 코드를 수정하여 원하는 기능을 구현하고자 한다.
개발 순서는 app.py 이후 html을 보며 js를 작성하는 것이 편하지만 요청, 응답, 데이터 흐름 순서는 별도로 표기해 두었다.
 
메인 View 페이지 요청에서 listing()의 fetch() 실행 이후

  • [기능 흐름 순서 2]
    • app.py에서 '/movie' URL의 GET 방식 요청에 대한 메서드로 접근
    • 이는 listing()라 선언된 함수를 실행하는데
    • pymongo의 find()를 통해 moviespedia라는 collection의 데이터 document 를 모두 찾아냄(읽기)

DB 찾기(읽기) 이후

  • [기능 흐름 순서 3]
    • 불러온 document들을 list() 클래스를 통해 리스트 객체로 변환
    • 리스트 객체를 all_movies라는 변수에 담음
    • all_movies 객체를 jsonify()에 의해 json 형식의 데이터로 변환(주어진 값과 대응하는 JSON 문자열) 후 'result'란 key에 넣어 반환
    • JavaScript로 이동
# [GET-0] CREATE 부분이 테스트 완료되어 DB에 자료가 추가되는 상황, READ로 View 페이지에 DB 데이터를 가져와서 보여주자.
# fetch('URL')부분, 반환값은 res로 전달.
# "localhost:5001/movie" URL GET방식 요청에 응답
@app.route("/movie", methods=["GET"])
def movie_get():
    # [GET-1] 필요한 데이터는? -> DB에서 API 데이터를 가져와야 한다.
    # READ(FIND)
    # 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
    all_movies = list(db.moviespedia.find({},{'_id':False}))
    # [GET-2] 가져온 데이터는? -> json으로 변환하여 반환 -> 프론트(js)로 이동
    return jsonify({'result':all_movies})

4. 기능 구현을 위한 script.js 부분 수정 및 작성

리뷰 목록 보기 기능은 View 페이지가 로드될 때 직후 보이도록 (document).ready()로 설계되었다.
이전 테스트를 통해 script.js에서 GET 요청을 시작하고 최종 응답을 받는 것까지 정상적으로 작동되는 것을 확인했다.
(document).ready()와 연결된 함수인 listing() 부분을 작성(수정)한다.
 
View 페이지인 index.html에서 id가 #cards-box인 div태그에 image(url로부터 이미지 img src="url")를 붙이고 자식 div내에 한 줄씩, title, desc, star, comment를 DB로부터 찾아 대입한 p*4 태그가 생성되어야 한다.

 
서버 시작 및 브라우저로 URL 입력하고 접속을 시작

  •  [기능 흐름 순서 1]
    • URL 요청으로 View 페이지가 반환(load)되면
    • JavaScript의 (document).ready()가 직후에 즉시 실행 됨
    • 이후 내부 listing() 함수 실행
    • listing() 내부 fetch()를 통해 '/movie' URL에 대한 GET 방식 요청 app.py로 이동
// [Read]
$(document).ready(function () {
    show_order();
});
function show_order() {
    fetch('/mars').then((res) => res.json()).then((data) => {

DB데이터 찾기(읽기) 이후(fetch() 이후. then() 절로 jsonify 변환 객체가 반환된 이후)

  • [기능 흐름 순서 4]
    • app.py 마지막에서 all_movies 객체를 jsonify()를 통해서 json 형식으로 변환된 데이터
    • 첫 번째 then()절의 res 인자으로 들어감
    • JavaScript로 다시 돌아와서 첫번째 then()으로 들어간 res는 *response.json()에 의해 Promise 객체로 변환
    • 해당 객체 데이터는 data라는 변수에 담기고
    • 리스트 형태의 data를 rows 변수에 담고
    • ($('#order-box'). empty(); 로 하드코딩된 테이블 부분을 비워줌)
    • 반복문을 통해 1렬씩 [0], [1],... [rows.length]까지 전체를 돌면서 아래 변수에 저장
    • 'image', 'title', 'desc', 'star', 'comment' key의 value값들이 image, title, desc, star, comment 변수에 담김
    • star는 숫자인데 ⭐모양으로 표현하고자 하기 때문에 repeat()을 사용하여 star숫자만큼 특수기호가 반복된 값을 star_image 변수에 한번 더 담음
    • temp_html에 위 변수 내용들을 ${title} 처럼 출력하는 div 태그 작성
    • index.html에 위 반복문의 결과인 임시 테이블 태그들이 한 줄씩 #cards-box라는 테이블에 반복문 결과인 테이블이 append 됨
    • (결과 View페이지에서는 DB에 저장된 주문 데이터들이 웹 페이지 하단에 목록 형태로 나타남)
// [Read]
$(document).ready(function () {
    listing();
});

function listing() {
    fetch('/movie').then((res) => res.json()).then((data) => {
        // json 형식으로 변환, 반환된 데이터가 res 인자로 들어옴
        // res.json()에 의해 Promise 객체로 변환되어 data에 저장
        // data 내용 테스트
        console.log(data)

        // data의 내용이 OpenAPI로부터 데이터 받는것과 동일
        // 리스트 형태의 data를 rows 변수에 담고
        let rows = data['result']
        console.log(rows)

        // 반복문 전에 하드코딩 부분 비워주기
        $('#cards-box').empty();
        // forEach 반복문을 통해
        rows.forEach((a) => {
            // 한줄씩 콘솔에 출력(브라우저 콘솔)
            console.log(a)

            // 리스트에 있는 key의 value들을 각 변수에 담기
            let image = a['image']
            let title = a['title']
            let desc = a['desc']
            let star = a['star']
            let comment = a['comment']
            console.log(image, title, desc, star, comment)
            
            // 별점은 value가 숫자로 되어있다. 별모양 이미지가 star숫자만큼 .repeat되는것으로 바꾸자.
            let star_image = '⭐'.repeat(star)

            // index.html에 위 변수들이 들어가도록 백틱 내 자리표시자${variable} 작성한 내용을 temp_html에 작성
            let temp_html = `<div class="col">
                                <div class="card h-100">
                                    <img src="${image}"
                                        class="card-img-top">
                                    <div class="card-body">
                                        <h5 class="card-title">${title}</h5>
                                        <p class="card-text">${desc}</p>
                                        <p>${star_image}</p>
                                        <p class="mycomment">${comment}</p>
                                    </div>
                                </div>
                            </div>`
            // 위 temp_html을 index.html의 #cards-box div에 붙여주기.
            $('#cards-box').append(temp_html)
        });
    })
}

5. 테스트

  • Flask 서버가 실행 중인 상태
  • 브라우저에서 localhost:5001 URL로 접속
  • 웹 페이지 로드 직후( (document).ready() 즉시 실행 )
  • 테스트로 insert 했던 2개의 DB 데이터
  • MongoDB내 moviespedia라는 collection에 테스트 데이터를 불러와지고(Find) content에 목록으로 나열됨


해당 프로젝트는 아래 깃을 통해 업데이트되고 있습니다.

https://github.com/yzpocket/Flask_project_pedia

 

GitHub - yzpocket/Flask_project_pedia: [Flask] Flask framework 미니프로젝트(project pedia)

[Flask] Flask framework 미니프로젝트(project pedia). Contribute to yzpocket/Flask_project_pedia development by creating an account on GitHub.

github.com