useTitle 적용 가이드

Elli·2025년 6월 25일
0

개요

요약

각 목록 페이지의 title과 description을 백엔드 api를 통해 받은 메뉴 데이터 값으로 출력하도록 하는 useTitle 컴포저블을 작성하여 host 모듈에서 expose 해두었습니다.

기존 문제점

*** 에서는 메뉴 관리를 통해 메뉴명을 바꿀 수 있으며, 매 버전마다 메뉴명이 조금씩 달라집니다.
기존에는 각 목록 페이지의 title과 description 출력에 프론트의 정적인 값을 사용했기 때문에, 메뉴 관리로 LNB의 메뉴명을 변경했을 때 프론트엔드 소스를 수정하여 재배포하지 않으면 목록의 title과 LNB 메뉴명이 일치하지 않는 이슈가 있었습니다.
이를 프론트 소스 변경 없이 해결하기 위해 [메뉴 관리]에서 설정한 메뉴 데이터의 title과 description을 반환하는 훅을 작성했습니다.

구현 방식

저희 솔루션에서는 route의 path가 달라질 때마다 현재 path(url)가 속한 LNB를 백엔드에서 받은 메뉴 데이터에서 찾고, 찾은 정보를 host의 useMenuStore에 저장해둡니다. useTitle 훅에서는 이 정보들 중 title(현재 path가 속한 LNB 이름)과 description을 반환합니다.

useTitle.ts

// 목록 페이지 title, description을 newMenuStore의 현재 선택된 메뉴 데이터에서 가져오는 훅
import { computed } from 'vue';
import { useMenuStore } from '@/stores/menu/useMenuStore';
export const useTitle = () => {
  const store = useMenuStore();
  const pageTitle = computed(() => store.currentMenuInfo?.lnbMenuName ?? '');
  const pageDescription = computed(
    () => store.currentMenuInfo?.description ?? ''
  );
  return {
    pageTitle,
    pageDescription,
  };
};

적용 가이드

  1. remote 레포지토리 shims-vue.d.ts 파일에 host useTitle 컴포저블 타입 정의를 추가합니다.
// shims-vue.d.ts파일
declare module 'host*****/hooks/useTitle' {
  import { Ref } from 'vue';
  export const useTitle: () => {
    pageTitle: Ref<string>;
    pageDescription: Ref<string>;
  };
}
  1. Title 및 description 적용이 필요한 곳에서 useTitle 컴포저블을 import 하여 사용합니다.
<template>
  <a-flex class="page-title">
    <a-typography-title :level="1">
      {{ pageTitle }}
    </a-typography-title>
    <p class="typography-description">
      {{ pageDescription }}
    </p>
  </a-flex>
</template>

<script setup lang="ts">
import { useTitle } from 'host*****/hooks/useTitle';

const { pageTitle, pageDescription } = useTitle();
</script>

remote 모듈 적용 예시

remote에서는 목록 페이지에 공통으로 ListLayout 컴포넌트를 적용하고 있습니다.

<template>
  <TheLayout>
    <div class="info-container-pull">
      <a-flex class="page-title">
        <a-typography-title :level="1">
          <slot name="title"></slot>
          {{ showTitle }}
        </a-typography-title>
        <p class="typography-description">
          {{ showDescription }}
        </p>
      </a-flex>
      <slot />
      <!--@deprecated -->
      <slot name="table" />
      <slot name="content" />
    </div>
  </TheLayout>
</template>

<script setup lang="ts">
import TheLayout from '@/layouts/TheLayout.vue';
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useT } from '@/hooks/i18n/useT';
import { useTitle } from 'host******/hooks/useTitle';

const { t } = useT();
const route = useRoute();

const props = defineProps<{
  title?: string;
  description?: string;
  /**
   * 백엔드 메뉴 데이터에서 받은 title/description과는 별개의 값으로 출력하고 싶을 때 isStatic 값을 true로 넘겨주시면 됩니다.
   */
  isStatic?: boolean;
}>();

const { pageTitle, pageDescription } = useTitle();

const showTitle = computed(() => {
  const fallbackTitle = t(props.title ?? (route.meta.title || ''));
  return props.isStatic ? fallbackTitle : pageTitle.value || fallbackTitle;
});

const showDescription = computed(() => {
  const fallbackDescription = t(
    props.description ?? (route.meta.subTitle || '')
  );
  return props.isStatic
    ? fallbackDescription
    : pageDescription.value || fallbackDescription;
});
</script>
<style scoped></style>

ListLayout 에서는 기본적으로 useTitle의 pageTitle과 pageDescription을 출력합니다.

Edge case

  1. LNB 데이터에 존재하지 않는 목록 페이지 (ex. 마이페이지)

마이페이지처럼 LNB 데이터에 존재하지 않는 목록 페이지는 menuStore에 현재 메뉴에 대한 정보가 없습니다.
따라서 useTitle의 pageTitle, pageDescription은 빈문자열('')을 반환하며, falsy 값으로 평가되어 fallbackTitle과 fallbackDescription이 출력됩니다.

  1. 메뉴 데이터의 LNB명이 아닌 프론트에서 별도로 정의해둔 title/description을 출력해야 하는 경우

프론트에서 별도로 정의해둔 title/description을 출력해야 하는 경우에는 해당 목록 컴포넌트에서 ListLayout을 호출 할 때 isStatic props를 true로 전달해주시면 됩니다. isStatic이 true일 경우, props로 함께 받은 title, description, 혹은 route.meta에 정의된 값이 출력됩니다.

0개의 댓글