스파르타 코딩클럽 8기) 8주차 - 프로젝트 완성!!

Mongle·2020년 6월 30일
0

먹방구독서비스(Web)

목록 보기
11/11
post-thumbnail

👀 프로젝트 영상 보기

일단 영상부터 보고 옵시다!!

👉👉👉🍕먹방구독서비스 클릭!👈👈👈


추가된 기능

👉 api 설계

app.py

#필요한 패키지를 설치하고 import합니다.
from pymongo import MongoClient
from flask import Flask, render_template,jsonify,request
from datetime import datetime, timedelta
app = Flask(__name__)

#ubuntu를 통해 서버에 올렸음
client = MongoClient('mongodb://...',27017)
db = client.dbproject

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

@app.route('/list')
def list_home():
  return render_template('list.html')

@app.route('/mypage')
def mypage_home():
  return render_template('mypage.html')
  
 @app.route('/community')
def community_home():
  return render_template('community.html')

#mypage의 memo를 db에서 가져오는 api
@app.route('/mypage/getMemo' ,methods=['GET'])
def get_Memo():

  memos = list(db.mypage.find({},{'_id':0}).sort('created_at',-1))
  print(memos)
  return jsonify({'result': 'success','memos_list':memos})

#영상 리스트를 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})

#list페이지에서 영상관련 정보를 사용자로부터 받을 후 db에 저장하는 api
@app.route('/list/saveMemo', methods=['POST'])
def saveMemo():
  title_receive = request.form['title_give']
  thumbnail_receive = request.form['thumbnail_give']
  videoId_receive = request.form['videoId_give']
  restaurant_receive = request.form['restaurant_give']
  food_catg_receive = request.form['food_catg_give']
  location_receive = request.form['location_give']
  memo_receive = request.form['memo_give']

  doc = {
    'title': title_receive,
    'thumbnail': thumbnail_receive,
    'videoId': videoId_receive,
    'restaurant': restaurant_receive,
    'food_catg': food_catg_receive,
    'location': location_receive,
    'memo' : memo_receive,
    'created_at': datetime.now()
  }
  db.mypage.insert_one(doc)
  
  return jsonify({'result':'success', 'msg':'내 보관함에 저장 완료'})


#사용자가 선택한 영상의 title정보를 받아와서 modal에 넣어주기위한 api
@app.route('/list/sendTitle', methods=['POST'])
def sendTitle():
  title_receive = request.form['title_give']
  
  title_one = list(db.HaetNim.find({'title':title_receive},{'_id':0}))

  return jsonify({'result':'success', 'title_one':title_one})
  
#커뮤니티에서 사용자가 글을 작성하면 db에 저장하는 api
@app.route('/message', methods=['POST'])
def set_message():
  username_receive = request.form['username_give']
  contents_receive = request.form['contents_give']

  doc = {
    'username': username_receive,
    'contents': contents_receive,
    #현재 시각도 db에 저장
    'created_at': datetime.now()
  }
  db.messages.insert_one(doc)

  return jsonify({'result':'success', 'msg':'메세지 작성을 완료하였습니다.'})

#24시간이 지나면 글을 화면에 출력하지 않게 하는 api
@app.route('/message')
def get_messages():
  #현재 시각 구하기
  date_now = datetime.now()
  #24시간 전 구하기
  date_before = date_now - timedelta(days=1)
  messages = list(db.messages.find({'created_at':{'$gte':date_before, '$lte':date_now}},{'_id': 0}).sort('created_at',-1))

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

# 글 수정 api
@app.route('/message/edit', methods=["POST"])
def edit_message():
  username_receive = request.form['username_give']
  contents_receive = request.form['contents_give']
  db.messages.update_one({'username':username_receive},
                        {'$set': {'contents':contents_receive, 'created_at':datetime.now()}})
  
  return jsonify({'result':'success', 'msg':'메세지 변경에 성공하였습니다'})

#글 삭제 api
@app.route('/message/delete', methods=["POST"])
def delete_message():
  username_receive = request.form['username_give']
  
  db.messages.delete_one({'username':username_receive})
  
  return jsonify({'result':'success', 'msg':'메세지 삭제에 성공하였습니다'})

