Mo-Gak-Ko > 전역 상태관리와 중첩 라우팅

jhj46456·2020년 5월 20일
0

Mo-Gak-Ko

목록 보기
2/18

문제

사이드 프로젝트를 진행하면서 다음과 같은 상황에 놓였던 적이 있습니다.

  1. 보이는 것은 모두 동일한 컴포넌트.
  2. 빨간 선을 기준으로 는 공개 데이터, 아래는 모임의 구성원만 볼 수 있는 비공개 데이터를 보여줘야 한다.
  3. 또한, URL"/room/:id/detail" 에 직접 접근하는 경우
    모임의 구성원만 비공개 데이터를 보여주고 아닌 경우 "/"으로 Redirect 시켜야 한다.

이 조건을 구현하기가 좀 까다로운 부분이 있는데, API를 호출하게 되면 InspectorNetwork tab에 response가 찍혀버립니다.
그렇게 되면 화면에는 보이진 않지만, 해당 모임의 비공개 데이터를 구성원이 아니더라도 볼 수 있습니다.

처음에는 Condition 안에 Hook을 넣을 수가 없어 "/" 에서 URL에 query가 있는지 비교하여 모달을 보여주었는데
Room에서 한번에 보여주는게 좋은 것 같다는 피드백을 받고 변경하게 되었습니다.

해결

우선, 위에서 적은 것처럼 Hook을 Condition 안에 적을 수 없어 Room 컴포넌트 안에 RoomDetail 컴포넌트를 보여주는 중첩 라우팅 을 사용하였습니다.

<Route path="/room/:roomId/detail" component={RoomDetail} />

이 코드가 Room 컴포넌트 안에 있다.

이 프로젝트는 GraphQL API를 사용하기 때문에 Apollo를 사용하였는데,
Apollo가 캐싱을 자동으로 해줘서 개발자가 store를 건드릴 필요는 하나도 없습니다.

먼저, Apollo의 쿼리 호출 과정은 다음과 같습니다.

  1. graphql 형태로 작성된 쿼리 호출
  2. API 서버로 요청을 보내기 전 InMemoryCacheROOT_QUERY에 동일한 기록이 있는지 확인
  3. (2)의 과정에서 처음 호출하는 쿼리라면 네트워크 요청을 보내고, 호출 했던 쿼리라면 네트워크 요청을 보내지 않고 바로 Cache에서 가져옴

글로만 적어두면 이해가 쉽지않아 이미지로 가져와봤습니다.



콘솔을 확인해보면 Room에서 호출된 roomPublic중첩 라우팅을 타고
RoomDetail에서 publicData를 요청하니 로딩없이 가져온 모습을 볼 수 있습니다.

이 것을 활용하면 문제를 해결할 수 있습니다.

@apollo/react-hooksuseQuery 는 조건을 입력하면 쿼리 호출을 막을 수 있는 skip 옵션이 있는데
skip에 publicData의 값들을 비교하여 원하는 경우에만 data를 가져올 수 있습니다.

이 경우에는 "/room/:id/detail" 로 접근하면 publicData의 isManager와 isMember 둘 다 false인 경우 skip을 합니다.

그 결과 "room/:id/detail" 로 URL 직접 접근을 하게 되면 다음의 절차를 거칩니다.

  1. Room이 mount 되어짐
  2. Room의 API 호출
  3. Room의 View 보여줌
  4. 중첩 라우팅으로 설정된 RoomDetail이 mount 되어짐
  5. RoomDetail의 API 호출
  6. InMemeoryCache에서 roomPublic의 data를 바로 가져온 다음 roomPrivate의 skip 조건에 roomPublic의 속성을 삽입
  7. roomPrivate를 호출할 때 roomPublic의 isMember가 false면 roomPrivate를 호출하지 않음

이렇게 보안패치를 할 수 있습니다.

이제 마지막으로 한 가지만 더 해주면 완벽하게 마무리 됩니다.

RoomDetail 컴포넌트에 mount 되어질 때 최초로 한번 방장인지 참여자인지 비교하여 권한이 없는 user는 "/" 으로 보냅니다.

"/room/:id/detail" 에서 roomPublic의 data는 반드시 존재합니다.

결과

Browser

❗ 스켈레톤이 나온 뒤 사라지면 API로부터 data를 정상적으로 받은 상태.

"/" 으로 쫓겨난 뒤 Network tab의 모습.
roomPrivate 는 네트워크 요청이 가지 않음.**

Server

각각의 쿼리에 아래 코드를 삽입한 상태

console.log("쿼리명");
return ~조회한 data;

❗ URL로 접근하는 경우 refresh 되어지기 때문에 같은 쿼리를 다시 보냄.

  • "/room/:id/detail" 로 URL 직접 접근 할 경우

  • "/room/:id" 들린 다음 /room/:id/detail" 으로 URL 직접 접근 할 경우

  • 모임의 구성원인 경우

profile
리액뚜

0개의 댓글