12 리팩토링 4 - 데이터 호출과 UX

source: categories/study/vue-beginner-lv3/vue-beginner-lv3_9-03.md

12.1 UX를 고려한 데이터 호출 시점

데이터 호출 시점

  1. 컴포넌트 라이프 사이클 훅: 여태까지 사용했던 방법
  • created: 컴포넌트가 생성되자마자 호출되는 로직
  1. 라우터 네비게이션 가드
  • 특정 URL로 접근하기 전의 동작을 정의하는 속성(함수)

위와 같이 데이터 호출 시점은 크게 2가지로 나뉩니다.

네비게이션 가드란?

네비게이션 가드(navigation guard)란 뷰 라우터로 특정 URL에 접근할 때 해당 URL의 접근을 막는 방법을 말합니다.
예를 들어, 사용자의 인증 정보가 없으면 특정 페이지에 접근하지 못하게 할 때 사용하는 기술입니다.

위 링크 참고.

Note

라우터 네비게이션 가드가 컴포넌트 라이프 사이클 훅보다 실행되는 시점이 더 앞선다.
라우터 네비게이션 가드가 호출되고 실행된 후, 컴포넌트 라이프 사이클 훅이 실행된다.
따라서 컴포넌트가 다 생성되고나서 데이터를 호출할 것인지,
아니면 컴포넌트가 생성되기 전에 라우팅 상태에서 데이터를 처리할 것인지,
이 2가지 중 하나를 선택할 수 있는 옵션이 생기는건데, 이 다음 시간에 각각의 차이점에 대해 짚어보고 그 다음에 네비게이션 가드를 구현해보도록 하겠습니다.

12.2 라이프 사이클 훅을 이용한 데이터 호출 방법의 문제와 비동기 처리 코드 수정

현재 문제

위 시점의 코드에서 문제점은, Ask, News, Jobs 각각의 탭을 클릭해 이동할 때마다
이전 탭메뉴의 데이터가 보였다가 업데이트되는 현상이 있다는 것이다.
이는 UX적으로 안좋은 동작이다.
이러한 현상의 원인은 이전 데이터를 담고있는 state를 업데이트 되기 전에도 렌더링해서 보여주기 때문이다.

현재는 Ask, News, Jobs 모두 statelist값에 데이터를 담아서 렌더링한다.
그렇기 때문에 다른 탭메뉴로가면 이전 텝메뉴의 데이터가 list에 담겨있기 때문에 그거를 보여주고,
클릭된 메뉴의 데이터가 list에 담기게되면 그때 다시 렌더링되면서 화면이 바뀌는 것이다.

지금은 list라는 state로 하나의 state에만 관리를해 이런 현상이지만,
이전에 news, jobs, ask 각각의 state로 관리했을 땐,
이전에 담긴 news 데이터를 미리 렌더링해 보여주고, 다시 업뎃되면 새로 불러와진 news 데이터를 렌더링하고..

뭐 이런 식이었다.

여튼 스피너 돌아가는 상태에서 이전 데이터가 미리 렌더링되어있던건 예나 지금이나 같은 현상이다.
이를 해결해야된다.

Note

라우터 안에 네비게이션 가드를 사용해서 위와 같은 문제를 해결해보도록 하겠습니다.

12.3 라우터 네비게이션을 이용한 데이터 호출 방법



import NewsView from "../../views/NewsView";

const newsRouter = [
    {
        // path: url 주소
        path: '/news',
        // component: url 주소로 갔을 때 표시될 컴포넌트 ( 페이지단위라고 보면됨, 예를 들어서 MainPage 같은 페이지단위 컴포넌트 )
        name: 'news',
        component: NewsView,
        beforeEnter: (to, from, next) => {
            console.log('to', to);
            console.log('from', from);
            console.log('next', next);
            // if (to.matched === '~~') {
                // 가장 많이 쓰이는 형식. to - 즉, 이동할 URL의 라우팅 정보에 ~~가 있다면, 여기 코드 실행
            //     next();
            // }
            if (to.auth === '~~~') {
                // 아니면 to에 인증 값이 있다고 했을 때, 인증값이 있다면 next();
                next();
            } else {
                // 인증 값이 없다면
                router.replace('/login'); // redirect를 해준다. router.replace('/login')는 redirect해주는 메소드임
            }
        },
    },
]

export default newsRouter


  • to: 이동할 URL의 라우팅 정보
  • from: 현재 URL의 라우팅 정보
  • next: beforeEnter에서 이 next 함수를 호출해줘야지만, 해당 URL, news로 이동할 수 있다. next() 함수를 호출하지않으면 해당 URL로 이동할 수 없다.

