[Vue.js] 스팀잇(Steemit)기반 앱 만들기 #3 - 무한 스크롤 구현하기

in #kr6 years ago (edited)

이번 시간에는 스크롤을 내렸을때 글 목록을 자동으로 가져오는 기능을 구현해보도록 하겠습니다. 이런 기능을 인피니티 스크롤(Infinite Scroll) 또는 무한 스크롤이라고 합니다.

아래는 무한 스크롤이 구현된 화면입니다.


이전글

무한 스크롤(Infinite Scroll) 모듈 설치하기

무한 스크롤 기능을 직접 구현해도 되지만, 이 글에서는 vue-infinite-scroll 모듈을 사용하여 구현하겠습니다. 아래와 같이 설치합니다.

$ npm install vue-infinite-scroll --save

글 목록 화면에 무한 스크롤 적용하기

무한 스크롤을 적용하기 위해 Main.vue 파일을 수정합니다. 글목록을 보여주는 영역을 <div>태그로 감쌉니다. 그리고 v-infinite-scroll, infinite-scroll-disabled, infinite-scroll-distance 속성을 추가합니다. 각 속성에 대한 내용은 아래쪽에서 다시 설명하겠습니다.

수정된 코드는 다음과 같습니다. 참고로 코드량이 많아 일부 생략하였습니다.

<v-flex xs12 md9 offset-md3>
  
  <div v-infinite-scroll="loadMore" 
       infinite-scroll-disabled="busy" 
       infinite-scroll-distance="10">
    
    <v-layout row wrap>
      <v-flex xs12 md6 xl4 v-for="d in discussions" :key="d.id">
        
        ... 생략 ...
        
      </v-flex>
    </v-layout>
  </div>
</v-flex>

vue-infinite-scroll 모듈에서 사용가능한 옵션은 아래와 같습니다.

OptionDescription
infinite-scroll-disabled이 속성의 값이 TURE 이면 스크롤이 비활성화됩니다.
infinite-scroll-distancev-infinite-scroll 함수가 실행되기 전, 요소 하단과 뷰 포트 하단 사이의 최소 거리입니다. 기본값은 0입니다.
infinite-scroll-immediate-check바인딩 되자마자 즉시 directive을 확인해야 함을 의미합니다. 내용이 스크롤 가능한 컨테이너를 채울만큼 많지 않을 때 유용합니다. 기본값은 true 입니다.
infinite-scroll-listen-for-eventVue 인스턴스에서 이벤트가 생성되면 스크롤을 다시 검사합니다.
infinite-scroll-throttle-delay이번 확인 시간과 다음 확인 시간 사이의 간격(밀리 초). 기본값은 200입니다.



이제 data 오브젝트에 무한 스크롤 기능 활성화 여부를 저장할 busy 변수를 추가합니다. 그리고 무한 스크롤 기능 작동시 호출되는 loadMore 함수를 구현합니다. loadMore함수에는 앞서 구현하였던, 최근글을 가져오는 getDiscussions 함수를 호출하도록 하겠습니다. getDiscussions 함수 호출 전에 busy값을 true로 변경하여 무한 스크롤이 중복하여 작동 하지 않도록 합니다.

