[TIL #32] Next.js (SSR) + react-responisve 에러

웹 개발을 하다보면 반응형 웹 처리를 항상 마주하게 된다.
처음 React를 공부할 땐 css media로 반응형 처리를 해주었었는데 아무래도 css에서 일일이 구현하다보니 불편해서(귀찮아서) js에서 반응형 처리를 진행하는 react-responsive를 사용하여 구현해왔다.

최근에 토이프로젝트를 진행하는데 이번에는 반응형이 아닌, 모바일 웹으로만 구현하고 싶어서 PC환경으로 접속시 모바일로 접속해달라는 위와 같은 화면을 띄우고 싶어서 react-responsive를 사용해서 다음과 같이 코드를 작성했다.

const isMobile = useMediaQuery({
    query: "(max-width:767px)"
});

// some codes...
<>
    {isMobile ?  
    <PageContainer></PageContainer> :
  	<MobileWarn/>}
</>

isMobile은 모바일 환경일 시 (width>767px) true를 return하고 아닐시에는 false를 return한다.
내가 생각했던대로라면 모바일로 접속시에는 <PageContainer></PageContainer> 을 rendering 하고 PC로 접속시에는 <MobileWarn/> rendering 해야한다.

하지만 모바일로 접속했을때 두 컴포넌트가 섞여서 기괴한 모습으로 화면에 출력되는 요상한 상황을 마주하게 되었다.

무엇이 문제인고.. 하니 프로젝트를 구현할 때 Next.jsgetStaticProps를 이용하여 SSR으로 구현했는데 이게 react-responsive와 만나 문제가 발생하게 된 것이었다.

Next의 SSRServer에서 React 코드를 html, css, js등으로 변환하여 응답해주는 방식을 띄고 있는데, 최초 응답 시 window 객체가 초기화 되지 않아 window에 대한 정보(width, height..)를 참조하지 않은 채로 데이터가 전송된다.

따라서 isMobile을 이용해서 모바일, PC 두 환경으로 나눠서 구현을 했다고 하더라도, 새로고침시에 나타나는 화면은 window 정보를 모르기 때문에 기본 스타일이 적용되어 나타나게 된다.

해결하는 방법은 SSR을 구현한다고 해도 Client Side에서 컴포넌트들이 마운트 되었을 때 isMobile 변수에 대한 정보를 업데이트 해주면 해결이 가능하다.

// useState를 이용해서 isMobile state 생성
const [isMobile, setIsMobile] = useState(false);
const mobile = useMediaQuery({
    query: "(max-width:767px)"
});

useEffect(()=>{	// mobile 쿼리로 인해 값이 바뀔 때 수행
  if(mobile) setIsMobile(true);
},[mobile]);

// some codes...
<>
    {isMobile ?  
    <PageContainer></PageContainer> :
  	<MobileWarn/>}
</>

컴포넌트가 마운트 되고 isMobile State를 업데이트 시켜서 컴포넌트 re-rendering을 발생시키면 처음에 원했던 대로 구현이 가능하다.
또한 위의 코드는 custom Hook으로 만들어 더 쉽게 사용이 가능하다.

export function useIsMobile () {
  const [isMobile, setIsMobile] = useState(false);
  const mobile = useMediaQuery({query: "(max-width:767px)"});
  
  useEffect(()=>{
    setIsMobile(mobile);
  },[mobile]);

  return isMobile;
}

출처
출처

좋은 웹페이지 즐겨찾기