본문 바로가기
오류 해결

[CKEditor] 4버전에서 5버전 업그레이드 관련 이미지 업로드 적용 이슈 해결

by 머그워트 2023. 8. 21.
728x90

CKEditor 4 버전의 이미지 업로드 방식은 꽤나 복잡하다.

이미지 업로드 버튼을 누르면 이렇게 꽤나 구린 레이어 팝업이 뜨는데

여기서 파일 선택을 누르면 업로드할 이미지 파일을 선택할 수 있다.

파일을 선택하고 나서 난 계속 습관적으로 확인을 누른다.

근데 서버로 전송을 먼저 누르지 않으면 확인 버튼을 눌렀을때 이미지 등록이 그냥 취소된다 ㅎ

 

그리고 이미 적용한 이미지의 크기를 변경해야할 때도

보통 다른 에디터에서 제공하는 것처럼.. 이미지의 모서리를 드래그해서 조절하는 게 아니라

이미지를 더블클릭해서 나오는 아래 팝업에서 너비를 직접 숫자값으로 입력해서 조절한다.

 

CKEditor5에서는 이러한 이미지 첨부 관련 기능이 많이 개선되었다.

게다가 CKEditor4는 2023년 6월부로 지원이 종료됐다고 한다.

그래서 5버전으로 올리려고 했는데… 이 버전업을 할때 꽤나 바뀐게 많아서 버전업 하는 데 엄청 삽질했다.

 

 

일단 프로젝트 환경은 react나 vue 이런걸 안쓰고 그냥 html 엔진을 쓰고 있었기 때문에 node 같은 건 안쓴다

4버전의 경우는 문제가 없었다. 그냥 JS방식의 파일 패키지를 통으로 받으면 되니까...

근데 5버전의 경우 node로 개발되어.. npm 방식으로 플러그인들을 add하는 방식이었다.

참고로 난 node js에 익숙하지 않다 ㅜ

 

주요 이슈가 이미지 관련이었으므로 이걸 적용한 버전이 필요했다.

그래서 5버전으로 업그레이드 하기로 했고.. 5버전에서 이미지 업로드를 지원하는 방식은 4개정도 있었는데

그 중 무료이고, Base64 바이너리가 아니라 파일로 업로드하는 방식인 SimpleImageAdapter를 선택했다.

 

이걸 적용하기 위해 두 가지 방법을 시도해 봤는데

  1. CDN을 사용한다.
    • cdn으로 간단하게 5버전으로 마이그레이션 할 수 있으려나?했는데 전혀 아니었다. 우선 cdn같은 경우는 커스터마이징이 안된다. 근데 이 cdn에는 SimpleImageAdapter가 포함되어 있지 않았다. (계속 플러그인을 못 찾는다고 오류남)
    • 그래서 CDN 사용은 포기
  2. online builder를 사용한다.
    • 내가 사용하고 싶은 플러그인을 ckeditor에서 제공하는 online builder에서 선택하면 얘네가 자체적으로 내가 선택한 플러그인으로 소스를 빌드해서 js파일로 다운받을 수 있게 제공해 준다.
    • 난 여기서 classic editor를 고르고 이것저것 플러그인을 넣어서 다운받았다.
    • 그런다음 다운받은 압축파일을 풀어서 소스에 통으로 붙여넣고,, build/ckeditor.js 파일을 import해서 사용하려 했다.
    • 이미지 업로드 플러그인으로 SimpleImageAdapter를 사용했는데 이거 관련 설정이 계속 꼬였다. URL 설정이 틀리다거나 계속 required plugin이 빠져있다거나 하는..
    • 그래서 중간에 다시 4버전으로 돌리고 4에서 이미지 첨부쪽만 개선한 플러그인이 없을까 알아봤는데.. easyimage라는 플러그인이 있었는데 유료기도 했고 이미지가 우리 서버가 아니라 ck 클라우드 서버에 올라간다 해서 원하는 방식이 아니었다.
    • 어찌저찌 설정을 만지다 보니 설정을 완료했다 ;;

 

