Fittering 개발 기록 - Next.js 13 redirect 관련 트러블 슈팅

thumbzzero·2023년 8월 9일
1

관련 공식 문서 : https://nextjs.org/docs/app/api-reference/functions/redirect

구현 기능


  • 로그인 안 된 사용자가 서비스 내부 페이지 접속 시 로그인 페이지로 redirect 되도록
  • 로그인된 상태에서 로그인/회원가입 페이지 접근 시 메인 페이지로 redirecte 되도록

로그인 여부 구분 방법

  1. 로그인 시 발급받는 JWT 토큰을 로컬 스토리지에 저장함 -> 로컬 스토리지에 있는 값이 null이면 로그인이 안 된 상태
  2. 로그인 후 1시간이 지나면 토큰 유효 시간이 만료됨 -> 로컬 스토리지에 있는 토큰이 만료된 경우 백엔드 API 요청 시 응답으로 403 코드가 날라옴

구현 과정 & 트러블 슈팅


Error: NEXT_REDIRECT at getRedirectError

customFetch 함수 : 로그인할 때 로컬 스토리지에 저장해 놓은 토큰 값을 기본 헤더로 포함하고, API 요청 BASE_URL을 추가해서 fetch 요청 후 Response를 반환 -> Response의 status가 401이면 로그인 페이지로 redirect 함

[초기 구현 코드]

export async function customFetch(
  requestURL: string,
  options?: RequestInit
): Promise<Response> {
  const headers = { ...defaultHeaders, ...options?.headers };
  const mergedURL = BASE_URL + requestURL;
  const mergedOptions = { ...options, headers };

  const response = await fetch(mergedURL, mergedOptions);

  if (response.status === 401) {
    redirect('/login');
  }

  return response;
}

-> Error: NEXT_REDIRECT at getRedirectError

next.js 깃허브 리포지토리에 남겨진 이슈와 스택 오버플로우를 참고해서 수정해보았지만 거의 해결이 되지 않음

[수정 코드]

export async function customFetch(
  requestURL: string,
  options?: RequestInit
): Promise<Response> {
  const headers = { ...defaultHeaders, ...options?.headers };
  const mergedURL = BASE_URL + requestURL;
  const mergedOptions = { ...options, headers };

  const response = await fetch(mergedURL, mergedOptions);

  if (response.status === 401) {
    window.location.replace('/login');
  }

  return response;
}
/* redirect.d.ts */
export declare function getRedirectError(url: string, type: RedirectType): RedirectError<typeof url>;
/**
 * When used in a React server component, this will insert a meta tag to
 * redirect the user to the target page. When used in a custom app route, it
 * will serve a 302 to the caller.
 *
 * @param url the url to redirect to
 */
export declare function redirect(url: string, type?: RedirectType): never;
/**
 * Checks an error to determine if it's an error generated by the
 * `redirect(url)` helper.
 *
 * @param error the error that may reference a redirect error
 * @returns true if the error is a redirect error
 */
export declare function isRedirectError<U extends string>(error: any): error is RedirectError<U>;
/**
 * Returns the encoded URL from the error if it's a RedirectError, null
 * otherwise. Note that this does not validate the URL returned.
 *
 * @param error the error that may be a redirect error
 * @return the url if the error was a redirect error
 */

Next.js 공식 문서와 redirect() 정의를 보면 redirect()NEXT_REDIRECT 에러를 발생시키는 것은 맞으나 사용 예제를 보면 올바르게 사용한 것 같은데 문제가 되는 이유를 고민해보았다.

그러다 layout.tsx 파일에 로컬 스토리지에서 토큰 값을 읽어왔을 때 null이면 로그인 페이지로 redirect 되도록 redirect('/login');을 코드를 작성했는데 정상적으로 로그인 페이지로 redirect 되는 것을 확인하게 되었다.

확실하지는 않지만 Next.js 공식 문서에 서버 컴포넌트, 클라이언트 컴포넌트, 라우트 핸들러, Server Actions에 사용될 수 있다고 적혀있는데 내가 작성한 customFetch 함수는 여기에 속하지 않아서 그렇지 않을까라고 추측해 보았다.

그러나 이 이유가 확실한지는 확인이 필요할 것 같다.

크롬 - 리다이렉션한 횟수가 너무 많습니다. 에러

위에 작성한 layout.tsx 파일의 로컬 스토리지에서 토큰 값을 읽어왔을 때 null이면 로그인 페이지로 redirect 되도록 하는 redirect('/login');코드

로그인 없이 메인 페이지로 바로 접속하자 로그인 페이지로 redirect가 되었으나 리다이렉션한 횟수가 너무 많습니다 라는 에러가 발생하였다.

-> 해결하기 위해 쿠키 삭제도 해보았으나 해결되지 않음

원인
: 작성한 layout.tsx이 app 디렉토리 안에 바로 있는 가장 상위의 layout 파일이어서 로그인 페이지에서도 적용되는 파일임 -> 로그인 페이지로 redirect 된 이후에도 세션 스토리지에 토큰이 없어 로그인 페이지로 무한대로 redirect가 됨

해결
: Route Group(https://nextjs.org/docs/app/building-your-application/routing/route-groups)을 사용해서 (auth) 디렉토리와 (service) 디렉토리를 구분한 후 layout.tsx 파일을 별도로 둠

0개의 댓글