PageTemplate 정리하기! (with useRouter)

9rganizedChaos·2023년 5월 12일
0
post-custom-banner

회사의 홈페이지는 회사 전반을 소개하는 사이트이다. 그만큼 방대한 정보를 담아내는 것이 코드스테이츠 웹사이트의 임무이다. 다양한 내용을 담아야 하는 만큼 사내 다양한 팀과 협업하게되고, 요청사항 역시 다양하다.
많은 개발자들이 거쳐가기도 했고, 일부 페이지는 외주를 통해 개발되어서, 다양한 코드베이스가 혼재되어있기까지 한 상황이다. 특히 회사에 입사하고 지난 1년반 가량은, 수많은 실험과 요청사항을 최대한 많이 구현해드리는데에 집중해왔었다. 그런 지금 FE개발자들 모두 코드 베이스를 관리하는 것이 너무 힘들다고 판단했다. 앞으로 최소 반년에서 일년 가량은 디자인 시스템을 재정비하고, 가급적 코드를 잘 유지보수할 수 있는 방향으로 개발에 임하기로 결정!

코드를 깔끔히 정리하는 것이 현재 최대 관심사인 만큼, 최근 리소스가 조금 남아, PageTemplate을 정리하는 작업을 진행했다!

AS-IS

🗿 페이지 별로 서로 다른 pageTemplate을 적용!

코드스테이츠 홈페이지를 방문해 여러 페이지들을 돌아다니다보면, 페이지 별로 서로 다른 템플릿이 적용되어 있다는 점을 쉽게 알 수 있다. (아래는 각각, 메인페이지, 코스페이지, 브랜드캠페인 페이지이다!)

그때그때 작업이 이루어지고, 또 어떤 부분은 실험적으로 적용해본 터라, 기존 웹사이트 코드에서 PageTemplate은 단일한 컴포넌트가 아니라, 각각 서로 다른 컴포넌트로 정의되어있었다.

🗿 단일한 PageTemplate이 아니었던 만큼 각 페이지에서 PageTemplate을 사용해주었다!

사실 이 작업을 처음 시작하게 된 것은 지난 번에 포스팅했던 Zustand를 적용하는 과정에서부터였다. Context를 Store로 변경하면서, Context에 있던 단순한 상태와 setter 함수는 모두 Store로 옮겼지만, Context 내부에 있던 컴포넌트와 useEffect 등의 로직은 어딘가로 옮겨야 했다.

  return (
    <SatisfactionSurveyContext.Provider
      value={{
        isSurveyModalOpen,
        isToastOn,
        // ...
      }}
    >
      <SatisfactionSurveyModalInner />
      {children}
    </SatisfactionSurveyContext.Provider>
  );

예를 들면 위와 같은 컨텍스트 파일의 리턴문에서 <SatisfactionSurveyModalInner />를 어딘가로 옮겨야 했던 것이다. 이런 자잘한 컴포넌트와 복잡한 로직들을 모두 _app.tsx로 옮길 생각을 하니 좀 아찔해졌다.
이 때 생각난 것이 바로 <PageTemplate>였다!

TO-BE

🌴 PageTemplate을 하나로 병합!

사실 각 페이지마다 서로 다른 템플릿을 갖고 있다고는 했지만, 그 각각의 템플릿 컴포넌트에는 중복되는 부분도 많았다! 각각으로 따지면 대여섯개쯤 되는 종류였지만, 전체 페이지 구성, 그리고 헤더의 타입으로 구분했고, 하나의 컴포넌트에서 분류에 따라 세부 내용을 렌더해주도록 수정하였다!

export const templateType = {
  FULL_WIDTH: 'fullWidth',
  CENTERED: 'centered',
  NO_TEMPLATE: 'noTemplate'
} as const;

type TemplateType = typeof templateType[keyof typeof templateType];

export const headerType = {
  DEFAULT: 'default',
  BLOG: 'blog',
  NO_MENU: 'noMenu'
} as const;

export type HeaderType = typeof headerType[keyof typeof headerType];

🌴 그리고 비로소 하나로 합쳐준 PageTemplate을 _app.tsx에 적용해주었다.

사실 꼭 이 방법이 아니라, getLayout에 각 페이지에 적용될 Layout을 지정해줄 수도 있다.
getlayout nextjs이라고 검색해보면 금방 해당 방법을 찾을 수 있다. 다만 나의 경우, 해당 방식을 사용할 경우, 결국 다시 페이지 파일 내부에서 해당 페이지에 적용될 레이아웃을 지정해주는 코드를 작성해야 하므로, 페이지에서 Layout을 씌워주는 것에 비해 엄청난 장점이 있는지 잘 모르겠다는 생각이었다.

결국 useRouter를 활용해 각 페이지에 적용될 PageTemplate을 잘 출력해줄 수 있었다.

처음에는 단일한 PageTemplate에서 pathname을 통해 분기해주는 것에 약간의 의구심을 품고 있었다.

1) useRouter는 훅.
2) 그러니, 클라이언트 사이드에서 동작할 때, 제대로 값을 가져올 것.
3) 만약 useRouter가 반환한 pathname을 통해 PageTemplate에서 분기를 쳐주면, 처음 서버사이드에서 렌더링되어 내려온 페이지는 의도한 PageTemplate을 갖고 있지 않겠지!?

위와 같은 생각이었다!

하지만 내 예상이 틀렸다. useRouter를 useEffect처럼 오해했던 것이고, useRouter는 서버에서 렌더링 될 때에도 렌더링 되는 페이지의 pathname을 잘 읽어온다! 개발자도구에서 disabled Javascript를 체크하고 페이지를 로드해보아도, 각페이지에 맞는 PageTemplate이 잘 렌더된다!

useEffect(() => {
  // pathname에 따라, 각 페이지의 템플릿을 지정해주는 코드
}, [pathname])

처음에는 위와 같이 코드를 작성해주었다! 그러나 useEffect는 브라우저에서 hydrate하는 과정에서 돌아가므로 당연히 제대로 동작하지 않았던 것이었다. 그러나 아래와 같이 작성하면 값이 잘 가져와진다!

  const { pathname } = useRouter();
  const template = checkTemplateType(pathname);
  const header = checkHeaderType(pathname);

모쪼록 위와 같은 방식으로 현재 웹사이트 프로젝트 내부에는 단일한 PageTemplate만 존재하며, Context를 통해 전역에서 사용되던 일부 컴포넌트도 PageTemplate 내부에서 잘 관리된다! (이로써 웹사이트 프로젝트 내에 존재하던 모든 Context들도 모두 Zustand Store + 커스텀훅 으로 변환 완료!)

profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 9월 6일

정호님 왜 이런거 이야기안해주셨어요!

답글 달기