[TIL] 8월 1일 TIL

imloopy·2022년 8월 1일
0

Today I Learned

목록 보기
43/56
post-custom-banner

현재 프로젝트 기간이기 때문에 자주 글을 쓸 수 없다(물론 노션에다가는 매일 기록하고 있다.)

TL;DR

  • next.js에서 localStorage에 접근할 수 있는 방법?
  • 로그인 모듈을 (기본적인 동작만 하는) 구현
  • 로그인 페이지 구현

로그인 모듈 구현

[ 로그인 구현을 위해 한 삽질들 ]

이슈

Next.js는 서버 사이드에서 실행된다.

  • 서버 사이드는 처음이다.
  • 서버 사이드 + another backend = ??? 나로서는 도저히 이해가 가지 않았다.
  • 그래서 서버사이드의 장점을 사용하진 못했지만, 일단 구현은 해야 하기 때문에 클라이언트 사이드에서 인증을 진행해야 했다.

기존 리액트에서 사용하던 방식대로 사용하면 안된다.

근데 기존 리액트에서 쓰는 방식 처럼, Next.js는 처음에 서버 사이드에서 실행된 다음 클라이언트에서 실행되기 때문이다.

  • localStorage에 바로 접근하는 코드를 리액트 컴포넌트나, 전역으로 설정이 불가능하다. Node.js에는 localStorage가 존재하지 않는다. 브라우저에만 존재하는 객체이다.
  • 같은 이유로, axios interceptor 역시 모듈 스코프에서 코드를 작성하게 되면 서버에서 실행되기 때문에 클라이언트에서 동작하지 않는다.
  • 클라이언트에서 하기로 결정했기 때문에 해당 이슈가 매우 크리티컬하다.

[ 해결 방법은? ]

리액트스럽게 해결한다.

리액트스럽게 해결한다는 것은 무조건 client side에서 해당 코드들이 실행되게 만들면 된다. 무조건 client side에서 해당 코드들을 실행할 수 있는 방법은 useEffect hook으로 감싸면 된다.

그런데, 여기서 문제가 끝이 아니다. 처음 유저 상태를 전역으로 가져와야 하고, 만약 토큰이 만료되었을 때 사용자의 상태를 지워야할 경우 interceptor에서 ContextApi에 선언된 상태에 접근할 수 있어야 한다. 이 때 두 가지 방법이 있다.

  • custom hook으로 내보내기
  • Provider 형식으로 감싸기

나는 두 번째 방식을 선택했다. interceptor의 특성 상 필요한 부분에만 사용되는 것이 아닌, 전역적으로 실행되어야 한다고 생각했다. 프로바이더를 _app.tsx에서 상위 컴포넌트들을 감싸면 initial load일 때 무조건 실행될 것이라고 판단했고, 실제로 프로바이더로 감싸보았더니 interceptor가 잘 동작함을 확인할 수 있었다. 또한 InterceptorProvider에서 AuthContext 내부 데이터들에 대해 접근이 가능함을 확인할 수 있었다.

주의: 감싸는 순서가 중요하다. 가장 처음에는 AuthContextProvider가 되어야 한다.

storage 관련 이슈 해결하기

이것은 온라인으로 찾아보니까 생각보다 간단했다. 마찬가지로 리액트스럽게 useEffect hook을 이용하여 hook 내부에서 사용하는 방식이 있었는데, 이 방식은 Provider와는 반대로 굳이 hook 내부에 선언할 필요는 없겠다고 생각해서, 또 좀 더 쉬운 방식이 있길래 해당 방식은 사용하지 않았다.

클래스로 선언하여 해결하기

클래스로 선언하여 if (typeof window)를 통해 필터링해준다면, 해당 코드는 서버, 클라이언트 모두 실행됨에도 불구하고 서버에서 에러를 내지 않게 된다. 다만, if (typeof window === 'undefined') 코드가 계속해서 중복되므로, 이를 해결할만한 좋은 아이디어가 있는지 찾아볼 필요는 있겠다.

  • Storage.ts
    class LocalStorage {
      static setItem<T = unknown>(key: string, value: T) {
        if (typeof window !== 'undefined') {
          localStorage.setItem(key, JSON.stringify(value));
        }
      }
    
      static getItem<T = unknown>(key: string, defaultValue: T) {
        if (typeof window !== 'undefined') {
          const value = localStorage.getItem(key);
          if (!value || value === 'undefined') return defaultValue;
          return JSON.parse(value) as T;
        }
        return null;
      }
    
      static removeItem(key: string) {
        if (typeof window !== 'undefined') {
          localStorage.removeItem(key);
        }
      }
    }
    
    export default LocalStorage;

아직 과제가 남아있다

next.js 특성상 항상 홈페이지로 진입하는 것이 아닌 여러 페이지로 진입할 수 있기 때문에, initial load일 때 사용자 프로필 정보를 불러와야 한다. 따라서 이 역시 프로바이더 형식으로 감싼 뒤 AuthContext 정보에 접근하여 값을 수정하는 방식을 사용해 보려고 한다.

느낀 점

  • 프로바이더는 처음 만들어봤는데, 생각보다 간단하면서 강력했다. useEffect hook으로 처리하는 방법 외에도 감싸서 처리하는 방법(생각만 해봤지 구현하는 법은 잘 몰랐다) 역시 무조건 실행되어야 하는 경우라면 좋은 방식임을 깨달았다.
  • NextJS에서 클라이언트 사이드로 fetch를 해야 하는데, 그리고 이전에 사용했던 리덕스는 interceptor를 함수로 감싸 redux state에 접근이 가능하도록 구성했던 방식을 ContextAPI에 적용해보면 어떨까?로 시작된 의문이 결실을 맺게되어 기쁘다.
  • 별개로, Next.js의 특성을 잘 활용하지 못하고 있는 점은 아쉽다. 특히 hydration 개념이 어려워서 쉽게 적응하지 못하고 있다. 여유가 되면 충분히 도전해 볼 만한데, 시간은 부족하고 구현은 해야하니 일단 클라이언트 사이드에서 fetch를 진행하고 있다. 다음에 next.js의 특징에 대해 좀 더 깊게 파봐야겠다.

출처

클라이언트 서버 모두에서 nextjs에서 api에러 핸들링하기

🍪 프론트에서 안전하게 로그인 처리하기 (ft. React)

post-custom-banner

0개의 댓글