요구사항: 리스트 -> 상세페이지 -> 리스트 와 같이 사용자가 움직일때 이전에 스크롤위치를 기억해달라.
위와 같은 요구사항에 대응하기 할때
여러가지 방법 (cookie에 위치를 기억한다든지, 앱 자체에서 스크롤 위치를 상태관리하고 리스트에 돌아왔을때 scrollTo를 통해 스크롤 위치를 바꿔준다든지 하는 방법) 이 있는데요.
그 중에서 Angular 기능을 최대한 사용하는 RouteReuseStrategy 기능을 이용하겠습니다.
리스트 -> 상세페이지 즉 컴포넌트 이동시 Router를 통해 이동하잖아요. 그리고 그럴때마다 컴포넌트를 생성합니다. RouteReuseStrategy를 이용하면 한번 생성한 컴포넌트를 살려두고 Router로 이동시 기존 컴포넌트를 다시 사용하는 거에요. 아주 효율적이고 직관적인 방법입니다.
이렇게 하기 위해서 해야할 것들은
최상단 Router 모듈에 RouteReuseStrategy 인터페이스를 구현한 Strategy를 넣어요. 아래와 같이 생겼고요. 이 Strategy를 모듈의 service로 붙입니다.
export class CustomRouteReuseStrategy extends RouteReuseStrategy {
private cache = new Map<string, DetachedRouteHandle>();
shouldDetach(route: ActivatedRouteSnapshot) {
if (getPath(route).startsWith('list')) {
return true;
}
return false;
}
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle) {
this.cache.set(getPath(route), detachedTree);
}
shouldAttach(route: ActivatedRouteSnapshot) {
const path = getPath(route);
if (path.startsWith('list') && this.cache.has(path)) {
return true;
}
return false;
}
retrieve(route: ActivatedRouteSnapshot) {
return this.cache.get(getPath(route));
}
shouldReuseRoute(
future: ActivatedRouteSnapshot,
curr: ActivatedRouteSnapshot
) {
return future.routeConfig === curr.routeConfig;
}
}
공식문서에도 RouteReuseStrategy에 대한 내용은 친절하지 않은데, 메서드에 대한 내용이 잘 나온 레퍼런스를 통해 구현할 수 있었습니다.
레퍼런스 : https://itnext.io/cache-components-with-angular-routereusestrategy-3e4c8b174d5f
리스트 - 상세에 대한 간단한 예제 : Stackblitz
이 기능을 회사 프로젝트에 적용할때에는 약간의 문제가 있었는데요.
Lazy Module로 구성하여 Route들을 분리해놨는데, LazyModule에 Service로 ReuseStrategy를 붙이니까. serivce가 intialized가 되질 않아요.
그래서 최상단 Router 모듈에 RouteReuseStrategy 붙이게 되었고, 모든 라우터에 반응하지만
RouteReuseStrategy 내부에서 분기처리하여 원하는 컴포넌트만 재사용하는 방법으로 문제를 해결했습니다.
출처: https://itnext.io/cache-components-with-angular-routereusestrategy-3e4c8b174d5f
RouteReuseStrategy Angular의 Route, component lifecycle의 행동을 제어하게됩니다.
컴포넌트간 navigate를 통해 route를 이동하게 되면, Angular는 이전 컴포넌트를 제거하고, 새로운 컴포넌트를 생성합니다. 컴포넌트에서 http requests같은 I/O관련 작업이 있다면 이건 효율적이지 않을 것 같습니다.
그래서 어떻게 컴포넌트를 부시고 살리지 않고 캐싱을 할 수 있느냐?
RouteReuseStrategy inteface는 다섯개 메서드를 갖습니다.
shouldReuseRoute이 메서드는 routes간 navigate할때마다 동작합니다.
매개변수 설명
future: source route
curr: target route
이 메서드가 ture를 리턴하면 route 안합니다. 변화가 없겠죠.
false를 리턴하면 원래대로 route가 이동하고 컴포넌트 죽이고 살리고 angular lifecycle대로 동작합니다.
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
// default action
return future.routeConfig === curr.routeConfig;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean;
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null;
shouldDetach(route: ActivatedRouteSnapshot): boolean;
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void;
if shouldAttach return true -> retrieve called
if shouldDetach return true -> store called
레퍼런스 : https://javascript.plainenglish.io/angular-route-reuse-strategy-b5d40adce841