스파르타 코딩클럽 8기) 7주차-프로젝트 개발일지3

Mongle·2020년 6월 22일
0

먹방구독서비스(Web)

목록 보기
10/11
post-thumbnail

🦁 핵심기능 완성!!

이번 프로젝트의 핵심 기능을 완성했다!!
저번 주에 고비라고 생각했던 부분도 생각보다 쉽게 해결되었다. 고민했던 시간이 무색할 정도로. 고민하는 과정에서 enumerate 함수를 알아보고, 활용해봤다는 점에서 위안을 얻으려한다. 이번에는 사용하지 않았지만 알고있으면 분명 도움이 될 만한 함수이기 때문에 본문에 간단한 설명을 첨부했다.

D-6 추가된 기능

  • 유튜브 영상 DB구축(python, mongodb)
  • DB 정보 화면에 출력(html, js, ajax)
  • bootstrap collapse 적용
  • 웹페이지 디자인(html,css)

👉 mongodb 구축

youtube.py

from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.tools import argparser
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbproject



DEVELOPER_KEY = ""
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

def youtube_search():
  youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
    developerKey=DEVELOPER_KEY)

  
  search_response = youtube.search().list(
    q = '입짧은햇님',
    order = "date",
    part = "snippet",
    maxResults = 50
    ).execute()
  
  # print(search_response)
  
  videos = []
  descriptions = []
  thumbnails = []
  channelTitles = []
  publishTimes = []
  videoId = []

  
  for search_result in search_response.get("items", []):
    if search_result["id"]["kind"] == "youtube#video":
      videoId.append("%s" % (search_result["id"]["videoId"],
                                  ))
      videos.append("%s" % (search_result["snippet"]["title"],
                                 ))
      descriptions.append("%s" % (search_result["snippet"]["description"],
                                 ))
      thumbnails.append("%s" % (search_result["snippet"]["thumbnails"]["medium"]["url"],
                                  ))
      channelTitles.append("%s" % (search_result["snippet"]["channelTitle"],
                                   ))
      publishTimes.append("%s" % (search_result["snippet"]["publishTime"],
                                  ))
      
  print("\n\nVideoId:\n\n", "\n".join(videoId), "\n")
  print("\n\nVideos:\n\n", "\n".join(videos), "\n")
  print("\n\ndescription:\n\n", "\n".join(descriptions), "\n")
  print("\n\nthumbnails:\n\n", "\n".join(thumbnails), "\n")
  print("\n\nchannelTitles:\n\n", "\n".join(channelTitles), "\n")
  print("\n\npublishTimes:\n\n", "\n".join(publishTimes), "\n")
  
  
  for i in range(45) :
    doc = {
      'videoId' : videoId[i],
      'title' : videos[i],
      'desc' : descriptions[i],
      'img_url' : thumbnails[i],
      'channelTitle' : channelTitles[i],
      'publishTime' : publishTimes[i]
     }
    db.HaetNim.insert_one(doc)
    #이렇게 간단한 문제를 enumerate함수로 해결하려하다니...


if __name__ == "__main__":
  argparser.add_argument("--q", help="Search term", default="Google")
  argparser.add_argument("--max-results", help="Max results", default=25)
  args = argparser.parse_args()

try:
  youtube_search()
except HttpError as e:
  print("An HTTP error %d occurred:\n%s"% (e.resp.status, e.content))

youtube api를 이용하여 mongodb에 원하는 정보를 저장해 주는 과정이 끝났다.

🧐 유튜브api 코드를 보면서 알게된 사소한 꿀팁,

유튜뷰는 모든 채널과 영상 하나하나에 고유한 코드를 부여한다. 이 코드를 이용해서 url을 통해 해당 영상 혹은 채널로 이동할 수 있다.
ex) "https://www.youtube.com/watch?v=${videoId}"

db에 영상 정보가 저장되어 있는 것을 확인할 수 있다.


👉 DB정보 화면에 출력하기

먼저 서버(app.py)에서 db에게 정보를 달라고 요청해야한다.

app.py

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

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

#템플릿을 불러오는 api
@app.route('/list')
def list_home():
  return render_template('list.html')

#db에서 가져온 데이터를 화면에 넘겨주는 api
@app.route('/list/getVideo' ,methods=['GET'])
def get_videos():

  videos = list(db.HaetNim.find({},{'_id':0}))

  return jsonify({'result': 'success','videos_list':videos})

