조리복 프로젝트 - Next탐구와 SnackBar 구현

Sally·2022년 12월 21일
3

조리복

목록 보기
2/4
post-thumbnail

snackBar 구현 (Next는 조금 다르더라)

팀내에서 스낵바 구현을 맡게 되었다.
저번 리액트 프로젝트에서 스낵바를 구현해보았기에 금방 끝날 줄 알았다...
하지만 개발 환경이 Next였기에 몇가지 STEP이 더 필요했었다. (이 부분 때문에 삽질 좀 했다)
이 글에서는 몇가지 STEP 중에서 _document와 portal을 만들때 유의할 점에 대해서 소개하고자 한다.
지금와 돌아보면, Next가 SSR이라는 대전제를 생각했다면 금방 해결했을 문제였다. 그래도 이 snackBar에서의 깨달음으로 페이지 컴포넌트 구현은 쉽게 할 수 있었다.

Pre-rendering

본격적인 설명에 들어가기에 앞서서, Next의 대표적인 특징인 Pre-rendering부터 짚고 넘어가고자한다. Next는 이점을 이해하고 가면, 이후에 왜 이렇게 코드를 작성해야하는지 이해가 쉽게 된다.

Next의 경우 기존 React에서의 CSR의 단점을 보안한 SSR 프로젝트를 만들기 위해서 나온 프레임워크이다.
그러다보니 기존에 리액트에서처럼 브라우저단에서 자바스크립트를 실행시켜 index.html을 완성하지 않는다. Next는 브라우저에 "완성된 HTML"을 보낸다.

이점 덕분에 Next를 활용하면 유저에게 더 빠르게 화면을 보여줄 수 있고, SEO에도 장점을 가질 수 있다
(*자세한 내용은 여기서)

이해하기 쉽도록 한 번 정리해보고자 한다

Next에서 index.html을 만들기 -> 완성된 index.html 파일을 브라우저에 보내기-> 브라우저는 완성된 화면 유저에게 보여주기 -> 자바스크립트 코드 작동하기

위의 사진은 실제 snackBar를 구현한 코드를 실행시켰을 때에 네트워크 동작이다.
index.html이 document로써 첫번째로 불러와지고 이후에 자바스크립트 파일들이 받아와지고 있는 것을 확인할 수 있다.

하지만 완성된 페이지가 바로 작동되는 것은 아니다.
자바스크립트 코드가 다 동작되기 이전에 유저가 화면을 누르게 되면 화면에서는 아무런 반응이 없게된다.
예를 들어 버튼이 눌리는 것과 같은 상호작용은 일어나지 않는다. onClick과 같은 이벤트는 자바스크립트에 의해 작동되니말이다.

이와 같이 완성된 화면이 보여진 이후, 자바스크립트 코드가 실행됐을 때에 유저와 상호작용이 가능한 상태가 되는 것을 Next에서는 hydration이라고 부른다.

Next에서 SnackBar를 구현하기

index.html => _document로

보통 스낵바를 구현하기 위해서 포탈을 많이들 사용한다.

리액트에서 해당 포탈을 구현하기 위해서는
보통은 기존의 app컴포넌트 (자세히 말하면 root를 id로 가지는 div밑에 들어가는 element)와 독립적으로 가져가기 위해 public 폴더 하위에 있는 index.html에 따로 div 태그를 선언해준다
아래와 같이 말이다.

<body>
	<div id="root"></div>
    <div id="snack-bar"></div>
</body>

그래서 평소와 같이 이렇게 태그를 선언 해주려고 했는데 이내 깨달았다.

Next는 public하위의 index.html를 사용해 스낵바를 만들 수 없다는 것을! 😱
그러면... Next에서는 도대체 어떻게 새로운 태그를 넣어주지?

바로 답은 _document.tsx를 사용해야한다.

_document

Next에서는 기존에 React의 public하위의 index.html의 역할을 _document.tsx로 대체해주었다.
해당 이유는 다음과 같다.

CRA를 통해서 만들어진 리액트 프로젝트에서는 보통 public 폴더 아래에 있는 index.html을 entry HTML file로 사용하는데 반해서, Next는 public하위의 것들을 모두 static assets으로 다루기로 정했기 때문이다. 참조링크