#mypage의 내 보관함의 영상메모 삭제 api
@app.route('/mypage/deleteMemo', methods=["POST"])
def delete_memo():
  memo_receive = request.form['memo_give']
  
  db.mypage.delete_one({'memo':memo_receive})
  
  return jsonify({'result':'success', 'msg':'메모 삭제에 성공하였습니다'})


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

이렇게 api를 구성해보았다.
api의 개수가 많아지다보니 구조가 한눈에 들어오지 않는 문제가 생겼다.
앞부분에 html template을 불러오는 get메소드 api를 설계하고, 기능을 담당하는 부분은 연관성있는 url끼리 묶어서 정리하려 해 보았지만 깔끔하게 정리된 것 같지는 않다.
다음 프로젝트에서는 프로젝트를 기획하는 단계에서부터 이 부분을 신경써서 설계해야겠다


👉 메모 저장 기능

글을 쓰다보니 길어져서 편의를 위해 번호를 붙였다.

  1. 먼저 간단한게 버튼을 누르면 메모를 작성할 수 있는 textarea가 열릴 수 있도록 만들었다.
function openclose() {
    let status = $('#post-box').css('display');
    if (status === 'block') {
        $('#post-box').hide
    } else {
        $('#post-box').show();
    }
}
  1. 다음으로, 사용자가 작성한 contents가 공백이거나 500자가 넘는지 확인하고 두 경우에 해당할 때는 false를 리턴해서 글 작성을 막아놓았다.
function isValidContents(contents) {
    if (contents == '') {
        alert('내용을 입력해주세요');
        return false;
    }
    if (contents.trim().length > 500) {
        alert('공백 포함 500자 이하로 입력해주세요.');
        return false;
    }
    return true;
}
  1. 랜덤으로 유저이름을 생성해주었다. 이 이름으로 메모를 db에 넣은 후에도 각각의 메모를 username으로 구분해줄 수 있게 되었다. 랜덤함수만들기는 프로그래밍 언어공부를 할때 항상 만들던 로직이었는데 이렇게 프로젝트에서 사용할 수 있어서 뿌듯하다.😁
function randomName(length) {
    let result = '';
    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
    let charactersLength = characters.length;
    //길에에 맞게 문자열 만들기
    for (let i = 0; i < length; i++) {
        //0보다 같거나 크고 chractersLength보다 짧은 문자열 만들기
        let number = Math.random() * charactersLength;
        //number를 정수로 만들기
        let index = Math.floor(number)
        //index 위치의 문자열 하나를 result에 더하기
        result += characters.charAt(index);
    }
    return result; //result를 반환한다.
}
  1. 이제 사용자로부터 받아온 contents를 서버에 보내서 서버가 db에 저장할 수 있도록 해 주었다. username과 함께 보내서 메모의 수정 및 삭제가 가능하게 했다.
function writePost() {
    let contents = $('#contents').val();

    if (isValidContents(contents) === false) {
        return;
    }

    let username = randomName(6);

    $.ajax({
        type: "POST",
        url: "/message",
        data: { 'username_give': username, 'contents_give': contents },
        success: function (response) {
            if (response['result'] == 'success') {

                window.location.reload();
            }
        }
    })
}
  1. 이제 서버로부터 받아 온 정보를 화면에 출력해주기 위해서 html형식으로 card-box에 append해준다.