위와 같은식으로 라우터 네비게이션 가드를 활용해 정의를 해주실 수 있다.
beforeEnter라고하는 것은 특정 URL로 접근할 때, 인증정보가 있는지 없는지를 확인할 때 가장 흔하게 쓰이는 것입니다.
저희같은 경우는 beforeEnter에서 데이터 호출을 하기 위해서 beforeEnter를 쓰고 있습니다.

UX를 어떻게 가져가냐에따라 beforeEnter라는 라우터 네비게이션 가드를 사용하시던지
아니면 라이프사이클 훅에서 데이터를 호출하실건지,
그것은 정책적으로 결정하시면 됩니다.



import NewsView from "../../views/NewsView";
import bus from "../../utils/bus";
import store from "../../store";

const newsRouter = [
    {
        // path: url 주소
        path: '/news',
        // component: url 주소로 갔을 때 표시될 컴포넌트 ( 페이지단위라고 보면됨, 예를 들어서 MainPage 같은 페이지단위 컴포넌트 )
        name: 'news',
        component: NewsView,
        beforeEnter: async (to, from, next) => {
            bus.$emit('start:spinner');
            // this.$store는 인스턴스 안에서 접근 가능한 것이다.
            // store에 접근하려면 import 해와야된다.
            // this가 인스턴스를 가리켜야되는데, 여기 안에선 인스턴스를 가리키지 않는다!!!!
            // this.$store -> import해와서 그냥 store로..
            // this.$route -> 첫번째 인자 to로..
            await store.dispatch('FETCH_LIST', to.name);
            bus.$emit('end:spinner');
        }, 
    },
]

export default newsRouter


Note

transition 적용 효과는 어떻게 안되나?
beforeEnter로 해당 라우터로 들어가기 전에 데이터 업뎃해서 이전 데이터가 뿌려지고 다시 바뀌는 현상은 없어졌으나,
새 데이터로 뜨고나서 페이지 전환효과가 일어나는데(페이드인아웃 적용해놓음) 이는 어떻게 안되나?

Note
  1. 새로운 데이터는 beforeEnter로 미리 받아왔어.
  2. 그리고나서 next() 함수를 실행시켜 해당 라우터로 넘어가게 하려고하는데,
  3. next() 함수가 실행되기 전에 이미 statelist 값이 바뀐 것을 next() 전 페이지도 바라보고 있으므로 렌더링함
  4. 그래서 값이 바뀌고 나서 next() 함수가 실행되면서 클릭한 탭의 페이지 컴포넌트가 렌더링되면서 transition 효과 발생

즉, 정리하자면

  1. 현재 news 페이지라고 한다면, 여기서 ask 페이지 클릭
  2. ask 페이지로 넘어가기전에 네비게이션 가드 beforeEnter 실행 -> 데이터 받아옴 -> statelist 값에 받아온 데이터 할당
  3. 현재 news 페이지에서도 list 데이터를 바라보록 있음 -> 새로 갱신된 statelist 값으로 페이지 렌더링
  4. next() 메소드 실행
  5. next() 메소드가 실행되고나서 이제야 ask 페이지로 넘어감
  6. 그리고 App.vue에 페이지 전환 효과(transition) 걸어놨었잖아? 이게 일어남.
    즉, 이미 news 페이지 컴포넌트에서 갱신된 list 값으로 렌더링이 일어났었지?
    그런데 ask 페이지 컴포넌트로 transition 효과로 넘어가면서 갱신된 list 값으로 렌더링이 일어날거란말야?
    그럼 같은 값인데 처음엔 그냥 바로 보이고, 그다음에 transtion 효과로 걸어놓은 페이드인아웃으로 깜빡이면서 또 똑같은 list 값이 그대로 나타남.

이걸 해결하고 싶은 것.
같은 state값 - list를 쳐다보고있어서 생기는 문제…
그런데 이는 코드 정리를 위해서 필요한 부분이고…
어떻게 해결할 수 있을까.. 흠..


생각해낸 해결책..

  1. storestate를 하나 더 만들어놓자. list말고 하나 더 거쳐갈… beforeList라는 state를 설정
  2. 일단 API 호출을해 새 데이터를 받아오면 statelist에 바로 담는 것이 아니라 beforeList에 담자.
  3. ListItem 컴포넌트는 여전해 statelist를 바라보게하자.
  4. 그리고 ListItem 컴포넌트가 랜더링될 때 created 라이프사이클 훅으로 beforeList에 할당된 값을 list로 넣어주자.

이렇게하면 원하는대로 작동한다.
하지만 이것이 올바르고 좋은 방법인지는 모르겠다.

12.4 [실습] 라우터 네비게이션 가드 실습 안내

이미 12.3에서 다 함.

12.5 [실습] 라우터 네비게이션 가드 실습 및 스피너 종료 시점 변경

위 코드 기준.. 나는 강의와는 다르게 로딩 스피너 작동 잘함.
제때 꺼지고, 켜지고.

Note

routermixins 사용 못하나?
궁금하네..

