내비게이션 메뉴

이 페이지

EmDash 메뉴는 관리 인터페이스를 통해 관리하는 정렬된 링크 목록입니다. 메뉴는 드롭다운을 위한 중첩을 지원하며 페이지, 게시물, 분류 용어 또는 외부 URL에 링크할 수 있습니다.

메뉴 쿼리

getMenu()를 사용하여 고유한 이름으로 메뉴를 가져옵니다:

---
import { getMenu } from "emdash";

const primaryMenu = await getMenu("primary");
---

{primaryMenu && (
  <nav>
    <ul>
      {primaryMenu.items.map(item => (
        <li>
          <a href={item.url}>{item.label}</a>
        </li>
      ))}
    </ul>
  </nav>
)}

해당 이름의 메뉴가 없으면 함수는 null을 반환합니다.

메뉴 구조

메뉴는 메타데이터와 항목 배열을 포함합니다:

interface Menu {
	id: string;
	name: string; // 고유 식별자("primary", "footer")
	label: string; // 표시 이름("Primary Navigation")
	items: MenuItem[];
}

interface MenuItem {
	id: string;
	label: string;
	url: string; // 해석된 URL
	target?: string; // 새 창의 경우 "_blank"
	titleAttr?: string; // HTML title 속성
	cssClasses?: string; // 사용자 정의 CSS 클래스
	children: MenuItem[]; // 드롭다운을 위한 중첩된 항목
}

URL은 항목 유형에 따라 자동으로 해석됩니다:

  • 페이지/게시물 항목/{collection}/{slug}로 해석됩니다
  • 분류 항목/{taxonomy}/{slug}로 해석됩니다
  • 컬렉션 항목/{collection}/로 해석됩니다
  • 사용자 정의 링크는 URL을 그대로 사용합니다

중첩된 메뉴 렌더링

메뉴 항목은 드롭다운 내비게이션을 위한 하위 항목을 가질 수 있습니다. children 배열을 재귀적으로 렌더링하여 중첩을 처리합니다:

---
import { getMenu } from "emdash";
import type { MenuItem } from "emdash";

interface Props {
  name: string;
}

const menu = await getMenu(Astro.props.name);
---

{menu && (
  <nav class="nav">
    <ul class="nav-list">
      {menu.items.map(item => (
        <li class:list={["nav-item", item.cssClasses]}>
          <a
            href={item.url}
            target={item.target}
            title={item.titleAttr}
            aria-current={Astro.url.pathname === item.url ? "page" : undefined}
          >
            {item.label}
          </a>
          {item.children.length > 0 && (
            <ul class="submenu">
              {item.children.map(child => (
                <li>
                  <a href={child.url} target={child.target}>
                    {child.label}
                  </a>
                </li>
              ))}
            </ul>
          )}
        </li>
      ))}
    </ul>
  </nav>
)}

메뉴 항목 유형

관리 패널은 다섯 가지 유형의 메뉴 항목을 지원합니다:

유형설명URL 해석
page페이지로 링크/{collection}/{slug}
post게시물로 링크/{collection}/{slug}
taxonomy카테고리 또는 태그로 링크/{taxonomy}/{slug}
collection컬렉션 아카이브로 링크/{collection}/
custom외부 또는 사용자 정의 URL그대로 사용

모든 메뉴 나열

getMenus()를 사용하여 모든 메뉴 정의(항목 제외)를 검색합니다:

import { getMenus } from "emdash";

const menus = await getMenus();
// 반환: [{ id, name, label, locale }, ...]

이는 주로 관리 인터페이스나 디버깅에 유용합니다.

메뉴 생성

/_emdash/admin/menus의 관리 인터페이스를 통해 메뉴를 생성하거나 관리 API를 사용합니다:

POST /_emdash/api/menus
Content-Type: application/json

{
  "name": "footer",
  "label": "Footer Navigation"
}

메뉴에 항목 추가:

POST /_emdash/api/menus/footer/items
Content-Type: application/json

{
  "type": "page",
  "referenceCollection": "pages",
  "referenceId": "page_privacy",
  "label": "Privacy Policy"
}

사용자 정의 외부 링크 추가:

POST /_emdash/api/menus/footer/items
Content-Type: application/json

{
  "type": "custom",
  "customUrl": "https://github.com/example",
  "label": "GitHub",
  "target": "_blank"
}

재정렬 및 중첩

재정렬 엔드포인트를 사용하여 항목 순서와 부모-자식 관계를 업데이트합니다:

POST /_emdash/api/menus/primary/reorder
Content-Type: application/json

{
  "items": [
    { "id": "item_1", "parentId": null, "sortOrder": 0 },
    { "id": "item_2", "parentId": null, "sortOrder": 1 },
    { "id": "item_3", "parentId": "item_2", "sortOrder": 0 }
  ]
}

이렇게 하면 item_3item_2의 자식이 되어 드롭다운이 생성됩니다.

완전한 예제

다음 예제는 주요 내비게이션이 있는 반응형 헤더를 보여줍니다:

---
import { getMenu, getSiteSettings } from "emdash";

const settings = await getSiteSettings();
const primaryMenu = await getMenu("primary");
---

<html lang="en">
  <head>
    <title>{settings.title}</title>
  </head>
  <body>
    <header class="header">
      <a href="/" class="logo">
        {settings.logo ? (
          <img src={settings.logo.url} alt={settings.logo.alt || settings.title} />
        ) : (
          settings.title
        )}
      </a>

      {primaryMenu && (
        <nav class="main-nav" aria-label="Main navigation">
          <ul>
            {primaryMenu.items.map(item => (
              <li class:list={[item.cssClasses, { "has-children": item.children.length > 0 }]}>
                <a
                  href={item.url}
                  target={item.target}
                  aria-current={Astro.url.pathname === item.url ? "page" : undefined}
                >
                  {item.label}
                </a>
                {item.children.length > 0 && (
                  <ul class="dropdown">
                    {item.children.map(child => (
                      <li>
                        <a href={child.url} target={child.target}>{child.label}</a>
                      </li>
                    ))}
                  </ul>
                )}
              </li>
            ))}
          </ul>
        </nav>
      )}
    </header>

    <main>
      <slot />
    </main>
  </body>
</html>

API 참조

getMenu(name)

모든 항목과 해석된 URL이 포함된 메뉴를 이름으로 가져옵니다.

매개변수:

  • name — 메뉴의 고유 식별자(문자열)

반환: Promise<Menu | null>

getMenus()

항목 없이 모든 메뉴 정의를 나열합니다.

반환: Promise<Array<{ id: string; name: string; label: string; locale: string }>>