Plate Editor 사용기

Kyojun Jin·2025년 1월 22일

할 일

next.js 기반 웹에서 plate editor 를 사용한다.
미디어는 s3 에 올리고 전체 게시글 html 은 dynamoDB 에 올린다.

만들어져 있는 에디터 코드에서 일부를 수정하여

1. 미디어를 s3 에 올리고 링크로 바꾸기
2. html 을 dynamoDB 에 올리기

를 구현한다.

기본 정보

shadcn

next.js 에선 UI툴로 공식적으로 shadcn 을 밀고 있다.
맨 처음 shadcn 은 next.js 와 상관 없이 따로 만들어진 js 기반 툴인데 Vercel 회사로 이직했다고 함 (shadcn 자체가 회사가 아니고 오픈소스지만 크게 보면 인수 합병이라고 볼 수 있을 듯.)

shadcn 은 가벼운 ui 툴임. npm 으로 라이브러리 설치하는 게 아니라 아예 만들어진 코드를 삽입해서 브라우저 로드 시간을 줄이는 데에 목적이 있음. tailwind css 랑 높은 호환성을 보인다고 함. (shadcn 자체가 tailwin css 로 만들어짐)

즉 shadcn 를 쓰면 일일히 ui 컴포넌트 안 만들어도 되고 갖다가 써도 되면서, 간단한 것은 className 으로 수정이 가능함.

slate

slate 는 udecode 에서 만든 텍스트 에디터임. 로우 레벨이라는데 자세히 모르겠음. 직접 쓸 것은 아니므로 생략하도록 한다.

plate

plate 는 slate 를 shadcn ui 로 감싸서 이미 만들어진 텍스트 에디터. spring(slate) - spring boot(plate) 쯤의 관계가 아닐까 생각함.

이 글에선 Plate Editor 를 사용해서 1. 이미지 업로드 / 2. 글 뽑아내기 를 하도록 함.

Plate 는 커스터마이징이 물론 가능하긴 하나 일단 만들어진 에디터가 있으므로 그것을 선택하도록 함.

설치

npx shadcx@latest add plate/editor-ai

이런 에디터가 바로 사용 가능함.

테마나 그런 건 직접 공홈에서 선택할 수 있음.

이걸 설치하면 여러 파일이 생성됨.

  1. app/api/ 밑에 uploadthing, ai 등이 생김
    api routes 를 바로 사용하도록 route.ts 파일이 생기는데 uploadthing 은 s3 대용으로 Plate 가 지원하는 회사인 거 같고 ai 는 chat gpt 나 gemini api key 쓰면 바로 동작하는 것 같음.

  2. components/editor , components/plate-ui
    컴포넌트에 대한 코드가 생성됨

    여기서 components/plate-uifixed-toolbar-button.tsx, floating-toolbar-buttons.tsx, slash-input-element.tsx 에서 사용자에게 보여줄 에디터 버튼들을 생략할 수 있음. editor 처음 설치하면 모든 버튼들이 다 있어서 복잡하니 이걸로 안 쓸만한 것을 지우는 것을 추천함.
    또한 이걸 지웠다면 components/editor/plugins/editor-plugins.tsx 에서 안 쓰는 components 와 plugins 를 주석처리해서 지워주는 것도 좋음. 각 element 는 공홈 참조하면 Node Elements 에 잘 설명돼있음.

  3. lib/uploadting.ts 가 생겨서 uploadthing 이 생김.
    이걸 변경하면 uploadthing 안 쓰고 s3 를 쓰는 방향으로 바꿀 수 있음 (여기에 로딩바랑 예외 처리랑 다 구현돼 있어서 일부만 수정하기로 함)

  4. globals.css[data-registry="plate"]가 붙은 테마 정보가 추가됨
    이거는 처음에 설치할 때 테마 선택한 것을 따라감. 공식 홈페이지에서 커스터마이징 할 수 있음.

  5. components.json 수정됨.
    설정 값이니 굳이 안 건드리는 게 좋을 듯. 테마 이름이랑 그런 게 있음.

uploadthing 대신 S3

뭔가 에디터에 업로드 하면 lib/uploathing.ts 가 동작함. 여기서 uploadThing(file: File) 만 잘 구워삶으면 파일을 s3 로 가도록 할 수 있을 거 같음.

고맙게도 공홈에 코드가 있다.
S3 업로드하는 코드

const { presignedUrl, fileUrl, fileKey } = await fetch('/api/upload', {
    method: 'POST',
    body: JSON.stringify({
      filename: file.name,
      contentType: file.type,
    }),      
}).then(r => r.json());

해당 코드에서 이 부분을 사용하기 위해 app/api/upload/route.ts 를 다음과 같이 작성한다.

아래 코드는 AWS javascript SDK v3를 이용하여 S3 Presigned URL(미리 서명된 생성 URL)을 작성하는 코드를 참고했다.

import { NextRequest } from "next/server";

import { bucket, cfUrl, postMediaPrefix, s3Client } from "@/lib/s3";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

export const POST = async (req: NextRequest): Promise<Response> => {
  const { filename, contentType } = await req.json();

  const key = `${postMediaPrefix}/${filename}`;

  try {
    const command = new PutObjectCommand({
      Bucket: bucket,
      Key: key,
      ContentType: contentType,
    });

    const body = {
      presignedUrl: await getSignedUrl(s3Client, command, {
        expiresIn: 60 * 60 * 24,
      }),
      fileUrl: `${cfUrl}/${bucket}/${postMediaPrefix}/${filename}`,
      fileKey: `${bucket}/${postMediaPrefix}/${filename}`,
    };

    return new Response(JSON.stringify(body), {
      status: 200,
    });
  } catch (error) {
    return new Response(JSON.stringify(error), {
      status: 500,
    });
  }
};

0개의 댓글