반응형

0. 목적

-. 커뮤니티 기능을 위해 간단한 게시판 (bbs)을 만든다.

-. i) 글 작성, ii) 글 조회, iii) 글 수정, iv) 글 삭제 순서로 진행해봄

  • 글 작성: bbs/write
  • 글 조회: bbs/list (글 목록) -> bbs/articleId
  • 글 수정: bbs/articleId/modify
  • 글 삭제: bbs/articleId/delete

-. Nested route 

-. db를 dynamo로 하는 이유는 공짜라서 ....

1. 게시판 레이아웃 만들기

-. 게시판의 기본 골격은 vue의 route, children기능을 이용해서 아래와 같이 꾸렸다. 제대로 작동하려나...

  {
    path: '/bbs',
    name: 'BBSLayout',
    props: true,
    component: BBSLayout,
    children: [
      {
        path:'write',
        name: 'BBSWrite',
        component: BBSWrite
      },
      {
        path:'',
        name: 'BBSList',
        component: BBSList
      },
      {
        path:':id',
        name: 'BBSRead',
        component: BBSList,
        children: [
          {
            path:'modify',
            name: 'BBSModify',
            component: BBSModify
          },
          {
            path:'delete',
            name: 'BBSDelete',
            component: BBSDelete
          }
        ]
      },
    ]
  },

2. 글 작성 테스트

1) 웹 프론트 작성

-. 글 작성 템플릿은 아래와 같이, 작성자 이름/비밀번호/카테고리/제목/내용으로 간단하게 꾸렸다. (로그인 미구현)

-. article 오브젝트에 담아서 서버에 post 방식으로 전달하는 axios모듈을 설정해 놨고, 이것을 flask 서버에서 받는다.

<template>
  <div class="container">
    <form class="write-form" @submit.prevent="sendForm">
      <label id="writer-label">writer</label>
      <input v-model="article.writer"
      type="text"
      placeholder="writer"
      id="writer">

      <label id="password-label">password</label>
      <input v-model="article.password"
      type="text"
      placeholder="password"
      id="password">

      <select id="category" v-model="article.category">
        <option>잡담</option>
        <option>정보</option>
        <option>질문</option>
        <option>요청</option>
      </select>

      <label id="title-label">Title</label>
      <input v-model="article.title"
      type="text"
      placeholder="Title"
      id="title">

      <label id="description-label">Description</label>
      <textarea v-model="article.description"
      placeholder="description"
      id="description"/>

      <button id="submit" type="submit">submit</button>

    </form>
  </div>
</template>

<script>

import Community from '@/services/Community.js'

export default {
  name: 'BBSWrite',
  props: {
  },
  data (){
    return {
      article: {
        title: '',
        description: '',
        writer: '',
        password: '',
        category: '잡담',
      }
    }
  },
  methods: {
    sendForm(){
      Community.postWrite(this.article)
    }
  }
}

</script>

2) flask api

-. 간단하게 플라스크로 post 단말을 만들었다. dynamo를 쓰려니 AI키가 없어서 글 고유번호를 어떻게 해야할지 고민을 하다가, 그냥 간단하게 밀리초 단위로 시간을 받아서 base52 변환을 한 것을 정렬키(articleNo)로 적용하기로 했다. 

@app.route('/api/bbs/write', methods=['POST'])
def bbs_write():
    params = json5.loads(flask.request.get_data(), encoding='utf-8')
    print(App.base52decode(time.time()))
    article = {
        #posted data
        'title': {'S': params['title']},
        'description': {'S': params['description']},
        'writer': {'S': params['writer']},
        'password': {'S': params['password']},
        'category': {'S': params['category']},

        #db info
        'articleFrom': {'S': 'coinadvisor'},
        'articleType': {'S': 'bbs'},
        'articleId': {'N': str(int(time.time()*1000))},
        'articleNo': {'S': App.base52decode(time.time())}
    }

    app.extensions['dynamodb_client'].put_item(
        TableName='community_articles',
        Item=article,
        ConditionExpression='attribute_not_exists(articlNo)',
    )

    response = flask.make_response(article)

    return response