서버에서 먼저 db에게 정보를 얻어와서 return jsonify()로 정보를 넘겨준다. list에 담아서 보내주었다. 주의할 점은 render_template을 위한 api와 기능이 다르기 때문에 기존의 api를 그대로 두고 정보를 넘겨줄 새로운 api를 생성해야한다.


이제 javascript, ajax, html을 이용해서 화면에 영상정보를 출력할 수 있다.

list.html

$(document).ready(function() {
        show_videos();
      });
      
      function show_videos(){
       
        $('#video-box').empty()
    	// 태그 비워주기
        $.ajax({
            type: "GET",
            url: "/list/getVideo",
            data: {},
            success: function (response) {
                
                let videos = response['videos_list']
                //db->app.py에서 가져온 변수 저장
                for (let i = 0; i < videos.length; i+=3) {
                    let video1 = videos[i]
                    let video2 = videos[i+1]
                    let video3 = videos[i+2]
                    
                    let videoId1 = video1['videoId']
                    let title1 = video1['title']
                    let desc1 = video1['desc']
                    let img_url1 = video1['img_url']
                    let channelTitle1 = video1['channelTitle']
                    let publishTime1= video1['publishTime']
                    
                    let videoId2 = video2['videoId']
                    let title2 = video2['title']
                    let desc2 = video2['desc']
                    let img_url2 = video2['img_url']
                    let channelTitle2 = video2['channelTitle']
                    let publishTime2= video2['publishTime']

                    let videoId3 = video3['videoId']
                    let title3 = video3['title']
                    let desc3 = video3['desc']
                    let img_url3 = video3['img_url']
                    let channelTitle3 = video3['channelTitle']
                    let publishTime3 = video3['publishTime']
      
                    //html에 붙일 card-deck 만들기
                    let temp_html = ` <div class="card-deck">
                                      <div class="card">
                                      <img class="card-img-top" src="${img_url1}" alt="Card image cap">
                                      <div class="card-body">
                                        <a href="https://www.youtube.com/watch?v=${videoId1}"><h5 class="card-title">${title1}</h5></a>
                                        <p class="card-text">${desc1}</p>
                                        <p class="card-text"><small class="text-muted">${channelTitle1}</small></p>
                                        <p class="card-text"><small class="text-muted">${publishTime1}</small></p>
                                        <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@mdo">보관함에 저장</button>
                                      </div>
                                    </div>
                                    
                                    <div class="card">
                                      <img class="card-img-top" src="${img_url2}" alt="Card image cap">
                                      <div class="card-body">
                                        <a href="https://www.youtube.com/watch?v=${videoId2}"><h5 class="card-title">${title2}</h5></a>
                                        
                                        <p class="card-text">${desc2}</p>
                                        <p class="card-text"><small class="text-muted">${channelTitle2}</small></p>
                                        <p class="card-text"><small class="text-muted">${publishTime2}</small></p>
                                        <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@mdo">보관함에 저장</button>
                                      </div>
                                    </div>
                                    
                                    <div class="card">
                                      <img class="card-img-top" src="${img_url3}" alt="Card image cap">
                                      <div class="card-body">
                                        <a href="https://www.youtube.com/watch?v=${videoId3}"><h5 class="card-title">${title3}</h5></a>
                                        
                                        <p class="card-text">${desc3}</p>
                                        <p class="card-text"><small class="text-muted">${channelTitle3}</small></p>
                                        <p class="card-text"><small class="text-muted">${publishTime3}</small></p>
                                        <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@mdo">보관함에 저장</button>
                                      </div>
                                    </div>
                                    </div><br>`
      
                    // 7. #star-box에 temp_html을 붙입니다.
                    $('#video-box').append(temp_html)
                }
            }
        });
      }

굉장히 부끄러운 코드다.
변명을 하자면, 부트스트랩의 card-deck의 특성상 영상정보를 3개씩 가져와야 했고, 일단 완성이 목표였기 때문에 영상 세 개의 변수명을 각각 다르게 지정해서 값을 저장하는 방법을 택했다. 하지만 반복문을 조금만 이용하면 훨씬 간결한 코드로 바꿀 수 있을 것이다.

ready함수에 show_videos함수를 추가를 안해서 한참 헤멨다. 이유없이 화면이 뜨지 않는다면 app.py가 아니라 ready함수를 살펴보자.

👇완성


영상제목을 클릭하면 해당 영상으로 이동한다.
매주 다른 먹방DJ가 업데이트되어 관련 유튜브 영상이 화면에 출력되는 기능이 완성되었다.


👉 collapse 기능 추가


