[Trend-Now] Quill 커스텀 이미지 핸들러 구현하기

강수영·2025년 6월 20일
0

이미지 업로드 방식 선택하기

Quill 에디터(편집 에디터)는 이미지를 첨부하면 base64 인코딩 형식으로 변환되어 저장됩니다.

하지만 base64 방식은 이미지 데이터를 긴 문자열 형태로 HTML에 포함시키기 때문에, 다음과 같은 문제가 발생합니다.

  • 파일 크기 증가 → base64로 인코딩되면 실제 이미지보다 30~40% 더 커짐
  • 렌더링 속도 저하 → 브라우저가 base64 문자열을 해석해야 하므로 느릴 수 있음

이를 해결하기 위해, base64 방식 대신 S3에 이미지를 업로드하고 해당 URL을 에디터에 삽입하는 방식을 선택하여 구현하였습니다.


🖼️ base64 vs S3 업로드

항목base64 인코딩S3 업로드
이미지 전달 방식이미지 → base64 문자열로 변환 후 포함이미지 → 서버에 업로드 후 URL 전달
전송 데이터 크기1.3~1.4배 커짐원본 그대로 전송
렌더링 성능<img src="data:image/png;base64,..."> → 해석 시간 더 걸림<img src="https://s3..."> → 브라우저 캐싱 가능
HTML/JS 코드 길이base64 문자열이 코드 내에 길게 삽입됨URL만 포함
사용자 경험(UX)초기 렌더 느릴 수 있음빠르고 깔끔

구현 시나리오

이미지를 base64 대신 S3에 업로드하고, 응답받은 URL을 에디터에 삽입하는 흐름은 다음과 같습니다.

  1. 사용자가 Quill 에디터의 이미지 버튼을 클릭합니다.
  2. 파일 탐색기가 열리면 사용자는 업로드할 이미지를 선택합니다.
  3. 선택한 이미지는 S3에 업로드되고, 서버로부터 업로드된 이미지의 URL을 응답받습니다.
  4. 해당 URL을 사용하여, 현재 커서 위치에 <img> 태그를 삽입합니다.

커스텀 이미지 핸들러 구현하기

Quill 이미지 버튼에 커스텀 핸들러 연결하기

Quill 에디터에서 이미지 버튼을 클릭했을 때의 동작을 직접 커스텀하려면, handlers 옵션을 설정해주어야 합니다.

const quill = new Quill(editorRef.current, {
  ...,
  modules: {
    toolbar: {
      container: [...], // 생략
      handlers: {
        image: () => {
          imageHandler(quill);
        },
      },
    },
  },
});

위와 같이 Quill 인스턴스를 생성할 때 handlers.image사용자 정의 함수(imageHandler)를 지정하면, 기본 동작 대신 원하는 방식으로 이미지를 처리할 수 있습니다.


커스텀 이미지 핸들러 코드 살펴보기

const imageHandler = useCallback((editor: any) => {
    // 파일 입력창 생성
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.setAttribute('name', 'file');
    input.setAttribute('multiple', '');
    input.click();

    // 파일 선택 후 이벤트 처리
    input.onchange = async (event: any) => {
      const target = event.target as HTMLInputElement;
      const files = target.files;
      if (!files || files.length === 0) return;

      const formData = new FormData();
      for (let i = 0; i < files.length; i++) {
        formData.append('images', files[i]);
      }

      //  S3 업로드 요청
      const response = await uploadImageToS3(token, formData); // 다중 파일

      // Quill 에디터에 <img> 태그 추가
      response?.imageUploadDto.forEach((img) => {
        const range = editor.getSelection();
        editor.insertEmbed(range.index, 'image', img.imageUrl);
        editor.setSelection(range.index + 1);
      });
    };
  }, []);

위 코드에서 imageHandler 함수는 Quill 에디터의 이미지 버튼을 클릭했을 때 실행되는 커스텀 핸들러입니다.

먼저 input 태그를 동적으로 생성해, 사용자가 로컬에서 이미지를 선택할 수 있도록 합니다.

이미지 선택이 완료되면 input.onchange 이벤트가 트리거되고, 선택된 파일들을 FormData에 담아 서버(S3)에 업로드합니다.

업로드가 완료되면 서버로부터 이미지의 URL이 포함된 응답을 받습니다.

그 후 Quill 에디터의 현재 커서 위치를 가져와, 해당 위치에 <img> 태그를 삽입하고, 커서를 이미지 뒤로 이동시킵니다.


결과

위와 같이 커스텀 이미지 핸들러를 적용하면, Quill 에디터에 이미지를 삽입할 때 base64가 아닌 실제 URL로 삽입되는 것을 확인할 수 있습니다.

이 방식은 렌더링 성능 향상, 콘텐츠 크기 최적화, 이미지 관리 용이성 등의 장점이 있습니다.

📖 출처

profile
프론트엔드 개발자

0개의 댓글