RxJs를 활용해 LruCache적용하기

God Beom·2022년 4월 22일
0
post-thumbnail

TL;DR

  • RxJs를 활용한 사용자에게 비교적 안전하게 더 나은 사용자 경험을 제공 할 수 있는 방법
  • UI사용경험 개선을 위한 캐싱.(서버 자원 사용량은 non-cacheing과 동일)

도입 목적

  • 목적
    개발중인 웹사이트의 홈화면 특정영역을 api를 통해 가져오는데 4초 이상의 시간이 소요 되었다. 개인별로 제공해 줘야 하고 타 서버와 인터페이스 시 지연이 이유였는데 나와 다른 분야의 전문가분들의 의견이니 받아 들이기로 하고 클라이언트측에서 해결해 보고자 적용

효과

  • 지연 없는 API 호출
  • 일시적 네트워크 오류 라도 문제 없이 서비스 이용 가능(Lru에 의해 삭제되지 않았다면)

캐싱은 종류에 따라 추적이 힘든 오류를 발생 시킬 수 있지만 이번에 소개한 캐싱 방법은 타 캐싱 방법에 비해 비교적 안전하게 도입 할 수 있습니다. cacahe key만 제대로 주어진다면 로딩없는 화면과 항상 최신에 데이터를 유지 할 수 있기 때문이에요.

RxJs Cache 흐름도

  • 최초(Cache Miss)
  1. call함수에서 http 데이터 요청
  2. 가져온 데이터 cache 입력.
  3. 가져온 데이터 call함수로 반환
  • 캐싱 후(Cache Hit)
  1. call함수에서 http 데이터 요청
  2. cache에서 이전 데이터 가져옴. (subscribe 1)
  3. http 데이터 요청.
  4. 가져온 데이터 cache 입력.
  5. 가져온 데이터 call함수로 반환 (subscribe 2)
  6. Diff Check Update : subscribe1(cache), subscribe2(response) 비교 후 변경 사항만 업데이트
    (100개의 리스트중 2개만 변경 되거나 변경 되지 않았을 경우 모두 재랜더링 할 필요는 없겠죠?)

Category.store.ts

async [CategoryActions.categorySelectFilterHttpCache]({ commit, state }: ActionContext<CategoryState, AppState>, payload: {categoryId: string; selectedFilter: CategoryFilter}) {
       commit(CategoryMutations.SET_LOADING,true)
       commit(CategoryMutations.SET_SELECTED_FILTER, payload.selectedFilter)
       state.payload.categoryId = payload.categoryId
       state.payload.paging = CategoryRepository.getDefaultCategoryPayload().paging
       CategoryRepository.getCategorysCacheHitFirst(state.payload)
           .subscribe( (categoryList) =>{
                    // 1call 당 2번 subscribe(1. cache, 2.response) 
                   state.payload.paging.loadSize = categoryList.length
                   commit(CategoryMutations.SET_CATEGORY_LIST,  categoryList)
                   commit(CategoryMutations.SET_LOADING, false)
               }
           )
   },

CategoryRepository.ts

 /** 캐싱 데이터 선방출 후 응답 데이터 방출 */
getCategorysCacheHitFirst(payload: CategoryPayload): Observable<CategoryDTO[]>{
        const cacheKey = this.cagegoryPayloadCacheKey(payload)
        return new Observable<CategoryDTO[]>(subscriber => {
                if(httpLruCache.has(cacheKey)){
                    subscriber.next(httpLruCache.get(cacheKey) as CategoryDTO[]) // Hit Cache 방출.1
                }
                const res = HttpService.get<CategoryDTO[]>('/posts');
                    res.then( response => {
                        const categoryList = this.toVM(payload.paging.start(), payload.paging.end(),response.data)
                        subscriber.next(categoryList) // Hit Response 방출.2
                        httpLruCache.set(cacheKey, categoryList)
                        subscriber.complete()
                        subscriber.unsubscribe()
                     })
                    res.catch(reason => {
                        subscriber.error(reason)
                    })
                }
            )
    }
profile
의미있는10%코드를 위하여

0개의 댓글