영상 밑에 '메모 열기'라는 버튼을 만들어주고,

<button class="btn btn-info" type="button" data-toggle="collapse" data-target="#multiCollapseExample2" aria-expanded="false" aria-controls="multiCollapseExample2">메모 열기</button>

메모를 열면 버튼 아래쪽에 db에서 가져온 정보를 보여줄 수 있다.

mypage.html

<div class="row">
        <div class="col">
          <div class="collapse multi-collapse" id="multiCollapseExample1">
            <div class="card card-body">
              오늘뭐먹지식당/
              신촌,홍대/
              한식
              <br><br>
              오늘은 왠지 집밥이 땡긴다.. 여기 가면 이걸 꼭 먹어야한다. 그런 아무말을 적어주시면 됩니다.<br>
            </div>
          </div>
        </div>
        <div class="col">
          <div class="collapse multi-collapse" id="multiCollapseExample2">
            <div class="card card-body">
              Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident.
            </div>
          </div>
        </div>
        <div class="col">
            <div class="collapse multi-collapse" id="multiCollapseExample3">
              <div class="card card-body">
                Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident.
              </div>
            </div>
          </div>
      </div>

세로로 한 줄, 가로로 세 줄로 구성된 collapse를 만들었다.

🧐 더 자세한 정보는 부트스트랩_collapse를 참고


👉 웹페이지 디자인


튜터님께서 꼭 먹방에 관련된 이미지를 넣을 것을 추천해주셔서 웹페이지 디자인을 시작했다.

음식사진이 필요하다면 foodiespeed에서 찾으면 된다. 무료로 음식 사진을 사용할 수 있는데 사진 퀄리티가 엄청나다.👍

화면에 들어있던 버튼과 잔 기능들을 없애고 강한 이미지에 집중했다. 역시 음식은 보기 좋은 것이 맛도 좋다.

🧐 hex코드와 투명도 조절 꿀팁

사진 위에 글씨를 넣을 때 사진과 글씨를 모두 살리는게 쉽지 않다. 특히 디알못이라면 더더욱..!!
hex코드투명도에 대해서 알고있으면 그럴듯하게 괜찮은 이미지를 만들 수 있다.

.display-4{
        background-color: #D6D6D6;
        background-color: rgba( 255, 255, 255, 0.5 )#투명도조절
      }

👉 enumerater()

'낱낱히 세다'라는 의미를 가진 enumerate 함수는 인덱스 번호와 컬렉션의 원소를 tuple형태로 반환한다.

a = ['mong','song','hong']
b = list(enumerate(a))
c = dict(enumerate(a))
print(b)
print(c)

#결과

[(0, 'mong'), (1, 'song'), (2, 'hong')]

{0: 'mong', 1: 'song', 2: 'hong'}

주로 for문과 함께 사용된다.

a = ['mong','hong','song']
b = []
c = {}
for i,name in enumerate(a):
    b.append((i,name))
    c[i] = a[name]
print(b)
print(c)

#결과

[(0, 'mong'), (1, 'hong'), (2, 'song')]

{0: 'mong', 1: 'hong', 2: 'song'}

🧐 python에서 range 대신 enumerate를 사용하는 이유는

  • range()는 컬렉션의 길이를 알아내야하고
  • 컬렉션에 접근을 인덱스값을 이용해서 해야한다.
  • enumerate()를 사용하면 보다 간단하게 코드를 짤 수 있다.

✍todo list

  • 보관함 UI 만들기
  • DB에 유튜브 데이터 넣기
  • 영상을 어떻게 보여줄지 (youtube영상url로 이동)
  • 보관함관련 api만들기
  • modal로 받은 정보 db에 저장하고 웹페이지에 띄워주기
  • 폰트 입히기

🦁 마치며...

저번 주에 기술적 한계로 영상 검색 기능을 제외하고 서비스 제공자가 미리 정해둔 양의 정보를 사용자에게 보여주는 방향으로 계획을 바꿨다.

그러는 과정에서 아이템 변경에 대해 생각했다. 사용자에게 원하는 영상을 찾으라고 하는 것 보다 서비스를 제공하는 과정에서 퀄리티있는 먹방만을 추려서 사용자에게 제공하는 먹방 구독서비스에 대해 가능성을 열어두게 되었다.

프로젝트를 만드는 과정에서 기획아이템이 바뀌는 것이 바람직한 것인지는 의문이 들지만 일단 할 수 있는 것은 다 해봐야겠다!

profile
https://github.com/Jeongseo21

0개의 댓글