기술

BlockNote 로 쓴 글, 검색봇이 받은 본문은 117자였습니다

2026.10.288분 읽기

자사 블로그의 글쓰기 화면은 'BlockNote'(노션처럼 블록 단위로 글을 쓰는 React 용 에디터)로 만들었습니다. 슬래시 메뉴, 드래그 핸들, 표, 이미지 업로드가 처음부터 들어 있어 붙이는 데 오래 걸리지 않았습니다. 글은 브라우저에서 멀쩡히 보였고, 한동안 문제가 있다고 생각하지 않았습니다.

이상한 신호는 Search Console 에서 나왔습니다. '발견됨 - 현재 색인이 생성되지 않음' 상태로 묶인 주소가 38건 쌓여 있었습니다. 구글이 주소는 알고 있는데 색인에는 넣지 않았다는 뜻입니다. 글을 써도 검색에는 걸리지 않는 상태였습니다.


검색봇이 받은 본문은 117자였습니다

화면이 아니라 서버가 내보내는 HTML 원문을 직접 받아 봤습니다. 2026년 8월 30일에 잰 값입니다. 실제 본문은 3,253자였는데, 응답 HTML 안에 들어 있는 본문은 117자였습니다. 제목과 껍데기 몇 줄만 있고 글 내용은 거의 없었습니다.

원인은 HTML 을 만드는 위치였습니다. BlockNote 는 글을 HTML 이 아니라 '블록 JSON'(문단·제목·표 같은 블록을 배열로 담은 데이터)으로 저장합니다. 화면에 낼 때는 이 JSON 을 HTML 로 바꿔야 합니다. 저는 이 변환을 브라우저에서 에디터 인스턴스를 만들어 처리하고 있었습니다.

에디터 인스턴스는 브라우저의 DOM 이 있어야 돌아갑니다. 그래서 HTML 은 화면이 그려진 뒤 효과 훅(useEffect) 안에서만 만들어졌습니다. 사람은 그 결과를 보지만, 검색봇이 처음 받는 HTML 에는 아직 본문이 없습니다.

에러는 한 번도 나지 않았습니다. 결과만 비어 있었기 때문에 오래 몰랐습니다. 브라우저 DOM 이 필요한 라이브러리는 서버 렌더링에서 이렇게 조용히 빠집니다.

같은 글이 사람에게는 3,253자로 보이고 검색봇이 받은 HTML 에는 117자만 들어 있던 비교 그림


저장 형식과 보여 주는 형식은 다릅니다

처음부터 HTML 로 저장하면 이 문제가 없지 않느냐고 물을 수 있습니다. 그렇게 하면 다시 편집할 때 블록 구조가 사라집니다. 표는 그냥 표 태그가 되고, 블록마다 붙은 속성도 남지 않습니다. 그래서 저장은 블록 JSON 으로 두는 것이 맞았습니다.

에디터의 변경 이벤트에서 블록 배열 전체를 JSON 문자열로 저장하는 방식은 그대로 두었습니다. 바꿔야 할 것은 보여 주는 쪽이었습니다. 검색에 걸려야 하는 화면이면 JSON 을 HTML 로 바꾸는 일을 서버에서 해야 합니다.

BlockNote 는 이 용도로 공식 서버용 패키지 '@blocknote/server-util' 을 따로 냅니다. 브라우저 없이 돌아갑니다. 바꾼 코드는 아래가 전부입니다.

import { ServerBlockNoteEditor } from "@blocknote/server-util";

const editor = ServerBlockNoteEditor.create();
const html = await editor.blocksToFullHTML(blocks); // 저장해 둔 블록 JSON

Next.js 서버 쪽에서 이 HTML 을 만들어 응답에 넣었습니다. 이제 검색봇이 처음 받는 HTML 에 본문이 같이 들어갑니다. 다만 서버 변환으로 바꾼 뒤 색인이 얼마나 돌아왔는지는 아직 숫자로 확인하지 못했습니다. 지금 말할 수 있는 것은 봇이 받는 HTML 에 본문이 들어간다는 데까지입니다.

블록 JSON 을 브라우저에서 HTML 로 바꾸던 구조를 서버 변환으로 옮긴 BEFORE AFTER 흐름도


붙이면서 같이 정한 것들

