AS-IS

현재 사용자의 상태 관리

useUserState라는 리코일 훅에서 전역으로 사용자의 정보를 관리하고 있다.

// useUserState.ts
import { recoilPersist } from 'recoil-persist'; 
const { persistAtom } = recoilPersist();

const userAtom = atom<RegisterResponse | null>({
  key: 'user',
  default: null,
  effects_UNSTABLE: [persistAtom],
});

export default function useUserState() {
  const getUserAtom = useRecoilValue(userAtom); // 전역 상태를 불러오는 훅
  const setUserAtom = useSetRecoilState(userAtom); // 전역 상태를 업데이트하는 훅

  const isLogin = getUserAtom !== null;
  const userData = getUserAtom;
  const token = getUserAtom?.token;

  const setUser = (data: RegisterResponse) => {
    setUserAtom(data);
    console.log(data.email);
  };
  const clearUser = () => setUserAtom(null);

  return { isLogin, userData, token, setUser, clearUser };
}

새로고침 시 데이터를 보존하기 위해 recoil-persist를 사용하고 있다.

<aside> 💡 recoil-parsist

: recoil의 상태값을 Storage와 동기화해주는 라이브러리

</aside>

그리고, 로그인 후 리다이렉트되는 페이지에서 권한 서버로부터 전달받은 code 값을 백엔드 서버에 전송하여 AccessToken을 전달받는다. 이후, 이 AccessToken을 localStorage에 저장하는 방식이다.

// auth/page.tsx
export default function Page() {
  const code = useSearchParams().get('code');
  const { setUser } = useUserState();
  const router = useRouter();

  useEffect(() => {
    if (code) {
      getRegister(code).then((response) => {
        setUser(response);
        localStorage.setItem('accesstoken', response.token.accessToken);
        alert(`로그인에 성공했어요!`);
        router.back();
      });
    }
  });

  return;
}

localStorage에서 사용자 정보를 받아오고, 데이터를 클라이언트에서 받아오게 된다. 하지만 서버에서 넘어오는 문서에는 데이터가 없다.

Untitled

Untitled

현재 방식의 문제점

서버 상태와 클라이언트 상태를 분리하기 위해 react-query와 recoil을 사용하고 있는데, 이 둘이 현재 혼재되어 있다. 사용자의 정보는 서버로부터 받아온 데이터이기 때문에 서버 상태이다. 만약 저장을 해야 한다면, useQuery의 onSuccess 함수 내부에서 localStorage나 cookie 등 브라우저 저장소에 저장하면 된다.

TO-BE

  1. react-query를 통해 사용자의 정보를 받아오고, 새로고침 이전까지는 캐싱된 데이터를 활용한다.

  2. 여기서 세 가지 방안이 있다.

    1. onSuccess에서 localStorage에 저장한다.
    2. onSuccess에서 cookie에 저장한다.
    3. 따로 브라우저에 저장하지 않는다.

    a안은, Next.js 서버 단에서 접근하지 못한다.

    b안은, Next.js 서버 단에서 접근할 수 있어 SSR 구현이 가능하다. 만약 브라우저 Cookie에 값이 저장되어 있다면, 서버 단에서 따로 호출할 필요가 없다. 그러나 브라우저 Cookie에 접근해야 하므로 SSG 구현은 불가능하다.

    c안도 괜찮은 방법이다. 새로고침하면 페이지 데이터를 새로 불러오듯, 사용자 데이터를 다시 불러오는 것도 괜찮다고 생각한다.

결론

우선, 현재 구현 방식은 사용자의 정보를 localStorage에 저장하는 것이다. localStorage는 서버에서 접근할 수 없으니 cookie로 저장 위치를 변경하고, 백엔드 서버로부터 AccessToken과 사용자 정보를 전달받으면 cookie에 저장하도록 하자.

그리고, Access Token을 로컬 스토리지나 쿠키에 직접 저장하는 것은 보안에 상당히 취약하다. 특히 XSS(JavaScript를 이용한 정보 탈취)에 취약하다. 백엔드 서버에 httponly 쿠키 설정을 할 수 있는지 물어보자.

  1. 로그인 시, 서버로부터 전달받은 Access Token을 우선 쿠키에 저장한다.
  2. 사용자 정보는 쿠키에 저장한다.