Next.js
Next.js App Router 사이트에 cudoc을 설정합니다. Markdown 문법 확장을 먼저 켜고, 그다음 문서 임베드를 더하며, 마지막의 HTML 내보내기는 선택 사항입니다.
먼저 보기: Next.js 데모는 실행 가능한 예제를 Next.js 16.4.2로 빌드한 사이트이며, 빌드에 쓴 Markdown 원문으로 가는 링크가 함께 있습니다. → 호스트별 데모
단계는 순서대로 따릅니다. 1–4단계는 Markdown 문법 확장을 켜고, 5–6단계는 한 문서가 다른 문서를 재사용하게 하는 문서 임베드를 더하며, 7단계는 선택 사항입니다.
이 가이드는 이미 App Router를 쓰는 애플리케이션을 전제로 합니다. @next/mdx 버전은 Next.js 버전에 맞춥니다.
@next/mdx는 로더이지 형식 제한이 아닙니다. 아래 설정은 .md와 .mdx를 함께 컴파일하며, .md만 쓰는 구성도 그대로 지원합니다. → .md와 .mdx 선택
1단계 — 설치
cudoc, remark 어댑터, remark-gfm, @next/mdx, 그리고 @next/mdx에 필요한 패키지 두 개를 설치합니다.
npm install @cudoment/cudoc cudoc-remark remark-gfm @next/mdx @mdx-js/loader @mdx-js/react
2단계 — remark 플러그인 등록
next.config.mjs에 아래를 병합하고, 사이트의 다른 설정은 그대로 둡니다.
// next.config.mjs
import createMDX from "@next/mdx"
const withMDX = createMDX({
extension: /\.mdx?$/,
options: {
format: "detect",
remarkPlugins: [
["remark-gfm"],
["cudoc-remark", { host: "next", syntax: {} }],
],
},
})
export default withMDX({
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
})
플러그인 이름은 문자열로, 옵션은 JSON으로 직렬화할 수 있게 유지해야 합니다. Turbopack이 워커 경계를 넘겨 전달하기 때문에 함수 참조는 살아남지 못합니다. 이 형태는 webpack에서도 그대로 동작합니다.
3단계 — mdx-components.jsx 준비
Next.js가 이 파일을 요구합니다. 이미 있으면 기존 매핑을 그대로 두고 아무것도 더하지 않습니다.
// mdx-components.jsx
export function useMDXComponents(components) {
return { ...components }
}
cudoc의 앵커, 배지, 표 컴포넌트를 등록할 필요는 없습니다.
4단계 — 스타일시트 가져오기
스타일시트는 루트 레이아웃에서 한 번만 가져옵니다.
// app/layout.jsx — add at the top
import "@cudoment/cudoc/styles.css"
문서는 docs/ 아래에 작성하고 기존 MDX 라우팅으로 렌더링합니다. 예를 들어 app/guide/page.jsx가 ../../docs/guide.md를 가져오는 식입니다.
문법 확장만 필요하면 여기서 멈추어도 됩니다. 앱을 실행하면 Markdown 문법의 기능들이 동작합니다. 문서 임베드가 필요하면 계속 진행합니다.
5단계 — 임베드 플러그인과 라이브러리 로더 추가
임베드 플러그인은 같은 remarkPlugins 배열에서 cudoc-remark 뒤에 붙이고, 통과형 로더는 두 번들러 모두에 같은 파일을 대상으로 등록합니다. 둘을 모두 넣은 next.config.mjs는 다음과 같으며, 사이트의 다른 설정은 그대로 둡니다.
// next.config.mjs
import createMDX from "@next/mdx"
import { libraryLoader } from "cudoc-remark/loader"
const withMDX = createMDX({
extension: /\.mdx?$/,
options: {
format: "detect",
remarkPlugins: [
["remark-gfm"],
["cudoc-remark", { host: "next", syntax: {} }],
[
"cudoc-remark/embed",
{ sourceRoot: "docs", outDir: ".cudoc/documents" },
],
],
},
})
const library = libraryLoader(".cudoc/documents")
export default withMDX({
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
webpack(config) {
config.module.rules.push({ test: /\.mdx?$/, use: [library] })
return config
},
turbopack: { rules: { "*.{md,mdx}": { loaders: [library] } } },
})
수집에 roots를 썼다면 임베드 플러그인에도 sourceRoot 대신 같은 목록을 넘겨야 파일이 수집 당시의 ID로 이어집니다.
임베드 플러그인은 각 임베드의 준비된 내용을 페이지가 컴파일될 때 접합하므로, 임베드된 절 안의 컴포넌트도 다른 컴포넌트와 같이 mdx-components.jsx를 거쳐 렌더링됩니다. 그만큼 컴파일된 페이지가 .cudoc/documents/embeds.json에 의존하게 되는데, 번들러는 그것을 볼 수 없습니다.
로더는 번들러가 그 의존성을 보게 합니다. 파일은 그대로 두고, 번들러가 컴파일하는 내용에 라이브러리 해시를 담은 보이지 않는 참조 정의 한 줄을 더합니다. 그래서 라이브러리가 바뀌면 페이지가 바뀐 것으로 개발 서버와 두 번들러의 빌드 캐시가 인식하고, cudoc collect가 이미 컴파일된 페이지에도 닿습니다. → 준비된 임베드 접합
6단계 — 빌드 전마다 수집 실행
수집 설정 파일을 만듭니다. host: "next"를 지정하면 수집이 cudoc-remark가 컴파일하는 방식 그대로 문서를 읽으며, syntax는 next.config.mjs의 옵션과 같게 유지해야 합니다. check.assetDirs는 /img/logo.png 같은 루트 기준 이미지가 어디에 있는지 cudoc check에 알려 줍니다.
// cudoc.config.mjs
export default {
sourceRoot: "docs",
outDir: ".cudoc/documents",
host: "next",
syntax: {},
check: { assetDirs: ["public"] },
}
그다음 package.json에 스크립트를 추가합니다.
{
"scripts": {
"collect": "cudoc collect --config cudoc.config.mjs",
"check": "cudoc check --config cudoc.config.mjs",
"dev": "npm run collect && next dev",
"build": "npm run collect && npm run check && next build"
}
}
수집은 Next.js보다 먼저 돌아야 합니다. 글을 쓰는 동안에는 next dev 옆에서 cudoc collect --watch --config cudoc.config.mjs를 실행합니다. docs/ 아래가 바뀔 때마다 다시 수집하고, 앞 단계의 로더 규칙이 그 결과를 개발 서버가 이미 컴파일한 페이지에도 전달합니다. → 수집 설정
임베드 플러그인은 import도 런타임 컴포넌트도 생성하지 않으며, 작성자가 Markdown에 import를 쓸 일은 없습니다.
수집된 경로는 App Router 경로에 맞춥니다. /help/guide에서 제공되는 guide.md라면 routes: { guide: "/help/guide" }가 필요합니다. basePath를 쓰는 사이트는 모든 경로를 그 아래에서 제공하는데, Markdown 링크는 Next.js가 접두사를 붙이지 않는 평범한 <a>로 렌더링됩니다. 그래서 예제처럼 mdx-components.jsx에서 a를 접두사를 붙여 주는 next/link로 연결합니다.
7단계 — HTML로도 내보내기 (선택)
방금 수집한 라이브러리를 재사용해 전달용 HTML 묶음을 만듭니다.
npm install cudoc-export
npx cudoc-export build docs --library .cudoc/documents --out-dir shared-html \
--links host --host-url https://docs.example.com/project/ --asset-dir public
Next.js 빌드 결과와 수집 데이터는 변경되지 않습니다. → 내보내기
이제 쓸 수 있는 것
앞의 여덟 줄은 1–4단계가 켜는 문법 확장이고 뒤의 다섯 줄은 5–6단계가 더하는 임베드 형태이며, 각 줄은 해당 레퍼런스로 이어집니다.
| 기능 | 예시 | 자세히 |
|---|---|---|
| 명시적 제목 앵커 | ## 한도 (#limits) |
문법 |
| 제목 배지 | ## 한도 (#limits) (@New) |
문법 |
| 제목이 있는 콜아웃 | > [!NOTE] 시작하기 전에 |
문법 |
| 표 셀 안 중첩 목록 | - 계정<br />-- 이메일 인증 |
문법 |
| 표 셀 병합 | 셀에 ^^나 >>만 쓰기 |
문법 |
| 코드 제목과 강조 줄 | ```js title="retry.js" {2} |
문법 |
| 탭 | > [!TAB] npm |
문법 |
| 변수 | (=version) |
문법 |
| 문서 전체 임베드 | sources: [reference.md] |
임베드 |
| 한 절 임베드 | sources: [reference.md#limits] |
임베드 |
| 제목 요약 표 | select: { depth: 2 } |
임베드 |
| 문서 트리 | render: { type: tree } |
임베드 |
| 사본의 문구 치환 | replace: [{ find, replace }] |
임베드 |
Next.js에서 알아 둘 점
두 번들러 모두 지원. 위의 문자열 지정자 형태는 Turbopack과 webpack 양쪽에서 빌드됩니다. 실행 가능한 예제가 둘 다 검증합니다.
.md와 .mdx의 차이. format: "detect"는 .md를 평범한 Markdown으로 두어 {value}가 그대로 글자로 남게 하고, .mdx는 직접 만든 React 컴포넌트를 위한 MDX로 다룹니다. → .md와 .mdx 선택
MDX 목차 (선택). remark 옵션에 toc: true를 더하면 컴파일된 .mdx 모듈이 두 단계 개요인 toc 바인딩을 내보냅니다. 평범한 .md는 ESM export를 받지 않습니다. 개요는 임베드 플러그인이 실행되기 전에 수집되므로, 임베드로 들어온 제목은 페이지의 앵커가 되지만 개요 항목은 되지 않습니다. → 목차 레퍼런스
.md 파일로 거는 링크는 쓴 그대로 남습니다. Next.js는 app/ 아래 파일로 페이지 경로를 정하고 @next/mdx는 링크 주소를 바꾸지 않으므로, [Limits](reference.md#limits)는 사이트가 제공하지 않는 파일을 가리킵니다. 문서와 linkReferences 모두 /reference#limits처럼 경로로 링크합니다. 임베드가 그리는 링크는 이미 수집된 경로를 씁니다.
직접 구성한 remark 파이프라인. 기본 수집기는 위의 표준 cudoc/GFM 구성을 전제로 합니다. 파이프라인이 다르다면 수집과 소스 치환에 반드시 그 컴파일러를 써야 합니다. → 컴파일러 캡처
다음으로
이어서 읽을 문서는 임베드 가이드, 어댑터 내부 동작, 실행 가능한 예제입니다.