Tiptap
Tiptap은 Headless WYSIWYG(What You See Is What You Get) 에디터로, 웹에서 텍스트 편집 기능을 구현할 때 사용하는 라이브러리
특징
- ProseMirror 기반
- Tiptap은 ProseMirror 위에서 동작하며, 이를 보다 쉽게 사용할 수 있도록 래핑한 라이브러리
- ProseMirror 자체는 강력하지만 설정이 복잡한데, Tiptap은 이를 쉽게 활용할 수 있도록 API를 제공
- Headless
- 기본 UI가 제공되지 않으며, 개발자가 원하는 대로 커스터마이징할 수 있음
- 버튼, 툴바 등의 UI는 직접 구현해야 하지만, 덕분에 원하는 디자인을 자유롭게 적용
- 확장성
- 플러그인(Extensions) 형태로 기능을 추가할 수 있음
- 기본 제공되는 확장(볼드, 기울임, 링크 등) 외에도 직접 커스텀 확장을 만들 수도 있음
- React, Vue, Svelte 등 다양한 프레임워크 지원
- 공식적으로 React, Vue를 지원하며, 다른 프레임워크에서도 사용할 수 있음
- Markdown 및 HTML 지원
- Markdown을 HTML로 변환하거나, HTML을 직접 다룰 수도 있음
기본 세팅
프로젝트 생성
npx create-next-app@latest practice_titap
cd practice_titap
의존성 설치
// 안되면 sudo 추가해서 실행(Mac 기준)
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
기본 코드 작성
// components/tiptap.tsx
'use client'
import {EditorContent, useEditor} from "@tiptap/react";
import {StarterKit} from "@tiptap/starter-kit";
export default function Tiptap() {
const editor = useEditor({
extensions: [StarterKit],
content: "<p>Hello Tiptap!</p>",
})
return (
<EditorContent editor={editor}/>
)
}
화면은 잘 출력이 되지만 'Console'탭에 들어가면 아래와 같은 경고가 떠있다.
❗️Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches.
이를 해석하면 `Tiptap은 기본적으로 클라이언트에서만 동작하는 에디터인데, SSR 환경에서는 서버와 클라이언트의 렌더링 결과가 다를 수 있어 hydration mismatch 오류가 발생할 수 있습니다.`인데, `editor`의 `immediatelyRender`속성을 `false`로 설정하면 클라이언트가 서버에서 받은 HTML을 그대로 유지한 상태에서, 에디터를 나중에 업데이트하도록 조정한다.
즉, 클라이언트에서 즉시 렌더링하지 않고, 살짝 기다렸다가 실행하는 방식입니다.
확장 기능 추가 및 설정
1. 특정 확장 기능만 추가
Heading(제목) 확장 기능을 추가하고, h1, h2, h3만 허용하는 설정
import Heading from '@tiptap/extension-heading'
new Editor({
extensions: [
Heading.configure({
levels: [1, 2, 3], // h1, h2, h3만 허용
}),
],
})
2. StarterKit에서 특정 확장 설정
StarterKit을 사용하면 여러 확장을 한 번에 포함할 수 있으며, 특정 확장의 설정을 변경할 수도 있음
import StarterKit from '@tiptap/starter-kit'
new Editor({
extensions: [
StarterKit.configure({
heading: {
levels: [1, 2, 3],
},
}),
],
})
3. StarterKit에서 특정 확장 비활성화
StarterKit에서 불필요한 확장을 제외할 수도 있음
import StarterKit from '@tiptap/starter-kit'
new Editor({
extensions: [
StarterKit.configure({
history: false, // Undo/Redo 기능 비활성화
}),
],
})
스타일링
1. 에디터 기본 스타일 적용
에디터 내부 요소의 스타일을 지정할수 있음
/* 에디터 내부 스타일 */
.tiptap p {
margin: 1em 0;
}
/* 전역 스타일 (저장된 콘텐츠를 다른 곳에서 렌더링할 경우) */
p {
margin: 1em 0;
}
2. 확장 기능을 활용한 커스텀 클래스 추가
확장 기능(Extensions)을 설정하여 HTML 요소에 클래스를 추가할 수 있음(Tailwind CSS를 사용할 때 유용)
new Editor({
extensions: [
Document,
Paragraph.configure({
HTMLAttributes: {
class: 'my-custom-paragraph', // 단락(p) 요소에 클래스 추가
},
}),
Heading.configure({
HTMLAttributes: {
class: 'my-custom-heading', // 제목(h1~h6) 요소에 클래스 추가
},
}),
Text,
],
})
<h1 class="my-custom-heading">Example Text</h1>
<p class="my-custom-paragraph">Wow, that's really custom.</p>
3. 에디터 컨테이너에 클래스 추가
에디터 전체를 감싸는 요소에 커스텀 클래스를 추가할 수도 있음
new Editor({
editorProps: {
attributes: {
class: 'prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none',
},
},
})
4. HTML 구조 변경
기본적으로 **굵은 텍스트(Bold)**는 <strong> 태그로 렌더링 → 커스텀 확장 기능을 만들어 <b> 태그로 변경할 수 있음
import Bold from '@tiptap/extension-bold'
const CustomBold = Bold.extend({
renderHTML({ HTMLAttributes }) {
return ['b', HTMLAttributes, 0] // <strong> 대신 <b> 태그 사용
},
})
new Editor({
extensions: [
// …
CustomBold,
],
})
❗️커스텀 확장을 만들어 별도 파일로 관리하면 유지보수가 용이
5. Tailwind CSS 스타일 적용
Tiptap은 Tailwind CSS와 잘 호환되며, @tailwindcss/typography 플러그인을 활용하면 더욱 깔끔한 스타일을 적용할 수 있음
import 'tailwindcss/tailwind.css'
new Editor({
editorProps: {
attributes: {
class: 'prose prose-lg mx-auto',
},
},
})
'프론트엔드 > 라이브러리' 카테고리의 다른 글
마인드맵 라이브러리(Cytoscape) (0) | 2025.02.26 |
---|