에디터 컴포넌트는 클라이언트 컴포넌트('use client')입니다. Next.js 서버 컴포넌트에 바로 넣지 않고 클라이언트 컴포넌트로 감싸서 씁니다.

이미지 업로드는 useCreateBlockNote({ uploadFile }) 하나로 붙습니다. 파일을 받아 우리 서버에 올리고 URL 문자열만 돌려주면 됩니다. 붙여넣기와 끌어다 놓기도 같은 함수를 지납니다.

UI 컴포넌트 라이브러리는 'mantine' 과 'shadcn' 중 하나를 고릅니다. 자사 블로그는 mantine 입니다. 이미 shadcn 과 Tailwind 로 된 프로젝트라면 shadcn 쪽이 스타일 충돌이 적을 것으로 보지만, 직접 확인하지는 않았습니다.

코드블록 구문 강조는 0.54 로 올리면서 Shiki 로 바꿨고, 색은 렌더 단계에서 칠합니다. 저장된 JSON 에 색이 들어가면 테마를 바꿀 때 옛 글이 따라오지 않기 때문입니다. 저장하는 것은 내용이고, 어떻게 보일지는 보여 줄 때 정한다는 점에서 HTML 변환과 같은 원리입니다.


패키지마다 라이선스가 다릅니다

고객사 일에서도 같은 요청이 되풀이해 들어옵니다. 공지사항 본문이나 약관 관리처럼 관리자가 서식 있는 글을 쓰는 화면이고, 대개 '노션처럼 쓰게 해 달라'는 말이 붙습니다. 그래서 고객사 비공개 코드에 넣기 전에 패키지별 라이선스를 npm registry 에서 하나씩 확인했습니다. 기준 버전은 0.56.0 입니다.

패키지

라이선스

고객사 비공개 코드

core · react · mantine · shadcn · server-util

MPL-2.0

그대로 쓸 수 있음

xl-pdf-exporter · xl-docx-exporter · xl-multi-column · xl-ai

GPL-3.0 OR PROPRIETARY

유료 라이선스 필요

같은 제품 안에서 기본 패키지와 확장 패키지의 라이선스가 갈립니다. 'xl-' 로 시작하는 패키지는 PDF·Word 내보내기, 다단 레이아웃, AI 기능입니다. 이 기능이 필요하면 유료 라이선스 비용이 견적에 들어갑니다. 버전이 바뀌면 다시 확인해야 하고, 이 확인이 법률 검토를 대신하지는 않습니다.

BlockNote 기본 패키지는 MPL-2.0, xl- 확장 패키지는 GPL-3.0 OR PROPRIETARY 로 갈리는 라이선스 구분도


견적 전에 확인하는 것

이 화면이 검색에 걸려야 하는가. 걸려야 하면 처음부터 서버 변환으로 갑니다.

PDF·Word 내보내기, 다단 레이아웃, AI 기능이 필요한가. 필요하면 확장 패키지 라이선스 비용을 견적에 넣습니다.

프로젝트가 이미 쓰는 UI 컴포넌트 라이브러리가 무엇인가. mantine 과 shadcn 중 하나를 고릅니다.

다 붙인 뒤 확인은 화면이 아니라 서버가 내보내는 HTML 원문으로 합니다.


마무리

관리자만 보는 화면(사내 공지, 내부 문서)이라면 브라우저에서 변환해도 문제가 없습니다. 이 차이는 검색에 걸려야 하는 화면에서만 문제가 됩니다. 그런데 사람 눈에는 그 차이가 보이지 않습니다. 저도 Search Console 에 숫자가 쌓이기 전까지 화면만 보고 괜찮다고 판단했습니다.

노션 같은 편집 화면이 필요하면 BlockNote 를 먼저 보고, 안 맞을 때만 Tiptap 으로 기능을 하나씩 붙여 직접 만듭니다. 0.54 에서 0.56 으로 올릴 때 깨지는 변경, 한글 입력 조합 중 슬래시 메뉴 동작, 여러 사람 동시 편집은 아직 따로 확인하지 않았습니다. 블로그에서 쓰는 동안 보고된 한글 입력 문제는 없었습니다.

#BlockNote#블록에디터#SEO#서버렌더링#Next.js#SearchConsole#오픈소스라이선스