export default {
  data: () => ({
    discussions: [],
    busy: false
  }),
  
  ... 생략 ...
  
  methods: {
    loadMore: function () {
      this.busy = true // 무한 스크롤 기능 비활성화
      this.getDiscussions()
    },

글을 가져온 후에는 무한 스크롤 기능을 다시 활성화 해주어야 합니다. 그래서 getDiscussions 함수를 다음과 같이 수정합니다. getDiscussionsByCreated함수의 콜백에서 busy값을 다시 false 로 변경합니다. 그리고 새로 가져온 데이터가 기존 데이터를 유지하면서 추가될 수 있도록 discussions 변수에 담아주는 코드를 아래와 같이 수정합니다.

// 스팀잇 최근글 가져오기
steem.api.getDiscussionsByCreated(query, (err, result) => {

  if (!err) {
      
    // 무한 스크롤 활성화
    // 가져온 글갯수가 10개 이상인 경우만 작동
    if (result.length > 10) { 
      this.busy = false 
    }
    
    // 가져온 데이터를 items에 담는다.
    const items = result.map(item => { // # 수정됨
    
      ... 생략 ...
    })

    // items 배열을 기존 discussions 배열과 합친다.
    this.discussions = this.discussions.concat(items)
  }
})

그리고 우리는 loadMore함수에서 스팀잇 글을 가져오도록 수정했기 때문에, created 함수에서는 스팀잇 글을 가져오는 getDiscussions 함수를 더 이상 호출하지 않도록 제거합니다.

created () {
  // this.getDiscussions() // # 제거됨
}



위의 코드를 실행해보면 계속 동일한 글 목록만 가져오는 것을 볼 수 있습니다. 왜냐하면 우리는 같은 페이지의 글목록을 가져오고 있기 때문입니다. 다음 페이지의 글을 가져오기 위해서는 steem.api.getDiscussionsByCreated 함수를 호출할 때 permlinkauthor 값을 넘겨줘야합니다. 다음 글을 가져오기 위해 이 두 값을 사용하려면, 글을 가져올때마다 마지막 값을 기억하고 있어야합니다. 아래와 같이 data 오브젝트에 next 오브젝트를 추가합니다. next 오브젝트는 permlinkauthor 를 가집니다.

data: () => ({
  discussions: [],
  busy: false,
  next: {
    permlink: null,
    author: null
  }
})

그리고 methods 에 있는 getDiscussions 함수를 다음과 같이 수정합니다. 스팀잇 최근글을 가져오는 함수를 호출할때 넘겨주는 query 오브젝트에 start_permlinkstart_author 추가하고, next.permlinknext.author 값을 넘겨줍니다. 마지막으로getDiscussionsByCreated 함수 콜백에서 가져온 글목록에서 마지막 데이터의 permlinkauthor 값을 각각 next.permlinknext.author 에 저장합니다.

getDiscussions () {
  let query = {
    tag: 'kr',
    limit: 10,
    start_permlink: this.next.permlink, 
    start_author: this.next.author
  }
  
  // 스팀잇 최근글 가져오기
  steem.api.getDiscussionsByCreated(query, (err, result) => {

    ... 생략 ...
    
    this.next.permlink = item.permlink // 마지막 permlink 값 저장  
    this.next.author = item.author // 마지막 author 값 저장

    return {
      id: item.id, // # 추가됨
      image: image,
      author: item.author,
      author_reputation: item.author_reputation,
      title: item.title,
      created: item.created,
      body: item.body.substr(0, 200),
      category: item.category,
      permlink: item.permlink,
      url: item.url,
      payout_value: item.payout_value,
      net_votes: item.net_votes,
      children: item.children
    }
  })



지금까지 작업한 코드를 실행해보면, 다음에 가져온 글 목록의 첫번째 글이 이전에 가져온 글 목록의 마지막 글이랑 동일하다는 것을 확인할 수 있습니다. 일부러 이렇게 의도한 것인지는 잘 모르겠습니다. 가져온 글목록의 글ID를 체크하여 글ID가 서로 동일하면 기존 글목록에 추가하지 않도록 합니다. 그래서 추가로 가져온 글 목록에서 첫번째 항목을 제거하도록 하겠습니다.

// 스팀잇 최근글 가져오기
steem.api.getDiscussionsByCreated(query, (err, result) => {
  this.busy = false
  if (!err) {

    // 가져온 글의 첫번쨰 항목 제거
    if (this.discussions.length > 0) {
      result = result.slice(1)
    }
    
    ... 생략 ...



글을 가져올때 아무런 변화가 없다가 화면에 글들이 갑자기 나타나면 아무래도 심심하기 때문에, 글을 가져올 때 화면에 로딩 이미지를 보여주도록 하겠습니다. 다음과 같이 <v-progress-circular> 태그를 글목록을 출력하는 부분 아래에 삽입합니다. 그리고 v-show="busy" 옵션을 사용하여 무한 스크롤 기능이 작동 중일때만 로딩 이미지가 보이도록 합니다.

    ... 생략 ...
  </v-flex>(html comment removed:  // v-for END )
</v-layout>
<div class="text-xs-center mt-3" v-show="busy">
  <v-progress-circular
            indeterminate
            color="primary">
  </v-progress-circular>
</div>


오늘 작업은 여기까지입니다.

여기까지 읽어주셔서 감사합니다.


전체 소스 내용은 github에서 볼 수 있습니다.


Sort:  

(jjangjjangman 태그 사용시 댓글을 남깁니다.)
[제 0회 짱짱맨배 42일장]5주차 보상글추천, 1,2,3,4주차 보상지급을 발표합니다.(계속 리스팅 할 예정)
https://steemit.com/kr/@virus707/0-42-5-1-2-3-4

5주차에 도전하세요

그리고 즐거운 스티밋하세요!

날잡아서 뚝딱뚝딱 따라 만들어봐야겠어요! ㅎㅎ 좋은글 감사합니다~

읽어주셔서 감사합니다.
시간나실때 따라하시다가 이해안되는거 있으면 질문해주세요~ㅎㅎ

Congratulations @anpigon! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes received
Award for the number of comments received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

thank you~:-)

Congratulations @anpigon! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

오우 재밌는 거 연재하시네요. ㅎㅎㅎㅎ 좋은 글 감사합니다. 저도 친구추가 했어요. ㅎㅎㅎ

친구추가 감사합니다~~^^
유 로보틱스님이 연재하시는 글 재미있게 잘보고 있습니다.ㅎㅎ

안피곤님은 천재

마이님 칭찬해주셔서 감사합니다.ㅎㅎ

좋은 연재 감사합니다. 앱개발은 안한지 정말 오래됐는데 필요할때 찾아보면 많은 도움이 될 것 같아요. 그런데 연재 초기글에 보면 vue를 쓰신다고 되어있는데 react와 비교해서 어떤가요? 최근 웹알못인 저도 react는 워낙 많이 들어봤고 스팀잇과 busy둘다 react쓰지 않나요?

제가 react는 깊이 있게 다뤄보지 않아서, vue와 자세하게 비교를 하면서 설명하기는 어렵습니다.
저의 경우, vue로 개발하는 이유는 단지 하나입니다. vue가 react보다 학습하기가 쉬웠습니다. vue는 기술문서를 반나절 정도 보고 바로 적용할 수 있습니다. 예를 들면, react는 jsx라는 문법을 사용하여 화면UI를 구성하는데 비해, vue는 html을 사용하기 때문에 기존에 알던 HTML방식으로 UI를 구성할 수 있습니다.

그리고 블록체인스튜디오님 말씀대로 스팀잇과 busy는 둘다 react로 구현되어있네요.^^

React와 Vue에 대해 설명이 잘되어 있는 글이 있어 공유합니다.
https://joshua1988.github.io/web_dev/vue-or-react/

자세한 설명과 링크 감사합니다. 네 react가 대세인거는 같더라고요^^ 말씀하신대로 vue가 html기반이면 확실히 배우기 쉽긴 하겠네요. 감사합니다!

재밌을거같아요~ 굿굿🍑

굿굿굿~🍊

Coin Marketplace

STEEM 0.20
TRX 0.15
JST 0.029
BTC 63362.14
ETH 2592.64
USDT 1.00
SBD 2.80