- 직장에서 코드리뷰(라고 불리기에는 한 달에 한 번)시간에 공유한 내용을 다시 정리한 글입니다.
- CSR환경에서 React-Router-DOM V5.X를 사용했습니다.
- UI는 Kendo UI를 활용합니다.
특정 페이지로 진입 시 상태 설정과 뒤로가기 시 이전 페이지의 상태값 유지하기
관리자 페이지 리팩터링을 하면서 놓쳤던 페이지간 상태 공유를 개발해야 하고 다음 2가지 상황에서 적용을 해야했다.
그러다가 어느 주말에 개발자 단톡에 올라온 Funnel 구조 이야기를 기점으로 현재 내 상황에 맞는 구조가 어떤 것이 있는지 생각을 해봤다.
처음 생각해본 구조는 퍼널이다. 올해 토스의 SLASH23에서 나왔다고 하여 영상을 보고 어떤 형식인지 살펴봤다.
이를 활용하여 관리자의 목록 화면과 상세화면을 Wrapper로 감싸고 Step이라는 단계를 통하여 UI를 바꿔주는 형식으로 구현할 수 있을 것 같았다.
그러나, 상세페이지는 꼭 상위 목록 페이지에서만 접근이 가능하지 않다.
따라서, 목록과 상세를 하나의 상위 컴포넌트로 묶어서 상태 공유하는 것은 현재 상황과는 부합하지 않다.
![]() | ![]() |
---|
Recoil을 통한 전역적 또는 Context를 통한 지역적으로 관리할 수 있어서 상세페이지가 아니더라도 접근이 가능하고 상태를 넘겨 받을 수 있다.
그러나, 상태를 저장한다 = 렌더링이 발생(좋고 나쁨이 아닌 화면 동기화 작업이 존재) + 상태를 인위적으로 지워줄 필요 + 새로고침 이후 상태 증발하는 현상이 나타날 수 있다
따라서, 전역 또는 지역은 부분적으로 부합하다고 생각했다.
Storage에 저장하는 방법또한 존재하지만, 이 또한 Recoil 및 Context API 처럼 직접적인 하위 페이지로 이동할 땐 저장 이를 벗어나면 삭제해야 하는 코드 추가 필요하다.
따라서, 장단점은 전역 또는 지역 항목과 동일하다고 본다.
React-Router-Dom(V5 기준) useHistory
라는 함수(hook)는 window.history 객체를 react에 알맞게 제공한다.
장점으로는 사용 시 브라우저의 hisotry stack을 남김 → 그 페이지로 움직인 “상황”(어떻게 움직였는지)을 기억하고 있다.
단점:
history
입력 시 어떤 값이 저장되는 지 바로 알 수 있기에 기밀정보를 보관하기에는 좋지 않은 장소위 장단점을 비교했을 때 로직에 따른 추가 코드, 특히 다른 방법들을 봤을 때 어떨 때 지우고 저장하는 로직이 상당히 애매하다고 생각했다.
또한, 브라우저의 history stack에 저장하여
구현은 상당히 단순했다. 페이지 넘김 시 새로운 url에 state key값에 넣오 주면 됬다. 이후 받아온 컴포넌트가 이를 조회하여 사용하면 된다.
/** Page 이동을 선언하는 컴포넌트 */
<Column
title="답변대기"
field="managerStandbySum"
className="ta-c"
width={100}
cell={LinkCellFn({
path:"/xxxxx/xxxxxxxx",
// historyState을 남길 때 구조 획일화를 위해 search라는 key에 원하는 상태를 객체로 전환하여 전달
historyState: { search: { ..... }}
})}
/>
/** 렌더링되는 컴포넌트 */
const ApprovalManagement = () => {
const location = useLocation();
const locationSearchState = location.state.search;
const [search, setSearch] = useState({
...INIT,
...locationSearchState,
});
const { types, status } = locationSearchSate;
// location 안 history state값을 설정
const initalTypeSetting = locationSearchState?.types ? { key: locationSearchState.types, value: ApprovalType[locationSearchState.types].name } : defaultItem;
const initalstatusSetting = locationSearchState?.status ? { key: locationSearchState.status, value: ApprovalStatus[locationSearchState.status].name } : defaultItem;
const initalValues = {
types: initalTypeSetting,
status: initalstatusSetting,
keywordType: KEYWORD_TYPE[0],
searchDate: {
start: null,
end: new Date(),
}
};
........
return (
<Form
onSubmitClick={onSubmitClick}
initialValues={initalValues}
.....
/>
)
해당 부분에 대해서 생각하지 못 하였기에 현재 페이지의 상태를 저장하고 나가는 방법 또한 history
객체를 활용할 수 있다고 생각했다.
그래서 window.history 문서를 봤고 window.history.state
를 통하여 현재 url 안의 상태값을 바꿀 수 있다.
window.histoy.state
를 조작window.history.replaceState
를 이용하여 직접 window의 값을 조작하고 검색할 때마다 작동해야 하기에 useEffect
를 사용useEffect(() => {
window.history.replaceState({search}, '', window.location.pathname)
refetch();
}, [refetch, search]);
useLocation
값을 찍었을 때, window.history
의 값이 보이지 않음useHistory
는 window.history
를 이용하는 것이 맞음( How does React Router location.state works?)useHistory
훅의 api를 이용useHistory
에서 변경된 값 → window.history
로 일방통행 업데이트 진행(window.history
에서 업데이트 한 값이 useHistory
로 반영되지 않음)useHistory().replace
를 통하여 hook에서 변경하는 방향으로 변경history.replace(location.pathname, {search});
useLocation
훅을 통하여 state를 받아서 queryKey의 search상태와 form의 initalValues를 setting해준 결과 의도대로 작동const initalFrom = locationSearchState?.from ? new Date(locationSearchState.from) : null;
const initalTo = locationSearchState?.to ? new Date(locationSearchState.to) : new Date();
const initalKeywordType = locationSearchState?.keywordType ? APPROVAL_DROP_DOWN.KEYWORD_TYPE.find(item => item.key === locationSearchState.keywordType ) : APPROVAL_DROP_DOWN.KEYWORD_TYPE[0];
const initalKeyword = locationSearchState?.keyword ? locationSearchState.keyword : null
const initalValues = {
keywordType: {key: 'test', value: 'testing'},
keyword: initalKeyword
searchDate: {
start: initalFrom,
end: initalTo,
}
};
// 위 긴 로직을 함수로 묶어서 page 렌더링 파일 내부에서 함수 반환값을 할당. 함수는 상수 관리 파일에서 보관 및 export
const initalValues = activityLogSearchToForm(search, roleGroup, location);
어느정도 진행을 하다가 선배와 이야기를 하다보니 url에 parameter로 남겨놓지 않아아서 몇가지 문제가 같이 있는 것고 이 또한 그 중 하나로 볼수 있을 것 같다고 했다. 만약 url으로 남겼다면 그대로 query를 날리거나, 브라우저 히스토리 설정 및 이용이 쉬웠을 수도 있고, 탭을 구현할 때도 조금 더 수월했을 수도 있을 것 같다고 한다. 공감한다.
그러나, 항상 완벽한 방법은 없다.
글 잘 봤습니다.