[LinkBrary] Next.js 쿠키 만들기 ( with. API Routes )

 

 

 

 

 

 


개발 환경

- Next.js 14 (page router)

- TailwindCSS

- Axios

 

문제 상황

 

BE에서 API를 통해 JWT을 보내주어 AccessToken을 통해 사용자 인증을 하는 방식을 사용하고 있으며, 코드잇 부트캠프 내에서 하는 프로젝트이기에 BE API를 변경할 수 없었고, RefreshToken 없이 AccessToken만을 사용해 인증을 하고, AccessToken의 주기가 짧아 자주 로그인을 해야했습니다.

 

또한, 현재 웹스토리지(LocalStorage)를 사용하여 SSR(서버사이드 렌더링) 시 로그인(사용자 인증)이 필요한 서비스(API)는 사용할 수 없었습니다. 

 

  • 문제점 요약
    • 짧은 토큰 수명때문에 API 통신에 있어 401(UnAuthorized)에러가 자주 발생
    • SSR 방식을 사용하기 위해 서버와 클라이언트에서 모두 접근할 수 있는 곳에 토큰을 저장하여 접근해야함
    • 프로젝트 진행 중이기에, 다른 팀원들의 코드 변경 없이 스토리지 변경

 

문제 해결을 위한 고민

1. 토큰 내에 저장되어 있는 수명을 보고 토큰이 만료되면 로그아웃을 하자!

  - 토큰 만료시간이 되면 자동으로 삭제해주는 쿠키를 사용하여 토큰을 관리하면, 서버와 클라이언트에서 모두 접근 가능하여 필요한 곳에서 사용할 수 있다. 

  - 토큰이 쿠키에 저장되어 있는지 확인만하면 현재 사용자가 로그인(사용자 인증)이 되어있는지 확인할 수 있어 추가 로직이 필요하지 않다.

 

2. 쿠키 말고 다른 방법은 없을까?

  - 개별 데이터 베이스를 두어 토큰을 관리하자. : 토큰만을 위한 추가적인 서버가 필요하며, 용량이 커지면 추가적인 비용 지불이 필요하다.(추가적으로 데이터 베이스에 저장할 데이터가 없음)

  - 웹스토리지(로컬, 세션)에 저장하면 서버에서 토큰에 접근할 수 없어 제한이 된다.

 

3. 쿠키를 만들 순 없을까?

  - 클라이언트에서 쿠키를 만들 수 있으며, 관련된 라이브러리(react-cookie, cookie 등..)가 많아서 만드는데 문제가 없다.

  - Next.js의 API routes를 활용하여 서버에서 httponly 등의 서버에서만 지정 가능한 설정도 만질 수 있다!

 

4. httponly를 쓸 순 없을까?

  - 클라이언트에서 접근하지 못하도록 막아서 보안을 높을 수 있음

  - Next.js의 API routes를 활용해서 토큰을 관리하는 API를 만들어 사용하자.

 

구현 코드

토큰 저장 API

// src/pages/api/save-token.ts

const expireOffeset = 7200; // 2시간

const saveTokenInCookie = async (req: NextApiRequest, res: NextApiResponse) => {
// ...
    const accessToken = req.body.accessToken; // accessToken을 request에서 가져옴
    if (accessToken !== req.cookies.accessToken) {
      // 헤더에 Set-Cookie설정을 넣어 브라우저가 자동으로 쿠키를 저장할 수 있도록함
      res.setHeader('Set-Cookie', [
        `accessToken=${accessToken}; max-age=${expireOffeset}; path=/; httponly; secure; sameSite=lax;`,
      ]);
// ...

 

토큰 삭제 API

// src/pages/api/delete-token.ts
const deleteTokenFromCookie = async (
  req: NextApiRequest,
  res: NextApiResponse,
) => {
// ...
	// 쿠키의 수명을 0으로 설정하여 삭제
    res.setHeader('Set-Cookie', ['accessToken=deleted; Max-Age=0; path=/']);
// ...

 

토큰 가져오기 API

// src/pages/api/get-token.ts

const getTokenFromCookie = async (
  req: NextApiRequest,
  res: NextApiResponse,
) => {

    const accessToken = req.cookies.accessToken;

    if (accessToken) {
      res.status(200).json({ accessToken });
    } else {
      res.status(401).json({ message: '로그인이 필요한 서비스 입니다.' });
    }
  // ...
};

export default getTokenFromCookie;

 

Axios interceptor 안에 로직 삽입

// axios instance
const onRequest = async (config: InternalAxiosRequestConfig) => {
  if (process.env.NODE_ENV !== 'production') {
    console.log(config);
  }
  if (!config.url?.startsWith('/auth')) {
    try {
      const data = await axios.get('/api/get-token/cookie');
      if (data.data.accessToken) {
        const accessToken = data.data.accessToken;
        if (accessToken) {
          config.headers.Authorization = `Bearer ${accessToken}`;
        }
      }
    } catch {}
  }

// ...

const onResponse = async (value: AxiosResponse) => {
  if (value.data?.accessToken) {
    try {
      const data = await axios.post(
        '/api/save-token/cookie',
        {
          accessToken: value.data.accessToken,
        },
        {
          headers: {
            'Content-Type': 'application/json',
          },
        },
      );
  // ...
  return value;
};

// ...

instance.interceptors.request.use(onRequest, onError);

instance.interceptors.response.use(onResponse, onError);

 

작동 테스트

로그인

(로그인 API 응답 -> axios interceptor response -> cookie 저장 )

 

로그아웃

(로그아웃 -> 쿠키 삭제 api route 호출)

 

게시글 조회

(폴더와 링크 조회 API 호출-> Axios interceptor request -> 쿠키 가져와서 header에 파싱 -> API 호출 계속) 

 

마무리

제한된 상황에서 내가 할 수 있는 것들은 무엇인가 확인하고, FE/BE를 구분하지 않고 더 좋은 서비스를 위해 고민한 경험이었고, 

"유저 정보를 저장하는 방식으로 세션과 쿠키 방식이 있다" 라고 공부는 했었지만, 두 방식의 장단점을 비교하면서 자주 사용해볼 기회가 없었던, 또 직접 만들어본 경험이 없었던 쿠키를 다루어 볼 수 있는 경험이 되어 알찼습니다.

 

(+ 추가 해결해볼 문제)

로그인을 한 후에, 사용자가 페이지에서 떠난 후 다시 돌아왔을 때 쿠키의 만료 시간 내면 자동 로그인이 되어야하는데

메인 페이지에 있는 Navigation Bar 안 로그인 버튼이 깜빡거리는 버그를 수정해볼 생각입니다.

 

전체코드

LinkBrary - 나만의 링크를 모아보세요 ☘️

반응형

'PROJECT > 에러핸들링' 카테고리의 다른 글

[OPENMIND] 반응형 리스트 요소 개수 개선 - UX  (3) 2024.07.26