Next는 프레임워크이기 때문에 _document.tsx가 프로젝트내에 적용되기 위해서는 몇가지 지켜야할 규칙들이 있다.

  1. pages 하위 폴더에 선언할 것 (*Next13에서 부터는 pages폴더 하위에 선언하던 것에서 app 폴더 하위에 선언하는 것으로 변경되었습니다 )
  2. _document라고 파일이름을 동일하게 가져갈 것
  3. 'next/document'에서 import 받은 것들을 사용할 것

위의 규칙들을 지켜서 구현한 코드는 아래와 같다.

import { Head, Html, Main, NextScript } from 'next/document';

const Document = () => {
  return (
    <Html lang="ko"> -> 이런식으로 html 태그들에 속성을 부여할 수 있다. 
      <Head />
      <body>
        <Main /> -> 리액트의 index.html의 root역할을 하던 곳 
        <div id="snack-bar"></div> -> 이곳에 새롭게 들어갈 snack-bar 태그 넣어주기
        <NextScript />
      </body>
    </Html>
  );
};

export default Document;

_document를 조작하게 되면 위와 같이 snack-bar id가 붙은 div가 안정적으로 들어가 있는 것을 볼 수 있다.

자 그럼, 이제 div도 잘 들어갔겠다 기존에 리액트에서 하던 것 처럼 Portal을 만들어볼까? 하면 에러가 나게 된다.🥲

Portal 만들기

리액트에서라면

const modalElement = document.getElementById(`${modalId}`);
	if (modalElement === null) {
		throw new Error('모달을 찾을 수 없습니다.');
	}
return ReactDOM.createPortal(children, modalElement);

이렇게만 코드를 작성해도 정상적으로 작동을 했을 것이다.
하지만 Next에서는 document가 정의되어 있지않는다는 에러 메세지를 마주하게 될 것이다.

이 에러는 왜 발생하는 걸까?
그 이유는 앞서 언급했던 것 처럼 Next의 Pre-rendering과 관련이 있다.

Next는 브라우저에 html파일을 전송하기 이전에, 코드를 실행시켜 완성된 html파일로 만들어서 브라우저에 보내게 된다.

그렇다는 것은 최초의 코드 구동 환경이 browser가 아니게 된다.
그런데 document는 다들 알다시피 browser에 종속된 속성이다. 그렇기 때문에 browser가 아닌 서버에서 코드가 실행되게 되면 당연하게도 document는 not defined일 수 밖에 없다.

그렇기 때문에 browser상태가 아닐때 구동하는 상황에 대해서도 생각하여 코드를 작성해야한다.

const [mounted, setMounted] = useState(false);
const portalRef = useRef<Element | null>(null);

  useEffect(() => {
    portalRef.current = document.querySelector<HTMLElement>(`#${modalId}`);
    setMounted(true);
  }, []); -> 컴포넌트가 브라우저상에 최초로 렌더링이 되었을 때 실행되게 된다 

  if (mounted && portalRef.current) { 
  	->mounted 즉, 컴포넌트가 화면에 렌더링 되었을 때 그리고 ref에 값이 있을 때에 returne된다
    return ReactDOM.createPortal(children, portalRef.current);
  }
 return <></>;

이런식으로 useEffect를 활용하여,
브라우저에 컴포넌트가 렌더링 된 이후에 document를 찾도록 지연시킴으로써 정상적으로 작동할 수 있게 한다.

앞으로는

블로그에 작성한 내용을 다시 살펴보니 별거 아닌것 같지만 그래도 그때에는 꽤나 삽질을 했었다.
아무래도, React개발 경험이 주이다보니 browser가 아닌 환경을 염두에 두고 코딩을 하는 것에 익숙하지 않은 탓인 것같다.

Next를 새롭게 공부하면서 새로운 기술에 대해서 익힌다는 것도 좋은 점 이겠지만 한편으로는 이렇게 고려해야할 경우의 수를 늘리면서 나의 코드 세계를 넓혀가는 기분도 든다.
Next의 세계 재밌네 🥹

3개의 댓글

comment-user-thumbnail
2022년 12월 21일

SEO가 뭐에용~~~?

1개의 답글