해봤는데 안되네.. 흠.. router에 코드 겹치는 부분 수정하고픈데 흠..
뭐 방법없낭..

12.6 UX를 고려한 데이터 호출 시점 요약 정리 및 추가 고려 사항 안내

데이터 호출 시점

  1. 라우터 네비게이션 가드

    • 특정 URL로 접근하기 전의 동작을 정의하는 속성(함수)
  2. 컴포넌트 라이프 사이클 훅

    • created: 컴포넌트가 생성되자마자 호출되는 로직
Note

라우터 네비게이션 가드를 사용하게되면 해당 라우터로 이동하기 전의 동작을 정의하고 실행시키기 때문에,
그 동작이 시간이 오래걸린다면, 사용자가 지금 앱이 잘 되고있는건지 멈춘건지 분간이 어려우므로,
로딩 스피너와 같은 현재 동작중이라는걸 알리는 표시를 꼭 넣어야합니다.

이런 부분에 대한 UX를 어떻게 가져갈 것인지에 대한 고민을 충분히 하셔야됩니다.

(로딩바는 어떻게 구현하는걸까? - 다운 속도에 맞춰 바가 %로 차는거.. 흐음.. 뭔가 이론상으론 알겠지만 코드상으론 어케구현해야될지.. 이것도 함 연구해보자)
API 요청을 보내고 응답 진행률? 그런걸 알 수 있는 방법이 있을까?
이건 좀 찾아봐야겠는데?

  1. 데이터가 완전히 불러와진 후 페이지전환을 하겠다.
  2. 일단 데이터가 불러와지지 않더라도 페이지 전환 후 데이터가 불라와지면 화면에 재렌더링 하겠다.

이렇게 선택지 중에서 선택을 할 수 있다.

번외 - 프로그래스바

Completing our Progress Bar

  • Use Progress bar with EventList
  • Use Progress bar with EventCreate

Prerequisite Knowledge

  • Vue Router
  • Vuex

현재 상황: 프로그래스바 로직을 실행하는 fetchEvent 함수가 있는 상황

Note

프로그래스바 로직을 실행하는 함수 - fetchEvent
현재는 컴포넌트의 created 라이프 사이클 훅 안에 해당 로직이 들어가 있다.
하지만 해당 페이지 컴포넌트에 접속하면 프로그래스바가 실행되자마자 순식간에 완료되어 없어지는 상황이다.
그 이유는?

import EventCard from '@/components/EventCard.vue'
import {mapState} from 'vuex'

export default {
   components: {
      EventCard,
   },
   created() {
      this.perPage = 3;

      // 프로그래스바를 created 라이프 사이클 훅에 넣어도 안되고,
      // 왜냐면 컴포넌트가 created되고 난 후 바로 실행되는 거기 때문에.
      // 그래서 프로그래스바가 그냥 시작되었다가 바로 끝나버리는거.
      this.$store.dispatch('event/fetchEvents', {
         perPage: this.perPage,
         page: this.page,
      })
   }
}
const router = new Router({
   mode: 'history',
   routes: [
      {
         path: '/',
         name: 'event-list',
         component: EventList,
         // 여기에 beforeEnter로 프로그래스바 로직을 넣으면?
         // 이것도 안됨.
         // 이건 라우터 진입 전에 실행되는 로직이라.
         // 그래서 해당 페이지로 들어가면 프로그래스바 로직인 fetchEvent가 실행안됨.
      }
      // ...
   ]
})

그럼 프로그래스바 로직을 어디에 넣어야할까?

  • beforeRouteEnter(routeTo, routeFrom, next): called before component is created.
  • beforeRouteUpdate(routeTo, routeFrom, next): called when the route change, but still using the same component.

우리는 beforeRouteupdate를 사용해야됩니다.

Note

soon vue router may get another per-route guard called beforeResolve which is called on Create and Update.

곧 vue 라우터는 Create 및 Update에서 호출되는 beforeResolve라는 또 다른 경로별 가드를 얻을 수 있습니다.

beforeResolve 이것이 존재한다면 이것을 사용할 것이지만 그렇지 않으므로 다른 몇가지 솔루션을 사용할 것입니다.

이벤트 목록에서 진행률 표시줄이 작동하도록 하기 위해 취해야하는 단계

  1. 먼저 페이지당 상태로 이동해야합니다. (We need to move perPage into the State.)
  2. EventList라는 컴포넌트에서 beforeRouteEnterbeforeRouteUpdate를 사용해야합니다. Use beforeRouteEnter and beforeRouteUpdate in EventList.
  3. currentPage props를 전달하기 위해서 router.js를 수정해야합니다. We need to modify the router.js to send in the currentPage as a prop.

Note

흐음 대충 어떤식인지 알겠지만.. 확실한건 이 강의를 들어봐야겠다..