효율적인 lnb만들기 in React ,ts ,next

미마모코딩·2022년 11월 18일
0

팀 프로젝트 경험

목록 보기
1/6
post-thumbnail

오늘은 프로젝트를 진행하던중 피드백받았고 어려웠던점을 적어보겠다.

lnb를 만들고 구성하는데 분기처리의 대한 이슈와 효율성의 이슈였다.

lnb란?

현재 서비스 영역만 해당되는 네비게이션이다.

서브메뉴,중분류 메뉴, 각 서브 분류별 사용되는 메뉴 바이다.

기존의 lnb는

<script>
import LnbCommunity from "@globalComponents/lnbItem/LnbCommunity"
import LnbEducation from "@globalComponents/lnbItem/LnbEducation"
import LnbInfo from "@globalComponents/lnbItem/LnbInfo"
import LnbService from "@globalComponents/lnbItem/LnbService"
import LnbSocialService from "@globalComponents/lnbItem/LnbSocialService"

import { useRouter } from "next/router"
import React, { useState } from "react"
import * as S from "./Lnb.style"


const Lnb = () => {
  const [subTheme, setSubTheme] = useState("")
  const router = useRouter()
  const urlPath: string = router.asPath
  const urlPathArray = urlPath.split("/")
  const urlMainKeyword = urlPathArray[1]

  const lnbRender = () => {
    switch (urlMainKeyword) {
      case "info": {
        const theme = "회사소개"
        return <LnbInfo theme={theme} setSubTheme={setSubTheme} subTheme={subTheme} />
      }
      case "service": {
        const theme = "돌봄서비스신청"
        return <LnbService theme={theme} setSubTheme={setSubTheme} subTheme={subTheme} />
      }
      case "education": {
        const theme = "교육안내"
        return <LnbEducation theme={theme} setSubTheme={setSubTheme} subTheme={subTheme} />
      }
      case "social_service": {
        const theme = "사회서비스"
        return <LnbSocialService theme={theme} setSubTheme={setSubTheme} subTheme={subTheme} />
      }

      case "community": {
        const theme = "커뮤니티"
        return <LnbCommunity theme={theme} setSubTheme={setSubTheme} subTheme={subTheme} />
      }
    }
  }

  return (
    <S.Wrapper>
      <div>{lnbRender()}</div>
    </S.Wrapper>
  )
}
</script>
export default Lnb

로 lnb를 구성했고 각각의 분기처리를 맡았고 5개의 컴포넌트의 내용은 아래와 비슷한 내용의 컴포넌트가 반복됐다.

<script>
import LnbCurrentLocation from "@globalComponents/lnbCurrentLocation/LnbCurrentLocation"
import Link from "next/link"
import React, { FC, useEffect } from "react"

interface Props {
  theme: string
  setSubTheme: React.Dispatch<React.SetStateAction<string>>
  subTheme: string
}
const LnbCommunity: FC<Props> = ({ theme, setSubTheme, subTheme }) => {
  useEffect(() => {
    setSubTheme("공지사항")
  }, [])

  return (
    <div>
      <LnbCurrentLocation theme={theme} subTheme={subTheme} />
      <div className="lnb_container">
        <h3 className="lnb_main_theme">커뮤니티</h3>
        <ul className="lnb_list">
          <Link href="http://localhost:3000/community/notice">
            <a>
              <li
                className={subTheme === "공지사항" ? "checked_subtheme" : "defalut"}
                onClick={() => setSubTheme("공지사항")}
              >
                공지사항
              </li>
            </a>
          </Link>
          <Link href="http://localhost:3000/community/parenting_information">
            <a>
              <li
                className={subTheme === "육아정보" ? "checked_subtheme" : "defalut"}
                onClick={() => setSubTheme("육아정보")}
              >
                육아정보
              </li>
            </a>
          </Link>
          <Link href="http://localhost:3000/community/gallery">
            <a>
              <li
                className={subTheme === "포토갤러리" ? "checked_subtheme" : "defalut"}
                onClick={() => setSubTheme("포토갤러리")}
              >
                포토갤러리
              </li>
            </a>
          </Link>
          <Link href="http://localhost:3000/community/qna">
            <a>
              <li
                className={subTheme === "질문과답변" ? "checked_subtheme" : "defalut"}
                onClick={() => setSubTheme("질문과답변")}
              >
                질문과답변
              </li>
            </a>
          </Link>
        </ul>
      </div>
    </div>
  )
}
</script>
export default LnbCommunity

useEffect를 사용하여 첫페이지에 접속하면 첫페이지의 1장에 해당하는 setSubTheme를 정해주었다.

이런 상황에서 어떻게 줄여나가고 반복사항을 어떻게 줄일까의 고민에 봉착했다.

쉽게 잘 변경되지않는 nav바의 depth에 대한 영역이기에 dummy data를 만들고 분기처리를 하면 효율적으로 처리할 수 있을 것 같다고 피드백을 들었다.