online builder로 성공한 방법

  1. 일단 아래 링크로 가서 원하는 기능을 다 고른 후 download 했다.
  2. 다운받은 폴더를 통으로 일단 스프링 소스에 넣었다. 계속 동작확인을 해가면서 개발해야 했기 때문..
  3. 우선 처음 이 파일을 적용하니까 simple image 기능은 들어갔지만 툴바에 아이콘이 아예 없고 textarea에 글도 안 써졌다.
  4. 다음을 적용해서 해결했다.
  5. 이 두 문제를 해결한 다음 simple image 설정을 했다.
    ClassicEditor.create(document.querySelector( '#desc' ), {
        plugins: [
            ...
            "SimpleUploadAdapter",
            ...
        ],
        ...
        simpleUpload: {
            toolbar: ['imageTextAlternative'],
            uploadUrl: '/admin/upload/ckeditor',
            withCredentials: true
        },
        image: {
            toolbar: [
                'imageTextAlternative', 'linkImage'
            ]
        },
        ...
    } )
    .then( newEditor => {
        newEditor.ui.getEditableElement().parentElement.insertBefore(
                newEditor.ui.view.toolbar.element,
                newEditor.ui.getEditableElement()
        );
        window.ckeditor = newEditor;
    } )
    .catch( error => {
        console.error( error );
    } );
  6. 설정을 하니까 이미지 첨부는 바로 되는데 제일 원했던 기능인 resizing이 안돼서 resize 관련 플러그인도 별도 설치했다.
  7. 플러그인 설치 방법
    • 플러그인은 npm 명령어로 설치해야 하므로 pc에 node js가 설치되어 있어야 한다.
    1. 다운받은 소스에서 루트경로/src/ckeditor.js 파일을 연다
    2. 추가하고자 하는 플러그인을 해당 소스에 import해준다.
      • import할 주소는 공식문서를 참고한다.
      • 예를 들어 Underline 플러그를 설치하고 싶으면 공식 문서에서 underline을 검색한다.
      • 결과 페이지 (https://ckeditor.com/docs/ckeditor5/latest/api/module_basic-styles_underline-Underline.html) 에서 플러그인명 아래의 주소 (@ckeditor/ckeditor5-basic-styles/src/underline) 뒤에 .js를 붙여 import할 주소를 완성한다.
      • 예) import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline.js';
    3. Editor.builtinPlugins 배열에도 방금 import한 플러그인명을 추가해 준다.
    4. 소스 수정이 완료되었으면 루트경로로 돌아가서 터미널을 열고 build를 실행한다. (npm run build)
    5. build가 완료되면 루트경로/build 경로에 빌드된 js 파일이 저장된다.
    6. 빌드된 js를 적용한 파일에서 에디터를 create할 때도 plugins 배열에 플러그인명을 추가해 준다.
      • ClassicEditor.create(document.querySelector( '#desc' ), {
            plugins: [
        	    ...
                "Underline",
                ...
            ],
        ...
    7. 프로젝트를 재 빌드해 결과를 확인해 본다
  8. 플러그인마다 의존관계인 것들이 있어서 중간중간 missing plugin을 위와 같이 설치했다.
  9. 툴바 설정도 완료해줬다.. 공식 문서의 도움을 많이 받았다
  10. 최종적으로 적용한 소스는 다음과 같다.
    • HTML
    ClassicEditor.create(document.querySelector( '#desc' ), {
        plugins: [
            "Alignment","Bold","Delete","Essentials","FontBackgroundColor",
            "FontFamily","FontSize","GeneralHtmlSupport","Highlight","HtmlComment",
            "Image","ImageStyle","ImageToolbar","ImageUpload","ImageResizeEditing","ImageResizeHandles","ImageResize",
            "Indent","IndentBlock","Input","Italic","Link","LinkImage",
            "MediaEmbed","MediaEmbedToolbar","Paragraph",
            "SimpleUploadAdapter","SourceEditing","StandardEditingMode","Strikethrough","Style",
            "Table","TableCaption","TableColumnResize","TableProperties","TableToolbar",
            "TextTransformation","Underline"
        ],
        toolbar: {
            items: [
                'bold', 'italic', 'underline', 'strikethrough',
                '|', 'fontBackgroundColor', 'fontFamily', 'fontSize',
                '|', 'alignment', 'outdent', 'indent',
                '|', 'link', 'insertTable', 'imageUpload', 'mediaEmbed',
                '|', 'undo', 'redo',
                '|', 'sourceEditing'
            ]
        },
        simpleUpload: {
            toolbar: ['imageTextAlternative'],
                uploadUrl: '/upload',
                withCredentials: true
            },
            image: {
                toolbar: [
                    'imageTextAlternative', 'linkImage'
                ]
            },
            fontSize: {
                options: [ 'default', 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 72 ],
                supportAllValues: true
            },
        } )
        .then( newEditor => {
            newEditor.ui.getEditableElement().parentElement.insertBefore(
                newEditor.ui.view.toolbar.element,
                newEditor.ui.getEditableElement()
            );
            window.ckeditor = newEditor;
        } )
        .catch( error => {
            console.error( error );
        } );
    • src/ckeditor.js
    /**
     * @license Copyright (c) 2014-2023, CKSource Holding sp. z o.o. All rights reserved.
     * For licensing, see LICENSE.md or <https://ckeditor.com/legal/ckeditor-oss-license>
     */
    import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor.js';
    import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment.js';
    import Autoformat from '@ckeditor/ckeditor5-autoformat/src/autoformat.js';
    import AutoLink from '@ckeditor/ckeditor5-link/src/autolink.js';
    import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote.js';
    import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold.js';
    import DataFilter from '@ckeditor/ckeditor5-html-support/src/datafilter.js';
    import DataSchema from '@ckeditor/ckeditor5-html-support/src/dataschema.js';
    import Delete from '@ckeditor/ckeditor5-typing/src/delete.js';
    import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials.js';
    import FindAndReplace from '@ckeditor/ckeditor5-find-and-replace/src/findandreplace.js';
    import FontBackgroundColor from '@ckeditor/ckeditor5-font/src/fontbackgroundcolor.js';
    import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily.js';
    import FontSize from '@ckeditor/ckeditor5-font/src/fontsize.js';
    import GeneralHtmlSupport from '@ckeditor/ckeditor5-html-support/src/generalhtmlsupport.js';
    import Heading from '@ckeditor/ckeditor5-heading/src/heading.js';
    import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight.js';
    import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline.js';
    import HtmlComment from '@ckeditor/ckeditor5-html-support/src/htmlcomment.js';
    import HtmlEmbed from '@ckeditor/ckeditor5-html-embed/src/htmlembed.js';
    import Image from '@ckeditor/ckeditor5-image/src/image.js';
    import ImageBlockEditing from '@ckeditor/ckeditor5-image/src/image/imageblockediting.js';
    import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting.js';
    import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload.js';
    import ImageUtils from '@ckeditor/ckeditor5-image/src/imageutils.js';
    import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize.js';
    import ImageResizeEditing from '@ckeditor/ckeditor5-image/src/imageresize/imageresizeediting.js';
    import ImageResizeHandles  from '@ckeditor/ckeditor5-image/src/imageresize/imageresizehandles.js';
    import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle.js';
    import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar.js';
    import Indent from '@ckeditor/ckeditor5-indent/src/indent.js';
    import IndentBlock from '@ckeditor/ckeditor5-indent/src/indentblock.js';
    import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic.js';
    import Input from '@ckeditor/ckeditor5-typing/src/input.js';
    import Link from '@ckeditor/ckeditor5-link/src/link.js';
    import LinkEditing from '@ckeditor/ckeditor5-link/src/linkediting.js';
    import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage.js';
    import LinkImageEditing from '@ckeditor/ckeditor5-link/src/linkimageediting.js';
    import LinkUI from '@ckeditor/ckeditor5-link/src/linkui.js';
    import List from '@ckeditor/ckeditor5-list/src/list.js';
    import ListProperties from '@ckeditor/ckeditor5-list/src/listproperties.js';
    import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed.js';
    import MediaEmbedToolbar from '@ckeditor/ckeditor5-media-embed/src/mediaembedtoolbar.js';
    import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph.js';
    import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js';
    import SimpleUploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter.js';
    import SourceEditing from '@ckeditor/ckeditor5-source-editing/src/sourceediting.js';
    import SpecialCharacters from '@ckeditor/ckeditor5-special-characters/src/specialcharacters.js';
    import StandardEditingMode from '@ckeditor/ckeditor5-restricted-editing/src/standardeditingmode.js';
    import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough.js';
    import Style from '@ckeditor/ckeditor5-style/src/style.js';
    import Subscript from '@ckeditor/ckeditor5-basic-styles/src/subscript.js';
    import Superscript from '@ckeditor/ckeditor5-basic-styles/src/superscript.js';
    import Table from '@ckeditor/ckeditor5-table/src/table.js';
    import TableCaption from '@ckeditor/ckeditor5-table/src/tablecaption.js';
    import TableColumnResize from '@ckeditor/ckeditor5-table/src/tablecolumnresize.js';
    import TableProperties from '@ckeditor/ckeditor5-table/src/tableproperties';
    import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar.js';
    import TextTransformation from '@ckeditor/ckeditor5-typing/src/texttransformation.js';
    import Title from '@ckeditor/ckeditor5-heading/src/title.js';
    import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline.js';
    
    // plugin 추가 시 본 파일에 import하고 Editor.builtinPlugins에 추가한 후 ckeditor5 폴더에서 npm run build 실행
    
    class Editor extends ClassicEditor {}
    
    // Plugins to include in the build.
    Editor.builtinPlugins = [
        Alignment,
        Autoformat,
        AutoLink,
        BlockQuote,
        Bold,
        DataFilter,
        DataSchema,
        Delete,
        Essentials,
        FindAndReplace,
        FontBackgroundColor,
        FontFamily,
        FontSize,
        GeneralHtmlSupport,
        Heading,
        Highlight,
        HorizontalLine,
        HtmlComment,
        HtmlEmbed,
        Image,
        ImageBlockEditing,
        ImageEditing,
        ImageResize,
        ImageResizeEditing,
        ImageResizeHandles,
        ImageStyle,
        ImageToolbar,
        ImageUpload,
        ImageUtils,
        Indent,
        IndentBlock,
        Input,
        Italic,
        Link,
        LinkEditing,
        LinkImage,
        LinkImageEditing,
        LinkUI,
        List,
        ListProperties,
        MediaEmbed,
        MediaEmbedToolbar,
        Paragraph,
        PasteFromOffice,
        SimpleUploadAdapter,
        SourceEditing,
        SpecialCharacters,
        StandardEditingMode,
        Strikethrough,
        Style,
        Subscript,
        Superscript,
        Table,
        TableCaption,
        TableColumnResize,
        TableProperties,
        TableToolbar,
        TextTransformation,
        Title,
        Underline
    ];
    
    // Editor configuration.
    Editor.defaultConfig = {
        toolbar: {
            items: [
                'heading',
                '|',
                'bold',
                'italic',
                'underline',
                'bulletedList',
                'numberedList',
                '|',
                'alignment',
                'outdent',
                'indent',
                '|',
                'imageUpload',
                'blockQuote',
                'insertTable',
                'link',
                'mediaEmbed',
                '|',
                'undo',
                'redo',
                '|',
                'fontBackgroundColor',
                'fontFamily',
                'fontSize',
                '|',
                'specialCharacters',
                'subscript',
                'superscript'
            ]
        },
        language: 'ko',
        image: {
            toolbar: [
                // 'toggleImageCaption',
                // 'imageTextAlternative',
                'linkImage'
            ]
        },
        table: {
            contentToolbar: [
                'tableColumn',
                'tableRow',
                'mergeTableCells',
                'tableProperties'
            ]
        }
    };
    
    export default Editor;

npm 사용법과 build된 파일을 사용하는 방식 자체가 익숙하지 않아 어려움이 있었지만

결국 해결 완……..!

 

 

CKEditor 4버전

CKEditor 5버전

 

 

 

5버전을 적용해보니 4와 다른 것

  • 이미지 업로드 방식이 훨씬 쉬워짐 - 별도 팝업 없이 바로 선택해서 첨부
  • table 태그와 img 태그가 그대로 안 들어가고 figure 태그 안에 들어감
    • css 수정이 일부 필요했음
  • editor 영역이 height가 안 잡혀 있는 듯했음… css를 별도로 추가함
  •  
728x90

댓글