All Articles

객체 매핑을 활용한 2depth 탭 네비게이션

react로 구현한 GNB 최종 결과물

기존 코드 분석

  • 2depth 구조
  • 상위 카테고리 <li> 의 자식요소로 그에 대응하는 하위 카테고리 메뉴 위치
    nav > li > (nav > li ...)
  • 상위 카테고리 클릭시 클래스명으로 DOM 제어
  • 하위 카테고리 <nav>에 aria-hidden 속성
    보조 기술 사용자에게 혼란을 주지 않기 위해 화면에 보이지 않는 하위 카테고리에 {aria hidden: true} 속성 부여

    • aria hidden
      스크린 리더와 같은 보조 기술에 탐색될지 말지를 정하는 속성
      기본값은 undefined
      화면에 시각적으로 보이지 않는 요소(display : block 혹은 visibillity : hidden) 는 보조기술에도 감지되지 않는다.
      화면에 시각적으로 렌더링되지 않지만 명시적으로 숨겨지지 않는 요소는 여전히 접근성 트리에 존재 (overflow: hidden으로 가려지는 요소)
      속성을 부여한 요소와 하위요소 모두 접근성 트리에서 제거

위에 정리한대로 기존 웹 사이트에서는 상위-하위 카티고리 요소가 부모-자식관계로 구조가 짜여있었다. 즉 모든 상하위 카테고리 UI를 모두 렌더한 후에 속성으로 화면에서 보이지 않게 처리한 듯했다. 하지만 사실 상위 카테고리와 하위 카테고리는 각각 데이터만 바뀔 분 같은 UI이기 때문에 리액트에서는 그렇게 요소를 렌더할 필요가 없다고 생각했다. (물론 그렇게 한 이유가 있었겠지만!)

결국 상위 카테고리와 하위 카테고리를 각각 하나의 컴포넌트로, 코드 상에서 형제 관계로 위치시켰다. (상하위 카테고리도 하나의 컴포넌트로 통합하려고 했지만, 생각해보니 둘의 역할이 달랐다. 상: 하위를 연다. 하: 페이지를 이동시킨다.)

구현 과정

로직

  1. 상위 카테고리 클릭
  2. 클릭된 상위 카테고리 저장(activeCategory)
  3. 클릭된 상위 카테고리에 대한 하위 카테고리의 데이터를 렌더 (<—객체 매핑)

데이터 구조

서버에서 받는 데이터의 구조는 다음과 같았다.

사실 원하는 데이터 구조는 ‘상위 카테고리명’에 대한 하위 카테고리 데이터를 담은 객체 형태({상위 카테고리명 : [ ...하위 카테고리 데이터 ]})였지만 이런 구조는 백에서 줄때나, 프론트에서 받을 때나 좋지 않은 구조라고 했다.

그래서 이런 데이터를 받은 후 프론트 단에서 데이터를 가공하여 저장하기로 했다.

{
	category : [{
		"id": 1,
		"korean_name": "상위 카테고리명",
		"english_name": "category name",
		"sub_category": [{
			"id": 1,
			"korean_name": "하위 카테고리명",
			"english_name": "category name",
		}, {..}]
	}, {..}]
}

작성한 코드

  1. {상위 카테고리명 : [ ...하위 카테고리 ]} 형태로 데이터 가공하기
useEffect(() => {
  async function getMenus() {
    const menus = await fetch(API.CATEGORY_TEST);
    const menu = await menus.json();
    setCategories(menu.category);
    setSubCategories(
      menu.category.reduce(
        (sub, c) => ({
          ...sub,
          [c.korean_name]: c.sub_category,
        }),
        {}
      )
    );
  }
  getMenus();
}, []);
  1. 기능 구현
  2. Category
    상위 메뉴 컴포넌트
    카테고리 클릭시, 상위 카테고리 한글명을 인자로 받아 activeCategory state 에 저장하는 함수 handleSubNavOn 호출
  3. SubCategory
    하위 메뉴 컴포넌트
    activeCategory state가 업데이트 되면 하위 카테고리 데이터 변경
const [categories, setCategories] = useState([]);
const [subCategories, setSubCategories] = useState({});
const [activeCategory, setActiveCategory] = useState("");

const handleSubNavOn = (name) => {
  setActiveCategory(name);
};

return (
  <div className="main_nav_box">
    <Category
      list={categories}
      handleClick={handleSubNavOn}
      activeItem={activeCategory}
    />
    {!!activeCategory && (
      <SubCategory
        title={activeCategory}
        list={subCategories[activeCategory]}
      />
    )}
  </div>
);