JWT

[JWT] 로그인 세션 관리하기 2 (Session Storage, Local Storage, Recoil)

마뇨기 2024. 2. 1. 16:37

2024.02.01 - [javascript] - [JWT] 로그인 세션 관리하기 1 (JWT에 관하여)

 

[JWT] 로그인 세션 관리하기1 (JWT에 관하여)

오늘이 아시안컵 16강이 있던 날인데, 축구를 보니 볼 차고 싶은 생각이 너무 들던 하루,,, (사진은 축구하고 있는 나) 오늘은 프론트엔드를 개발하면서 로그인 세션을 어떻게 관리하였는지 작성

mini-frontend.tistory.com

 

이전 글에서 JWT에 대해 알아보았다.

그렇다면 프론트엔드에서 JWT가 어떻게 쓰이는지 한 번 알아보자.

 

 


 

1. JWT 보관 방법

JWT 토큰을 서버로 부터 발급받는다면 브라우저 등에 보관해야 한다. 프론트엔드를 개발할 때 JWT 토큰을 보관하는 방법을 다음과 같다.

 

1) Local Storage

  • 브라우저를 닫아도 데이터가 유지된다. 사용자가 다시 접근할 때 토큰을 재사용할 수 있다.
  • XSS (Cross-Site Scripting) 공격에 취약할 수 있으며, 데이터 계속해서 보관되기 때문에 악의적인 스크립트가 Local Storage에 접근하여 토큰을 탈취할 수 있다.

2) Session Storage

  • 탭이나 브라우저가 닫히면 데이터가 사라지기 때문에, Local Storage 보다는 토큰 탈취에 대한 위험을 줄일 수 있다.
  • XSS (Cross-Site Scripting) 공격에 위험이 있긴 하다.
  • 브라우저 탭이나 윈도우가 닫히면 토큰이 사라지므로, 사용자가 브라우저를 재시작할 때마다 다시 로그인해야 할 수 있다.

3) 상태 관리 라이브러리 (Recocil, Redux 등)

  • 탭 혹은 브라우저가 닫히거나 페이지가 새로고침되면 데이터가 사라진다.
  • XSS (Cross-Site Scripting) 공격에 위험이 있다.
  • 토큰 데이터가 사라질 경우가 많기 때문에 사용자가 자주 로그인해야 하는 불편함이 있다.

4) Secure, HTTP-Only Cookie

  • Secure Cookie : 오직 암호화된 HTTPS 연결을 통해서만 전송된다.
  • HTTP-Only Cookie : HTTP(S) 통신을 통해서만 접근할 수 있다. 이는 JavaScript와 같은 클라이언트 측 스크립트를 통한 직접적인 접근이 차단된다.
  • SecureHTTP-Only 속성은 종종 함께 사용된다. 이렇게 함으로써, 쿠키는 HTTPS 연결을 통해서만 전송되며, 클라이언트 측 스크립트에 의해 접근될 수 없다. 이는 쿠키를 이용한 다양한 보안 위협으로부터 보호하는 효과적인 방법이다.

 


 

 

2. 프로젝트에서 선정한 보관 방법

 

결론적으로 말하자면 accessToken은 Session Storage, refreshToken은 Local Storage에 담았다.

 

1) Secure, HTTP-Only Cookie를 사용하지 않는 이유

 

 4가지 방법을 살펴보았을 때, Secure, HTTP-Only Cookie를 사용하여 토큰을 보관하는 것이 보안 측면에서 가장 안전할 수 있다.

 토큰을 서버와 단순히 통신만 한다면 Secure, HTTP-Only Cookie를 사용하는 것이 효과적이다. 하지만, accessToken에 담겨있는 데이터를 확인이 필요할 때는 직접 accessToken을 열어야 하기 때문에 Secure, HTTP-Only Cookie를 사용할 수 없다.

 프로젝트에서는 accessToken에 있는 데이터에 따라 특정 동작을 하도록 프론트에서 처리했기에 Secure, HTTP-Only Cookie을 사용할 수 없었다.

 

그렇다면 refreshToken은 Secure, HTTP-Only Cookie에 보관할 수 있지 않을까?

 눈물이 나게도 Secure, HTTP-Only Cookie는 프론트에서 구현하려면 Next.js를 사용하여 서버 사이드 렌더링을 사용하여야 한다고 한다...

 하지만, 아직 Next.js에 대해 공부를 하지 않은 상태.. 아니면 백엔드에서 구현하는 게 맞지만, 백엔드 분의 개인 사정으로 더 개발을 하지 못하는 상태라 프론트에서 최선의 방법을 찾아야 했다.

 

2) 상태 관리 라이브러리를 사용하지 않는 이유

 

이 방법을 자주 쓰는지는 모르겠다.

전역 상태 관리는 브라우저나 탭을 닫거나 새로고침을 한다면 해당 데이터가 없어지기 때문에 토큰이 자주 교체된다. 이는 보안 측면에서 큰 이점이 있다.

상태 관리 라이브러리에 익숙해질 겸 이 방법을 사용하려고 했지만, 다음과 같은 문제가 있어 사용하지 않았다.

 

1. React 컴포넌트 외부에서 직접 참조할 수 없다.

 

프로젝트에서는 Recoil을 통해 상태 관리를 하고 있다. Recoil 특성상 React 컴포넌트 내부에서 사용하여야 한다. 하지만, axios 인터셉터가 컴포넌트 위부에서 사용되기 때문에 Recoil을 사용할 수 없다.

 

2. 새로고침 하면 없어지는 데이터들

 

Recoil을 사용한다면 새로고침을 하였을 때 상태 관리에 있던 데이터는 모두 초기화 된다. 만약 사용자가 새로고침을 하였을 때 다시 로그인을 해야 한다고 생각한다면 사용자 입장에서 너무 불편할 것이라 들었다.

 

3) 결국 Session Storage, Local Storage

 

결국 보안을 위해 자주 업데이트 할 필요가 있는 accessToken은 Session Storage, accessToken보다 긴 기간 보관이 필요한 refreshToken은 Local Storage에 보관하였다.

 

 


 

3. 프로젝트 코드

 

export async function LoginAPI(account: string, pw: string) {
  try {
    const res = await axios.post(`${URL}/login`, {
      account: account,
      password: pw,
    });
    const accessToken = res.headers["authorization"];
    sessionStorage.setItem("accessToken", accessToken);
    return res;
  } catch (error) {
    alert("로그인에 실패하셨습니다.");
    throw error;
  }
}

 

서버에서 login API를 작동하면 body에는 refreshToken을, header에는 accessToken을 넘겨받도록 하였다. 받은 accessToken은 Session Storage에 보관하였고, 코드상에 보이지 않지만, refreshToken은  Local Storage에 보관한다.

 

 


 

이번에는 토큰이 어떻게 보관되는지 작성 해보았다.

다음에는 토큰이 만료된다면 프론트엔드에서 어떻게 처리할지 알아보자.