그렇기때문에 개선의 대한 구조와 중복제거의 중점을 두고

<script>
export const data: MenuList = [
  {
    title: "회사소개",
    depth1: "info",
    href: "/info",
    list: [
      { title: "대표인사말", href: "/info" },
      { title: "회사연혁", href: "/info/history" },
      { title: "사회적기업", href: "/info/se" },
      { title: "오시는길", href: "/info/map" }
    ]
  },
  {
    title: "돌봄서비스신청",
    depth1: "service",
    href: "/service",
    list: [
      { title: "돌봄서비스안내", href: "/service/info" },
      { title: "돌봄서비스신청", href: "/service/service" },
      { title: "이용요금안내", href: "/service/price" },
      { title: "자주하는질문", href: "/service/questions" },
      { title: "돌봄이용후기", href: "/service/review_babysitter" }
    ]
  },
  {
    title: "교육안내",
    depth1: "education",
    href: "/education/babysitter",
    list: [
      { title: "아동양육지도사", href: "/education/babysitter" },
      { title: "놀이학습지도사", href: "/education/play" },
      { title: "보육사 지원안내", href: "/education/job" },
      { title: "보수교육", href: "/education/additional_education" },
      { title: "교육안내", href: "/education/educational_guidance" }
    ]
  },
  {
    title: "사회서비스",
    depth1: "social_service",
    href: "/social_service/current",
    list: [
      { title: "사회서비스 현황", href: "/social_service/current" },
      { title: "보육서비스와 사회적기업", href: "/education/play" }
    ]
  },
  {
    title: "커뮤니티",
    depth1: "community",
    href: "/community/notice",
    list: [
      { title: "공지사항", href: "/community/notice" },
      { title: "육아정보", href: "/community/parenting_information" },
      { title: "포토갤러리", href: "/community/gallery" },
      { title: "질문과답변", href: "/community/qna" }
    ]
  }
]
</script>

위와같이 dummy를 만들었다.

그리고

<script>
import Link from "next/link"
import { useRouter } from "next/router"
import React from "react"
import { data } from "../header/_Gnb_desktop"
import * as S from "./MainLnb.style"

function MainLnb() {
  const router = useRouter()
  const aspath = router.asPath
  const currentMenu = router.asPath.split("/")[1]

  const menuItemObj = data.find((currentLocationData) => currentLocationData.depth1 === currentMenu)
  const menuDepth_1_title = menuItemObj?.title

  const menuList = menuItemObj?.list

  if (!menuList) return //타입가드 메뉴리스트가 없을 수 있기때문에
  const menutitle = menuList.filter((menuItem) => (menuItem.href === aspath ? menuItem.title : null))[0].title
  //메뉴리스트 순회하면서 현재 패스와 아이템의 href가 일치하는 case를 찾고 일치하면 일치한 title을 뿌림 배열반환이니까 [].title로접근

  return (
    <S.Wrapper>
      <div className="current_location_info_box">
        <div className="character_img_container">
          <img className="character_img" src="https://dasar.co.kr/images/img_sub_ico.png" alt="character_img" />
        </div>
        <h3 className="menu_category_title">{menuDepth_1_title}</h3>
        <span className="classification_mark"> &gt; </span>
        <span className="menu_category_depth1">{menutitle}</span>
      </div>
      <div className="baby_img_container">
        <img className="baby_img" src="https://dasar.co.kr/images/img_sub.png" alt="baby_img" />
      </div>
      <ul className="menu_list">
        <h3 className="menu_category">{menuDepth_1_title}</h3>
        {menuList?.map((menuItem, i) => {
          const itemStateClass = aspath === menuItem.href ? "checked_lnb_list_item" : "lnb_list_item"
          return (
            <div key={i}>
              <Link href={`${menuItem.href}`}>
                <a>
                  <li className={itemStateClass}>{menuItem.title}</li>
                </a>
              </Link>
            </div>
          )
        })}
      </ul>
    </S.Wrapper>
  )
}

export default MainLnb
</script>

기존의 5개의 컴포넌트 ,글로벌 css ,불필요한 props 드릴링 이슈로 위처럼 개선했다 .

코드해설

asPath를 split으로 자르고 인덱스 값을 추출하여 원하는 현재 경로를 추출했다.
data라는 dummy데이터를 임포트해서 data.find를 통해 배열을 순회하면서 위에서 추출한 값이랑 비교하며 첫번째로 나온 요소를 리턴한다.

그 리턴값을 통하여 list배열의 접근하고 그 값으로 맵을 돌려서 안에 요소들을 화면에 그려주고 현재 path와 href가 같다면 클레스를 조건부로 넣어 현재 경로에대한 css요소를 추가해주었다.

router처리와 asPath와 더미의 href값을 적절하게 설정하여 불필요하게 반복되는 현상을 제어 할 수 있었다.

0개의 댓글