블로그 만들기 02 – 옵시디언 연동하기
2025-08-27
#블로그#티스토리#Nextjs#Vercel#Obsidian#Markdown
Obsidian은 마크다운 기반의 개인 지식 관리 도구이다. 단축키 기반의 글쓰기, 링크 연결, 템플릿 활용 등 유용한 기능을 갖추고 있으며, 마크다운 파일을 그대로 가져다 쓸 수 있다는 점에서 정적 블로그와 궁합이 좋다.
이 포스트에서는 Obsidian에서 작성한 글과 이미지를 Next.js 기반 블로그 프로젝트에 자동 연동하는 과정을 기록한다.
✅ 연동 절차
1. Obsidian Vault 생성
-
obsidian-blog라는 이름으로 Vault를 생성한다.
-
기본 폴더 구조는 다음과 같다.
. ├── _assets/ # 이미지 파일 저장 ├── _templates/ # 게시글 템플릿 저장 ├── 폴더1/ │ └── 게시글.md └── 폴더2
2. GitHub 저장소 연동
Vault 디렉토리를 GitHub 저장소와 연결한다.
.obsidian, .git, .github, templates 등은 커밋 및 푸시 대상에서 제외한다.
git init git remote add origin <your-repo-url> echo '.obsidian/' >> .gitignore
3. Git Push 시 블로그 프로젝트에 복사
Obsidian Vault 내 작성된 .md 파일들을 블로그 프로젝트 내 /posts 디렉토리로 복사한다. 이를 위해 pre-push 훅에 아래 스크립트를 추가한다.
- .git/hooks/pre-push
#!/bin/zsh # 📁 최종 타겟 디렉토리 (md 파일 + 이미지 복사) TARGET_DIR="/blog/posts" # 📁 public용 이미지 복사 타겟 PUBLIC_ASSETS_DIR="/blog/public/_assets" # 디렉토리 존재 확인 및 생성 if [ ! -d "$TARGET_DIR" ]; then mkdir -p "$TARGET_DIR" fi if [ ! -d "$PUBLIC_ASSETS_DIR" ]; then mkdir -p "$PUBLIC_ASSETS_DIR" fi # 📦 md, png 파일 + 디렉토리만 posts로 복사 (기존 로직) rsync -av --delete \ --exclude='_templates' \ --exclude='.obsidian' \ --exclude='.github' \ --exclude='.git' \ --exclude='_assets' \ --include='*/' \ --include='*.md' \ --exclude='*' \ ./ "$TARGET_DIR" # 🖼️ _assets만 public으로 따로 복사 rsync -av --delete \ --include="_assets/*" \ --exclude='*' \ ./_assets/ "$PUBLIC_ASSETS_DIR"
이 때, next.js의 경우 public 폴더를 이용해서 이미지 처리를 하는게 수월하므로, "_assets" 폴더만 따로 public에 복사해준다.
4. 이미지 경로 처리
Obsidian 내부 링크 형식인 ![[이미지.png]]은 HTML에서 사용할 수 없으므로, 이를 변환하는 전처리 과정을 추가한다.
// 이미지 처리 export const preprocessObsidianLinks = (markdown: string): string => { return markdown.replace( /!\[\[([^\]]+)\]\]/g, (_, filename) => `<img src="/_assets/${filename.trim()}" alt="${filename.trim()}" />` ); }; // 게시글 불러오기 export const getAllPosts = (): Post[] => { const walk = (dir: string, files: string[] = []): string[] => { const entries = fs.readdirSync(dir); for (const entry of entries) { const fullPath = path.join(dir, entry); if (fs.statSync(fullPath).isDirectory()) { walk(fullPath, files); } else if (entry.endsWith(".md")) { files.push(fullPath); } } return files; }; const fullPaths = walk(postsDirectory); return fullPaths.map((fullPath) => { const relativePath = path.relative(postsDirectory, fullPath); const slug = relativePath.replace(/\.md$/, "").replace(/\\/g, "/"); // cross-platform const fileContents = fs.readFileSync(fullPath, "utf8"); const { data, content } = matter(fileContents); if (!data.id) { throw new Error(`Missing 'id' in frontmatter: ${relativePath}`); } return { id: data.id, slug, // 경로용 보조값으로 유지 가능 title: data.title || slug, date: data.date ? new Date(data.date) : new Date(), tags: data.tags || [], description: data.description || "", content, }; }); }; // 게시글 컨텐츠 파싱 후 적용 import remarkGfm from "remark-gfm"; import rehypeSlug from "rehype-slug"; import { unified } from "unified"; import remarkParse from "remark-parse"; import remarkRehype from "remark-rehype"; import rehypeRaw from "rehype-raw"; import rehypeStringify from "rehype-stringify"; import { extractTOCFromHTML } from "app/_lib/toc"; import { getAllPosts } from "app/_lib/post"; import Content from "app/_components/content"; import { preprocessObsidianLinks } from "app/_lib/markdown"; export default async function PostPage({ params, }: { params: Promise<{ id: string }>; }) { const allPosts = getAllPosts(); const { id } = await params; const post = allPosts.find((p) => p.id === id); if (!post) return <div>404</div>; const rawContent = post.content; const preprocessed = preprocessObsidianLinks(rawContent); const html = await unified() .use(remarkParse) .use(remarkGfm) .use(remarkRehype, { allowDangerousHtml: true }) .use(rehypeRaw) .use(rehypeSlug) .use(rehypeStringify, { allowDangerousHtml: true }) .process(preprocessed); const htmlString = html.toString(); const toc = extractTOCFromHTML(htmlString); console.log("htmlString", htmlString); return ( <div className="relative flex justify-center"> <Content post={post} postContent={htmlString} /> </div> ); }
5. 결과
🎁 부록: 추천 Obsidian 플러그인
1. Git
- Git 커밋 및 푸시를 Obsidian 내에서 바로 수행할 수 있다.
- Vault에 버전 관리와 협업 워크플로우를 자연스럽게 도입할 수 있다.
- 자동 푸시 시간 설정도 가능하다.
2. Templater
- 반복적으로 작성하는 서식을 템플릿으로 관리할 수 있다.
- 날짜, 파일 이름, 사용자 정의 변수 삽입 등 다양한 자동화 기능을 제공한다.
- 나만의 메타데이터 양식 또는 마크다운 문서 뼈대를 손쉽게 만들 수 있다.
🔍 마무리
🌟 좋았던 점
- 마크다운 기반 글 관리와 디렉토리 트리 UI의 결합으로 확장성과 유지보수성이 향상되었다.
- 직접 디렉토리를 파싱하고 구조화하는 과정에서 fs와 Node 환경에 대한 이해가 증진되었다.
- 사이드바 트리 UI에 간단한 애니메이션을 적용하여 사용자 경험이 개선되었다.
😭 아쉬웠던 점
- 현재 이미지 사이즈를 조절하지 못한다..