[TIL NINJA] 21/05/01 헤더 마운트 & 로그인 리다이렉트

21959 단어 TILTIL

window 객체는 서버사이드와 렌더 단에서 사용할 수 없다.

 <a onClick={onClickLoginButton}>{window.location}</a>


다음과 같이 에러 발생 !

window객체에서 location을 얻어 전달해야하는 경우.

  const onClickLoginButton = () => {
    location.current = window.location.href.split(
      `${window.location.origin}/`,
    )[1];
    router.push(
      { pathname: '/login', query: { pathname: location.current } },
      '/login',
    );
  };

다음과 같이 클릭 시 window 객체에 접근해서 location을 따오도록 한다.

next/link router vs window.location

  1. router.patname으로 현재 url 가져올 수 있으나, /post/161의 경우 /post/[post]로 값을 가져옴. 161router.query에 담겨서 전달됨. 불편...
  2. window.location은 현재 url 그대로 가져온다. ( 내가 원하던거 )

useEffect에서 url값 가져오지 말고, onClick에서 url값 가져오게 한다

현재 로직
헤더의 로그인 버튼을 누르면, 그 때의 url을 쿼리에 넣어서 로그인 페이지로 이동한다.( 페이지 별로 헤더 다시 마운트 됨 , 그래서 useEffect() 내부에서 url 가져와도 되었음. )

문제점
1. 우선 헤더가 계속 새롭게 마운트 되는 것부터 문제. ( 헤더는 페이지별로 달라지지 않으므로 새롭게 마운트 시킬필요가 없다)
2. 같은 페이지에서 쿼리로만 내부 컴포넌트 변경시켜서 렌더링하는 경우 ( url을 갱신 시키지못함. query가 변했으므로 url 갱신 시켜야하는데 그러지 못함)

해결
Header컴포넌트가 AppLayout에 있고 AppLayout을 페이지 별로 부르고있음. 이렇게 하지말고 _app.js에서 AppLayout을 불러 헤더는 새롭게 마운트 시키지 않도록 바꾸어야함.

  • _app.js
const App = ({ Component, pageProps }) => {
  const router = useRouter();

  /** Google Tag Manager */
  useEffect(() => {
    const handleRouteChange = (url) => GTMPageView(url);
    Router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      Router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, []);

  /** Scroll Height Restoration */
  useEffect(() => {
    const cachedPageHeight = [];
    const html = document.querySelector('html');

    Router.events.on('routeChangeStart', () => {
      cachedPageHeight.push(document.documentElement.offsetHeight);
    });

    Router.events.on('routeChangeComplete', () => {
      html.style.height = 'initial';
    });

    Router.beforePopState(() => {
      html.style.height = `${cachedPageHeight.pop()}px`;
      return true;
    });
  }, []);

  const { title = '오즈의 제작소', description } =
    pageMetaData[router.asPath] || {};

  return (
    <>
      <Head>
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
        />
        <style>{fontFace}</style>

        {/* Open Graph */}
        <meta property="og:type" content="website" />
        <meta property="og:title" content={title} />
        <meta property="og:description" content={description} />
        <meta property="og:url" content="https://ozjejakso.com" />
        <meta
          property="og:image"
          content="https://ozjejakso.com/image/ogImg.png"
        />

        {/* Twitter Card */}
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:site" content="@ozjejakso" />
        <meta name="twitter:creator" content="@ozjejakso" />
        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        <meta name="twitter:url" content="https://ozjejakso.com" />
        <meta
          name="twitter:image"
          content="https://ozjejakso.com/image/twitCardImg.png"
        />

        <title>{title}</title>
        <meta name="description" content={description} />
      </Head>
      <GlobalStyle />
      <ThemeProvider theme={Theme}>
        <AppLayout>
         <Component {...pageProps} />
        </AppLayout>
       
      </ThemeProvider>
    </>
  );
};
    <AppLayout>
         <Component {...pageProps} />
    </AppLayout>

이렇게 _app.js에서 AppLayout으로 감싸면 페이지별로 새롭게 헤더 마운트 안함.

  • useEffect가 아닌 onClickHanlder에서 url을 가져오도록함.
    내가 원하는 것은 로그인 페이지로 전달되는 쿼리에 url값을 넣어주는 것임. 그렇다면 로그인 버튼을 눌렀을 때! url값을 가져와서 전달해주면 되는것!!

location(url을 담는 변수)를 useRef()로 선언한 이유

location을 useRef 변수로 선언한 이유 : location 값을 onclick함수내부에서 바꾼다. 이 때 렌더링을 다시 할 필요가 없음. (render() 내부에서 사용하는 변수가 아니라서). 불필요한 렌더링을 막고자 useRef로 사용하여 선언.
렌더링 될 때마다 새롭게 location 변수를 로드할 필요가 없자나!! 그래서 useRef로 한거야

useCallback() 복습

메모이제이션된 콜백을 반환합니다.

인라인 콜백과 그것의 의존성 값의 배열을 전달하세요. useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.

const fun = useCallback(()=>{
	console.log(state);
},[state])
state가 변경 될 때만 새롭게 함수를 로드시킴. 원래라면 렌더링 할 때마다 컴포넌트 내부에서 선언한 변수 함수 다시 로드하는데 useCallback으로 감싸면 횟수 줄일 수 있다.

**만약 빈 배열이라면 ?**

```javascript
const fun = useCallback(()=>{
	console.log(state);
},[])

state가 변경되어도 새로운 state를 참조하지 못하고 처음 함수 로드 되었을 때의 state값만을 참조하게됨. 계속 초기값만 찍힐 것임.
useMemo(() => fn, deps)와 같을까?
같다. useCallback은 인자로 들어간 함수를 의존성 배열이 변할 때 새롭게 만들어 리턴하는 것이고, useMemo도 인자로 들어간 함수의 리턴값을 의존성 배열이 변할 때 전달하는 것이라서 그렇다.

Reference

좋은 웹페이지 즐겨찾기