GithubXDiscord

customization-guide > 커스텀 컴포넌트 사용법

커스텀 컴포넌트 사용법

Notionpresso은 다양한 Notion 블록 타입에 대한 기본 컴포넌트를 제공합니다. 이 섹션에서는 이러한 기본 컴포넌트를 사용하고 커스터마이징하는 방법, 그리고 새로운 커스텀 컴포넌트를 만드는 방법을 설명합니다.

기본 컴포넌트 사용 예시

Heading 컴포넌트

Heading 컴포넌트는 Notion의 제목 블록을 렌더링합니다. 다음은 Heading 컴포넌트를 커스터마이징하는 예시입니다:

import React from "react";
import { Heading, type HeadingArgs } from "@notionpresso/react";

const CustomHeading: React.FC<HeadingArgs> = (props) => {
  // level 3 헤딩에 밑줄 추가
  if (props.type === "heading_3") {
    return <Heading {...props} style={{ textDecoration: "underline" }} />;
  }
  return <Heading {...props} />;
};

export default CustomHeading;

이 예시에서는 level 속성을 확인하여 특정 레벨의 헤딩에 대해 스타일을 변경합니다.

Paragraph 컴포넌트

Paragraph 컴포넌트는 일반 텍스트 블록을 렌더링합니다. 다음은 Paragraph 컴포넌트를 커스터마이징하는 예시입니다:

import React from "react";
import { Paragraph, type ParagraphArgs } from "@notionpresso/react";

const CustomParagraph: React.FC<ParagraphArgs> = (props) => {
  return (
    <Paragraph
      {...props}
      className="custom-paragraph"
      style={{ lineHeight: "1.8", marginBottom: "1em" }}
    />
  );
};

export default CustomParagraph;

이 예시에서는 모든 단락에 대해 줄 간격과 하단 여백을 조정하고, 사용자 정의 클래스를 추가합니다.

Toggle 컴포넌트 커스터마이징 예시

Toggle 컴포넌트는 Notion의 토글 블록을 구현합니다. 다음은 Toggle 컴포넌트를 커스터마이징하는 고급 예시입니다:

import React, { useState } from "react";
import { Toggle, type ToggleArgs } from "@notionpresso/react";
import { ChevronDown, ChevronRight } from "your-icon-library";

const CustomToggle: React.FC<ToggleArgs> = (props) => {
  const [isOpen, setIsOpen] = useState(false);

  const handleChangeOpen = (open: boolean) => {
    setIsOpen(open);
    // 여기에 추가적인 로직을 넣을 수 있습니다.
    console.log(`Toggle state changed to: ${open}`);
  };

  return (
    <Toggle {...props} isOpen={isOpen} onChangeOpen={handleChangeOpen}>
      <Toggle.Icon>
        {isOpen ? <ChevronDown size={20} /> : <ChevronRight size={20} />}
      </Toggle.Icon>
      <Toggle.Content>{props.children}</Toggle.Content>
    </Toggle>
  );
};

export default CustomToggle;

이 예시에서는:

  1. useState를 사용하여 토글의 열림/닫힘 상태를 관리합니다.
  2. handleChangeOpen 함수를 통해 상태 변경을 처리하며, 필요에 따라 추가 로직을 구현할 수 있습니다.
  3. Toggle 컴포넌트에 isOpenonChangeOpen prop을 전달하여 상태를 직접 제어합니다.
  4. isOpen 상태에 따라 다른 아이콘을 표시합니다.

<Toggle.Icon>에 컨텐츠를 제공하지 않으면 기본 아이콘이 사용됩니다.

커스텀 컴포넌트 적용하기

Notionpresso에서 커스텀 컴포넌트를 사용하는 방법은 다음과 같습니다:

import { Notion } from "@notionpresso/react";
import CustomHeading from "./CustomHeading";
import CustomParagraph from "./CustomParagraph";
import CustomToggle from "./CustomToggle";

const MyNotionPage = ({ pageData }) => {
  const customComponents = {
    heading_1: CustomHeading,
    heading_2: CustomHeading,
    heading_3: CustomHeading,
    paragraph: CustomParagraph,
    toggle: CustomToggle,
  };

  return (
    <Notion custom={customComponents}>
      <Notion.Body>
        <Notion.Blocks blocks={pageData.blocks} />
      </Notion.Body>
    </Notion>
  );
};

