치춘짱베리굿나이스
로그인 유지 본문
로그인 유지
작년 부스트캠프 자료로 작성했던 것인데 블로그에 옮겨오는 것을 잊어버린듯
이슈
- 사용자 경험 측면에서, 페이지를 이동할 때마다 로그인이 해제되어 재로그인을 해야 한다면 사용자에게 불편을 초래할 수 있음
- 단순히 로그인 정보 (id - 다른 사람이 열람해도 상관없는 값) 를 전역 상태값으로 관리하면 해결되긴 하지만, 만약 유저가 페이지 내부의 상호작용 (버튼, 링크 등) 을 거치지 않고 url로 직접 접근하거나, 새로고침을 할 경우 웹 페이지 전체가 리로드되면서 로그인 상태값이 날아간다
해결 방안
CheckLogin 컴포넌트
export const CheckLogin = ({ children }: Props) => {
const [currentUser, setCurrentUser] = useRecoilState(currentUserState);
useEffect(() => {
if (currentUser.id) return;
fetchCheckLogin().then((data) => {
const { data: body, code } = data;
if (code !== 10000) setCurrentUser({ id: null });
else setCurrentUser({ id: body.id });
});
}, []);
return children;
};
CheckLogin
컴포넌트에서 로직을 처리하고, 내부에서children
prop을 이용하여 페이지를 렌더링하는 방식을 사용하자- 컴포넌트 내에서는
fetchCheckLogin
함수를 통해 서버로부터 로그인 여부 확인 요청을 보내고, 응답에 따라 전역 상태값으로 로그인된 유저의id
(고유값) 를 저장한다 - 로그인 여부는 요청에 함께 실어 보내는 쿠키를 통해 파악한다
- 컴포넌트 내에서는
- 이 컴포넌트는 각 페이지 접근 시마다 서버에 요청을 보내 로그인 여부를 확인 후, 로그인 정보를 다른 페이지에서 사용할 수 있도록 전역 상태값에 저장해준다
- 다만
CheckLogin
은 내부에 렌더링 요소가 전혀 없이, 로직만 수행하는 컴포넌트이기 때문에 로직을 분리하여 커스텀 훅을 작성하는 것이 더 나을 것 같았음
useCheckLogin 훅
// useCheckLogin.ts
export function useCheckLogin() {
// CheckLogin과 완전히 동일한 로직, 단 커스텀 훅으로 변경 후 children prop 삭제
}
// App.tsx
...
const App = () => {
useCheckLogin();
return (
<Routes>
...
</Routes>
);
}
- 위와 완전히 동일한 로직을 가진
useCheckLogin
훅을 만들자- 모든 페이지의 최상위에서 동작한다는 전제는 그대로이므로, 컴포넌트 트리의 최상위 노드인
App
의 로직 부분에 훅을 호출한다 (App.tsx
)
- 모든 페이지의 최상위에서 동작한다는 전제는 그대로이므로, 컴포넌트 트리의 최상위 노드인
- 이 경우 처음 페이지를 열었을 때를 제외하면
App
은 다시 마운트되지 않고 하위 컴포넌트만 마운트와 언마운트가 이루어지는 방식으로 페이지 이동이 구현되어 있기 때문에,useCheckLogin
은App
의 최초 마운트를 제외하면 내부 로직이 실행되지 않는다 (여기서 삽질 엄청 함) App
컴포넌트 대신 모든 페이지 컴포넌트 상단에useCheckLogin
을 호출하면 문제가 해결되지만, 각 페이지와 직접적으로 연관된 로직이 아니므로 최상위에서 지속적으로 동작하는 로직을 만드는 편이 관심사 분리와 코드 유지 측면에서 더 적합하다고 판단했다
useLocation의 도입
export function useCheckLogin() {
const [currentUser, setCurrentUser] = useRecoilState(currentUserState);
const location = useLocation();
useEffect(() => {
if (currentUser.id) return;
fetchCheckLogin()
.then((data) => {
const { data: body, code } = data;
if (code !== 10000) setCurrentUser({ id: null });
else setCurrentUser({ id: body.id });
return code;
})
}, [location]);
}
- 위에서 가진 문제점인
App
의 최초 마운트를 제외하면 내부 로직이 실행되지 않는다 를 해결하기 위해useLocation
훅과location
객체를 사용했다useLocation
은 react-router-dom에서 지원하는 훅으로, 현재 url의 정보를 담은 객체를 반환- 페이지 이동 등으로 url이 변경될 경우,
location
의 내용물 또한 변경
location
객체를useEffect
의 의존성으로 추가하면 페이지 이동 시마다 로직을 호출할 수 있다
currentUser의 유지에 관한 논의
- 만약 사용자가 한 번도 새로고침을 누르지 않을 경우, 전역 상태값이 사라지지 않으므로 서버 측에서의 세션 만료와 관계 없이 클라이언트의 로그인 정보가 유지된다
- 이에 따라
currentUser
을setInterval
등을 통해 주기적으로 reset 시켜주거나, 주기적으로 서버에 요청을 보내 로그인 여부를 체크한다는 방안이 거론됐었음 - 다만 어차피 마이페이지 등 유저 권한이 필요한 페이지의 경우, 페이지를 렌더링하기 위한 데이터를 받아올 때 서버에서 쿠키 만료 여부를 같이 체크하므로 클라이언트의 전역 상태값이 유지되어도 크게 상관이 없다고 느꼈다
- 이걸로 고민하느라 머리가 매우 아팠었던 기억이...
'ClientSide > 기타' 카테고리의 다른 글
CSR과 SSR, SSG (0) | 2023.07.18 |
---|---|
Vercel 앱 GoDaddy 도메인과 연결하기 (0) | 2023.04.02 |
클라이언트에서 Server Sent Event 수신받기 (0) | 2022.12.26 |
클라이언트에서 crypto 모듈 동작 안 할때 해결책 (0) | 2022.10.07 |
XML 파서와 XPath (0) | 2022.10.05 |
Comments