-. db 데이터 확인하면 입력한대로 정상적으로 적재되는 것을 확인할 수 있다.

3. 글 목록 페이지

-. 글 작성기능을 넣었으니, 이제 작성된 글을 보여주는 기능이 필요하다.

1) 글 목록 출력 api

-. 글 목록 출력은, dynamodb에서 articleType=bbs를 키로 추출한다.

@app.route('/api/bbs', methods=['GET'])
def bbs_list():
    res = flask.current_app.extensions['dynamodb_client'].query(
        TableName="community_articles",
        KeyConditionExpression='articleType = :articleType',
        Limit=40,
        ScanIndexForward=False,
        ExpressionAttributeValues={
            ':articleType': {'S': 'bbs'}
            }
    )
    ret = res['Items']

    result = json.dumps(ret, ensure_ascii=False, indent=4)
    response = flask.make_response(result)
    return response

2) 목록 보여주기

-. 어떤 형태로 보여줄지를 고민해봤는데.. 며칠 전에 만든 뉴스 크롤 뷰 페이지와 동일하게 만들기로 했다. 편하기도 하고... 통일성(?)도 있고.  상위 Layout에서 tableContents object를 전달받아 리스트로 보여주는 페이지를 만들었다.

<template>
  <div class="container">
    <div class="table-wrap horizontal headless-table" id='bbs-table' >
      <!-- <div class="table-header" style="visible:none">
        <div class="table-header-item article-ref">출처</div>
        <div class="table-header-item article-title">제목</div>
        <div class="table-header-item article-time">게재일자</div>
      </div> -->
      <div v-for="(content, index) in tableContents" 
      :key=index class="table-row"
      v-on:click="sendTo(index)" ref='rows'> 
        <div class="table-row-item article-category" :id="'table-row-category-' + index">
          {{content.category.S}}
        </div>
        <div class="table-row-item article-title" :id="'table-row-title-' + index">
          {{content.articleTitle.S}}
        </div>

        <div class="table-row-item article-time" :id="'table-row-time-' + index">
          <time :date="content.articleId.N">{{content.articleId.N}} </time>
        </div>
        <div class="table-row-item description" :id="'table-row-desc-' + index">
          <p v-html="content.description.S"></p>
        </div>
        <div v-if="content.thumbnail" class="table-row-item thumbnail" :id="'table-row-thumbnail-' + index">
          <img :src="content.thumbnail" alt="썸넬">
        </div>
      </div>
    </div>
  </div>
</template>

<script>


export default {
  name: 'BBSList',
  props: {
    tableContents: Object
  },
  data (){
    return {
    }
  },
  methods: {
  }
}

</script>

3) 다듬기

-. 현재 글 작성 시각이 miliseconds로 뜨니, 이걸 현재부터 몇초 전인지 보여주는 것으로 바꾼다. 특이하게, object로 받아온 변수는 vue 내에서 함수처리를 하지 못한다. (이미 처리된 데이터라 그렇댄다...) 그래서 부모 페이지에서 넘겨주기 전에 시간 갭을 계산해줘야 한다. 위에서 정의한 getList() 부분을 변경하자.

    getList () {
      var global = this;
      Community.getBbsList(global.lastContent)
      .then(response => {
        for (var i=0; i < response.length; i++){
          response[i]['timegap'] = this.timeParse(response[i].articleId.N)
          global.tableContents.push(response[i]);
        }
        global.lastContent = global.tableContents.at(-1).articleId.N;
      })
    },
    timeParse (miliseconds){
      const _now = Date.now();
      const _diff = _now - miliseconds;

      const diffSeconds = parseInt(_diff/1000);

      const gapHours = parseInt(diffSeconds/3600)
      const gapMin = parseInt((diffSeconds%3600)/60)
      const gapSec = (diffSeconds%3600)%60

      var gapHM = '';
      
      if(gapHours > 0) {
        gapHM = gapHM + gapHours + "시간 전";
        return gapHM
      }
      if (gapMin > 0) {
        gapHM = gapHM + gapMin + "분 전";
        return gapHM
      }
        gapHM = gapHM + gapSec + "초 전";
        return gapHM
    },

-. 그럭저럭 봐줄만 한 것 같다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기