//cards-box에 html붙이는 함수
function addHTML(username, contents, created_at) {
    let tempHtml = makeMessage(username, contents, created_at)
    $('#cards-box').append(tempHtml);
}
//붙일 html만들기
function makeMessage(username, contents, created_at) {
    return `<div class="card custom-card">
            <div class="card-body">
                <textarea id="${username}-textarea" class="area-edit" cols="80"></textarea>
                <h5 id="${username}-contents" class="card-title">${contents}</h5>
                <br>
                <h6 id="${username}-username"class="card-subtitle mb-2 text-muted">#익명(${username})</h6>
                <h6 class="card-subtitle mb-2 text-muted">#${created_at}</h6>
            </div>
            <footer class="card-footer">
              <div class="card-footer-footer" align="right">
                <a id="${username}-edit" href="#" class="card-footer-item"token interpolation">${username}')">수정</a>&nbsp &nbsp &nbsp 
                <a id="${username}-delete" href="#" class="card-footer-item"token interpolation">${username}')">삭제</a>
              </div>
                <a id="${username}-cancel" href="#" class="card-footer-item area-edit"token interpolation">${username}')">취소</a></a>&nbsp &nbsp &nbsp 
                <a id="${username}-submit" href="#" class="card-footer-item area-edit"token interpolation">${username}')">수정완료</a>
              
            </footer>
        </div>`
}
  1. 글 수정을 위한 메서드이다. 수정버튼을 누르면 서로 다른 버튼을 보여줘야해서 show, hide 함수를 따로 만들어주었다. submit함수를 이용해서 수정된 contents를 다시 서버로 보내주고 서버에서 db에 저장한다.
function editPost(username) {
    showEdits(username);
    let contents = $(`#${username}-contents`).text();
    $(`#${username}-textarea`).val(contents);
}
function showEdits(username) {
    //수정 입력칸, 수업완료버튼, 취소 버튼 보여주기
    $(`#${username}-textarea`).show();
    $(`#${username}-submit`).show();
    $(`#${username}-cancel`).show();

    //메모 내용, 수정 버튼 숨기기
    $(`#${username}-contents`).hide();
    $(`#${username}-edit`).hide();
}

function hideEdits(username) {
    // 수정 입력 칸, 수정완료 버튼, 취소 버튼을 숨기기
    $(`#${username}-textarea`).hide();
    $(`#${username}-submit`).hide();
    $(`#${username}-cancel`).hide();

    // 메모 내용, 수정 버튼을 보여주기
    $(`#${username}-contents`).show();
    $(`#${username}-edit`).show();
}

function submitEdit(username) {
    // 수정된 메모 내용을 가져오고 올바른지 검사
    let contents = $(`#${username}-textarea`).val();
    if (isValidContents(contents) == false) {
        return;
    }
    $.ajax({
        type: "POST",
        url: "/message/edit",
        data: { username_give: username, contents_give: contents },
        success: function (response) {
            if (response['result'] == 'success') {

                // 변경된 메모를 보기 위해 창을 새로고침
                location.reload();
            } else {
                alert('메시지 변경에 실패했습니다.');
            }
        }
    });
}
  1. 메모를 삭제하기 위한 메서드이다. 삭제할 글의 username을 서버로 보내서 해당 글을 삭제한다.
function deletePost(username) {
    let delete_one = $(`#${username}-delete`).text();
    alert("삭제하시겠습니까?")
    $.ajax({
        type: "POST",
        url: "/message/delete",
        data: { username_give: username },
        success: function (response) {
            if (response['result'] == 'success') {
                // 변경사항 저정, 새로고침
                location.reload();
            } else {
                alert('메시지 삭제에 실패했습니다.');
            }
        }
    });
}

👉 보관함 저장 기능

메모저장기능을 자세히 다루었기 때문에 보관함 저장 기능은 핵심 내용만 포스팅하도록 하겠다.

😤보관함 저장 기능을 만들 때 정말 많은 좌절을 경험했다.

메모 기능보다 먼저 구현을 시작했는데 자바스크립트와 ajax 문법에 미흡한 상태로 바로 백엔드 로직 구현에 들어갔기 때문에 수정, 삭제 기능을 만드는 데 진행이 아주 아주 아주 더뎠고 결국 밤을 새워 생활코딩 자바스크립트 강의를 완강하게 만들었다.

생활코딩 자바스크립트 js알못에게 빛과 소금같은 강의였다. 추천👍

나를 괴롭혔던 코드가 바로 👇 얘다.
list.html

onclick="savePost('${title1}')"

layout.js