이 예시에서 custom prop을 통해 커스텀 컴포넌트를 Notion 컴포넌트에 전달합니다. 각 블록 타입에 대해 커스텀 컴포넌트를 지정할 수 있습니다.

완전한 커스텀 컴포넌트 만들기

Notionpresso의 기본 컴포넌트를 사용하지 않고, 완전히 새로운 컴포넌트를 만들 수도 있습니다. 다음은 Todo 컴포넌트를 처음부터 만드는 예시입니다:

import React from "react";
import { RichText, type TodoProps } from "@notionpresso/react";

const Todo: React.FC<TodoProps> = ({ children, ...props }) => {
  const {
    to_do: { color, rich_text: texts, checked },
  } = props;

  const colorClass = color !== "default" ? `notion-${color}` : "";

  return (
    <div className={`notion-block notion-to-do ${colorClass}`}>
      <div className="notion-to-do-item">
        <div className="notion-to-do-item-content">
          <input
            type="checkbox"
            checked={checked}
            readOnly
            className="notion-to-do-checkbox"
          />
          <div
            className={`notion-to-do-text ${checked ? "notion-to-do-checked" : ""}`}
          >
            <RichText props={texts} />
          </div>
        </div>
        {children}
      </div>
    </div>
  );
};

export default Todo;

이 예시에서 주의할 점:

  • RichText 컴포넌트만 Notionpresso에서 import합니다. 이는 복잡한 텍스트 렌더링을 처리하기 위함입니다.
  • CSS 클래스 이름은 모두 notion-으로 시작합니다. 이는 Notionpresso의 스타일 규칙을 따르는 것입니다.
  • gnotion-${color} 형식의 클래스를 추가합니다. 기본 색상("default")의 경우 추가 클래스를 적용하지 않습니다.
  • 컴포넌트는 children prop을 받아 하위 컨텐츠를 렌더링할 수 있게 합니다. 이는 중첩된 구조를 지원하기 위함입니다.

컴포넌트 작성 시 주의사항

  1. 타입 안정성: TypeScript를 사용하여 prop 타입을 명확히 정의하세요. Notionpresso에서 제공하는 타입(예: HeadingArgs, ParagraphArgs)을 활용하세요.
  2. prop 전달: 커스텀 컴포넌트에서 기본 컴포넌트로 모든 prop을 전달하려면 {...props}를 사용하세요. 이는 타입 안정성을 유지하면서 불필요한 prop 나열을 방지합니다.
  3. 스타일 오버라이딩: 기존 스타일을 완전히 대체하지 않도록 주의하세요. 대신 className을 추가하거나 style prop을 확장하는 방식을 사용하세요.
  4. 성능 고려: 불필요한 리렌더링을 방지하기 위해 React.memouseMemo, useCallback을 적절히 사용하세요.
  5. 접근성: 커스텀 컴포넌트를 만들 때 접근성(a11y)을 고려하세요. 예를 들어, 토글 버튼에 적절한 aria- 속성을 추가하는 것이 좋습니다.
  6. 테마 지원: Notionpresso의 테마 시스템과 호환되도록 커스텀 컴포넌트를 설계하세요. CSS 변수를 활용하면 다크 모드 등의 테마 전환을 쉽게 지원할 수 있습니다.

커스텀 컴포넌트 적용하기

Notionpresso에서 커스텀 컴포넌트를 사용하는 방법은 다음과 같습니다:

import { Notion, type NotionPage, type CustomComponents } from 'Notionpresso';
import CustomParagraph from './CustomParagraph';
import CustomToggle from './CustomToggle';

const MyNotionPage = ({ pageData }: { pageData: NotionPage }) => {
  const customComponents = useMemo<CustomComponents>(() => ({
    paragraph: CustomParagraph,
    toggle: CustomToggle,
  }), []);

  return (
    <Notion custom={customComponents}>
    <Notion.Cover cover={pageData.cover} />
      <Notion.Body>
        <Notion.Title title={pageData.title} />
        <Notion.Blocks blocks={pageData.blocks} />
      </Notion.Body>
    </Notion>
  );
};

이 예시에서 custom prop을 통해 커스텀 컴포넌트를 Notion 컴포넌트에 전달합니다.

이러한 가이드라인을 따르면 Notionpresso의 기능을 확장하면서도 일관성과 유지보수성을 유지할 수 있습니다.