function savePost(title) {
    //title을 받아왔음, ajax로 해당 영상 정보 가져오기
    $('#title_save').empty();

    $.ajax({
        type: "POST",
        url: "/list/sendTitle",
        data: { 'title_give': title },
        success: function (response) {
            if (response['result'] == 'success') {
                let titles = response['title_one'];

                for (let i = 0; i < titles.length; i++) {
                    let title_one = titles[i];
                    let title = title_one['title'];
                    let thumbnail = title_one['img_url']
                    let videoId = title_one['videoId']

                    let temp_html = `<label for="restaurant" class="col-form-label">영상 이름 : </label>
              <input type="text" class="form-control" name="title" value="${title}" readonly>

              <label for="restaurant" class="col-form-label">영상 썸네일 : </label>
              <input type="text" class="form-control" name="thumbnail" value="${thumbnail}" readonly>

              <label for="restaurant" class="col-form-label">영상 Id : </label>
              <input type="text" class="form-control" name="videoId" value="${videoId}" readonly>`

                    $('#title_save').append(temp_html);

                }

            }
        }
    })

}

지금 보면 왜 그렇게 헤멨을까 싶지만, save()에 매개변수로 영상의 title을 넘겨줌으로써 영상 하나하를 특정할 수 있다는 생각을 하지 못했을 때에는 모든 영상을 첫번째 영상으로 인식하는 문제가 있었다.😥

🧐 결국 메모 기능을 구현하면서 함수에 매개변수를 지정함으로써 영상을 특정할 수 있다는 것을 떠올릴 수 있었다. 해결방법이 생각나지 않을 때는 다른 기능을 먼저 구현하거나 다른 사람들의 코드를 살펴보는 것이 큰 도움이 된다!!

한 가지 아쉬운 점은 영상title이 아니라 videoId(유튜브에서 제공하는 영상 고유번호)로 줬으면 더 좋았을 것 같다.

마무리...

😁나는 꼭 개발자가 되어야갰다!!😁
프로젝트가 끝나고나니 꼭 내 분신을 하나 만든 것 같다. 살면서 이렇게 시간이 쏜살같이 지나간 적이 있었나 싶다. 밥먹을 때, 잠잘 때도 프로젝트 생각을 했다. 특히 영상 고유의 id를 가져오기 위한 방법을 고민했을 때에는 잠들려고 누웠다가 방법이 떠올라서 다시 일어나서 코딩을 하기도 했다. 실제로 그렇게 문제가 해결됐다. 정말 짜릿한 순간이었다.

무엇보다도 기한 안에 완성했다는 사실이 뿌듯하다. 처음 기획했던 그대로 영상리스트와 보관함, 커뮤니티까지 기능 축소 없이 그대로 완성했다는 사실도 기쁘다. 정말 동네방네 자랑이라도 하고싶다.

🥴 아쉬운 부분도 있다. 백엔드를 구현하는 코드에 분명 불필요한 작업들이 있다.
1. 보관함 기능을 구현할 떄 영상정보를 받아와서 화면 출력이후, 또다시 같은 정보를 다른 페이지에 넘겨줘야하는데, db에서 받아서 바로 적용하는게 아니라 db에서 받은 정보를 client에 보내서 출력하고 그 정보를 다시 db로 보내서 새로운 데이터베이스에 저장하고, 또 다시 받아와서 새로운 페이지에서 보여주고 있다.
2. 코드 면에서도 변수를 줄이고 코드를 간단하게 바꿀 수 있는 부분들이 존재한다.
3. UI부분에서는 웹페이지에 맞게 화면이 재배치될 수 있도록 설정해주는 부분이 필요하다.
(2020.09.16 수정)
4. 객체지향의 개념이 전혀 적용되지 않았다.
같은 기능을 구현하는 부분이 계속 반복되는 문제, 불필요한 코드가 생기는 문제의 근본적인 원인은 객체지향의 개념을 적용하지 않고 코드를 구현했기 때문이다. 아주 중요한 부분인데 3개월 전의 나는 이 사실을 모르고 있었다😂 반드시 보완되어야 할 부분이다.

다음 프로젝트에서는 이번에 아쉬웠던 부분에 특히 신경써야겠다. 설계할 때부터 api를 꼼꼼히 다듬고 점검하자!

profile
https://github.com/Jeongseo21

0개의 댓글