상세 컨텐츠

본문 제목

노션에 등장 블록 에디터 (editor.js)

자바스크립트

by e7e 2023. 7. 30. 09:42

본문

워드프레스 공식 에디터로 유명한  editor.js !!!, 노션 으로 더욱더 유명세를 타게 된당.

공식 홈페이지는 https://editorjs.io/  를 가보면 된당!

 

블로그 제작시에 글 작성 에디터로 넣으면 아두 꽤나 있어 보이는뎅,

문제는 필요한  플러그인들이 조각 조각 분리되어 있어, 일일이 가져다 붙여줘야 한당!

 

그래서  아래 소스를 맹글었당!(대략 혹 필요하다 싶은 플러그인은 모두 모아보았당!)

내용 저장/  내용 불러오기 기능도 당근 필요할 것이니, 샘플데이터와 함께 집약시켰당!

 

이 소스 하나면 블록에디터 시작에는 아마 부족함이 없을 것으로 사료되었당.

(가축 사료 아니공!~~ 개발자 사료땅!)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <!-- Core  include only Paragraph block -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
    <!-- Header Plug-in-->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script>
    <!-- Link embeds-->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/link@2.5.0/dist/bundle.min.js"></script>
    <!-- Raw Html -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/raw@2.4.0/dist/bundle.min.js"></script>
    <!-- Simple Image -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/simple-image@1.5.1/dist/bundle.min.js"></script>
    <!-- Image -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/image@2.8.1/dist/bundle.min.js"></script>
    <!-- CheckList -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/checklist@latest"></script>
    <!-- List -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/list@latest"></script>
    <!-- Embed -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/embed@latest"></script>
    <!-- Quote -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/quote@2.5.0/dist/bundle.min.js"></script>
    <!-- Table -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/table@2.2.1/dist/table.min.js"></script>
    <!-- Nested List -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/nested-list@latest"></script>
    <!-- Delimiter -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/delimiter@1.3.0/dist/bundle.min.js"></script>
    <!-- Warning -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/warning@latest"></script>
    <!-- Code -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/code@2.8.0/dist/bundle.min.js"></script>
    <!--  Attach -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/attaches@latest"></script>
    <!-- Marker-->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/marker@latest"></script>
    <!-- Inline Code -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/inline-code@1.4.0/dist/bundle.min.js"></script>
    <!-- UnderLine -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/underline@latest"></script>
    <!-- Alert -->
    <script src="https://cdn.jsdelivr.net/npm/editorjs-alert@latest"></script>
    <!-- Mermaid  필요없는 놈 
    <script src="https://cdn.jsdelivr.net/npm/editorjs-mermaid@latest"></script>
    -->
    <!-- Codeflask -->
    <script src="https://cdn.jsdelivr.net/npm/@calumk/editorjs-codeflask@latest"></script>
</head>

<body>
    <button onclick="fSave()">저장</button>
    <button onclick="fChg()">바꾸깅</button>
    <div id="myEditor"></div>
    <script>
        // 내용 바꾸깅
        async function fChg() {
            let data = { "time": 0, "blocks": [{ "type": "header", "data": { "text": "이거슨 바뀐 데이터ㅋㅋ", "level": 1 } }], "version": "2.26.5" }
            editor.render(data);
        }

        // 내용 저장 (localStorage에 할깡, DB에 할깡 clob롱)
        async function fSave() {
            alert("F12 눌렁 Console.log 내용 확인해 주삼");
            let rslt = await editor.save();
            console.log("체크:", rslt);
            console.log("체크:", JSON.stringify(rslt));
        }



        const myHolder = document.querySelector("#myEditor");
        /*
           잘 된 설정을 가지고 있는 게 필요
        */
        const editor = new EditorJS({
            holder: myHolder,
            // 뿌릴 데이터들
            data: {
                blocks: [
                    {
                        "type": "header",
                        "data": {
                            "text": "이것은 첨부터 보이는 데이터",
                            "level": 2
                        }
                    },
                    {
                        "type": "linkTool",
                        "data": {
                            "link": "https://e-7-e.tistory.com",
                            "meta": {
                                "title": "E7E 만만세",
                                "site_name": "E7E BLOG",
                                "description": "비오는 날은 언제나, 가슴이 눈물로 가득찬당?, 안찬당?",
                                "image": {
                                    "url": "https://wimg.mk.co.kr/news/cms/202304/14/news-p.v1.20230414.15e6ac6d76a84ab398281046dc858116_P1.jpg"
                                }
                            }
                        }
                    },
                    {
                        "type": "raw",
                        "data": {
                            "html": `<div style=\"background: #000; color: #fff; font-size: 30px; padding: 50px;\">
                                     \n무엇이든 HTML
                                     \n</div>`,
                        }
                    },
                    {
                        "type": "simImg",
                        "data": {
                            "url": "https://image.ytn.co.kr/general/jpg/2022/0228/202202281325208959_d.jpg",
                            "caption": "오 오 로제님!",
                            "withBorder": true,
                            "withBackground": true,
                            "stretched": false
                        }
                    },
                    {
                        "type": "image",
                        "data": {
                            "file": {
                                "url": "https://www.tesla.com/tesla_theme/assets/img/_vehicle_redesign/roadster_and_semi/roadster/hero.jpg"
                            },
                            "caption": "Roadster // tesla.com",
                            "withBorder": false,
                            "withBackground": false,
                            "stretched": false
                        }
                    },
                    {
                        "type": "checklist",
                        "data": {
                            "items": [
                                {
                                    "text": "지나는 지나치는 법이 없음",
                                    "checked": true
                                },
                                {
                                    "text": "서니는 줌 apif를 잘 한당",
                                    "checked": true
                                },
                                {
                                    "text": "비오는 화요일!!! 슬프당",
                                    "checked": false
                                }
                            ]
                        }
                    },
                    {
                        "type": "list",
                        "data": {
                            "style": "ordered",
                            "items": [
                                "이거슨 리스트 아이템이예용",
                                "리스트라니까용",
                                "간단하고 파워풀하지용",
                                "비슷한 설정이 반복되는 게 못생겼어요"
                            ]
                        }
                    },
                    {
                        "type": "embed",
                        "data": {
                            "service": "coub",
                            "source": "https://www.youtube.com/watch?v=t7hqUhdycQk",
                            "embed": "https://www.youtube.com/embed/t7hqUhdycQk",
                            "width": 580,
                            "height": 320,
                            "caption": "AOA 모야?"
                        }
                    },
                    {
                        "type": "quote",
                        "data": {
                            "text": "음 이것은 인용구문을 쓸 때 유용하겠군",
                            "caption": "E7E 말쌈!",
                            "alignment": "left"
                        }
                    },
                    {
                        "type": "table",
                        "data": {
                            "withHeadings": true,
                            "content": [["Kine", "Pigs", "Chicken"], ["1 pcs", "3 pcs", "12 pcs"], ["100$", "200$", "150$"]]
                        }
                    },
                    {
                        "type": "nestedlist",
                        "data": {
                            "style": "unordered",
                            "items": [
                                {
                                    "content": "Apples",
                                    "items": [
                                        {
                                            "content": "Red",
                                            "items": [
                                                {
                                                    "content": "Roze"
                                                },
                                                {
                                                    "content": "Jenni"
                                                },
                                                {
                                                    "content": "Risa"
                                                }

                                            ]
                                        },
                                        {
                                            "content": "Green",
                                            "items": []
                                        },
                                    ]
                                },
                                {
                                    "content": "Bananas",
                                    "items": [
                                        {
                                            "content": "Yellow",
                                            "items": []
                                        },
                                    ]
                                },
                            ]
                        }
                    },
                    {
                        "type": "delimiter",
                        "data": {}
                    },
                    {
                        "type": "warning",
                        "data": {
                            "title": "메몽:",
                            "message": "스스로를 경고하랑. 그것만이 살길이당"
                        }
                    },
                    {
                        "type": "code",
                        "data": {
                            "code": "body {\n font-size: 14px;\n line-height: 16px;\n}",
                        }
                    },
                    {
                        "type": "attaches",
                        "data": {
                            "file": {
                                "url": "https://www.tesla.com/tesla_theme/assets/img/_vehicle_redesign/roadster_and_semi/roadster/hero.jpg",
                                "size": 91,
                                "name": "hero.jpg",
                                "extension": "jpg"
                            },
                            "title": "Hero"
                        }
                    },
                    {
                        "type": "paragraph",
                        "data": {
                            "text": "Create a directory for your module, enter it and run <mark class=\"cdx-marker\">npm init</mark> command."
                        }
                    },
                    {
                        "type": "paragraph",
                        "data": {
                            "text": "Create a directory for your module, enter it and run <span class=\"inline-code\">npm init</span> command."
                        }
                    },
                    {
                        "type": "header",
                        "data": {
                            "text": "Create a directory for your module, enter it and run <u class=\"cdx-underline\">npm init</u> command."
                        }
                    },
                    {
                        "type": "alert",
                        "data": {
                            "type": "danger",
                            "align": "left",
                            "message": "<strong>행사 smokes!</strong><br>내겐 휴식이 <em>필요해</em> 레알!."
                        }
                    }

                ]
            },
            // 추가 플러그인들 설정
            tools: {
                // Header 설정
                header: {
                    class: Header,
                    config: {
                        placeholder: '헤더를 넣으삼',
                        levels: [1, 2, 3, 4, 5, 6],
                        defaultLevel: 3,
                    },
                    shortcut: 'CMD+SHIFT+H',
                },
                linkTool: {
                    class: LinkTool,
                    config: {
                        header: '', // get request header 선택사항
                        //백엔드 데이터 가져오깅( Cross Origin에 주의)
                        endpoint: 'http://localhost:9004/editor/link',
                    }
                },
                raw: {
                    class: RawTool,
                    config: {
                        placeholder: "플레이스 홀더랑"
                    }
                },
                simImg: {
                    class: SimpleImage
                    //No Config
                },
                image: {
                    class: ImageTool,
                    config: {
                        // Your backend file uploader endpoint
                        byFile: 'http://localhost:9004/uploadFile',

                        // Your endpoint that provides uploading by Url
                        byUrl: 'http://localhost:9004/fetchUrl',
                        buttonContent: "파일 올릴거얌",
                        actions: [
                            {
                                name: 'new_button',
                                icon: '<svg>...</svg>',
                                title: 'New Button',
                                toggle: true,
                                action: (name) => {
                                    alert(`${name} button clicked`);
                                }
                            }
                        ]
                    }
                },
                checklist: {
                    class: Checklist,
                    inlineToolbar: true
                    // No Config
                },
                list: {
                    class: List,
                    inlineToolbar: true,
                    config: {
                        defaultStyle: 'unordered'
                    }
                },
                embed: {
                    class: Embed,
                    inlineToolbar: true,
                    config: {
                        services: {
                            youtube: true,
                            coub: true
                        }
                    }
                },
                quote: {
                    class: Quote,
                    inlineToolbar: true,
                    shortcut: 'CMD+SHIFT+O',
                    config: {
                        quotePlaceholder: 'Quote 입력',
                        captionPlaceholder: 'Quote\'s 작자들',
                    },
                },
                table: {
                    class: Table,
                    inlineToolbar: true,
                    config: {
                        rows: 2,
                        cols: 3,
                        withHeadings: true
                    },
                },
                nestedlist: {
                    class: NestedList,
                    inlineToolbar: true,
                    config: {
                        defaultStyle: 'unordered'
                    },
                },
                delimiter: {
                    class: Delimiter
                    //No Config
                },
                warning: {
                    class: Warning,
                    inlineToolbar: true,
                    shortcut: 'CMD+SHIFT+W',
                    config: {
                        titlePlaceholder: 'Title',
                        messagePlaceholder: 'Message',
                    },
                },
                code: {
                    class: CodeTool,
                    placeholder: "코드 적어주삼"
                },
                attaches: {
                    class: AttachesTool,
                    config: {
                        /**
                         * Custom uploader
                         */
                        uploader: {
                            /**
                             * Upload file to the server and return an uploaded image data
                             * @param {File} file - file selected from the device or pasted by drag-n-drop
                             * @return {Promise.<{success, file: {url}}>}
                             */
                            uploadByFile(file) {
                                // your own uploading logic here
                                return MyAjax.upload(file).then((response) => {
                                    return {
                                        success: 1,
                                        file: {
                                            url: response.fileurl,
                                            // any data you want
                                            // for example: name, size, title
                                        }
                                    };
                                });
                            },
                        }
                    }
                },
                marker: {
                    class: Marker,
                    shortcut: 'CMD+SHIFT+M',
                    //No Config
                },
                inlineCode: {
                    class: InlineCode,
                    shortcut: 'CMD+SHIFT+C',
                    //No Config
                },
                underline: {
                    class: Underline
                    //No Config
                },
                alert: {
                    class: Alert,
                    inlineToolbar: true,
                    shortcut: 'CMD+SHIFT+A',
                    config: {
                        defaultType: 'primary',
                        messagePlaceholder: 'Enter something',
                    }
                },
                code2 : {
                    class: editorjsCodeflask,
                }
            }

        });
    </script>
</body>

</html>

실행 결과를 보면(항상 F12화면도 같이 확인하장) 아래당. 

웬 만큼 자바스크립트를 한 사람이라면 소스만 보고도 짐작할 수 있겠지만,

혹 위 소스의 기럭지가 기러  맘에 부담이 오는 사람들이 있을 수 있어 간략히 뽀인또를 설명하면

editor.js를 쓰기 위해서는 아래 설정형식이 기본 틀이당.(어렵지 않당!)

	<div id="myHolder></div>
	<!-- Editor.js 코아, paragraph만 지원 -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
    <script>
    //에디터 생성
    const editor = new EditorJS({
          holder: myHolder,
          //초기 데이터 넣는 부분
          data: {
             blocks: [
             /* 해당 플러그인에 맞게 설정 */
             ]
          },
          // 플러그인 설정 부분
          tools:{
          }
   }
   </script>

위 상태에서 만약 Header 플러그인을 추가하고 싶다면, 라이브러리 추가 후 tools  속성에 설정한당.

	<div id="myHolder></div>
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
    <!-- Header Plugin 추가 -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script>
    <script>
    const editor = new EditorJS({
          holder: myHolder,
          //초기 데이터 넣는 부분
          data: {
             blocks: [
             ]
          },
          // 플러그인 설정 부분
          tools:{
           // Header 플러그인 설정
                header: {
                    class: Header,
                    config: {
                        placeholder: '헤더를 넣으삼',
                        levels: [1, 2, 3, 4, 5, 6],
                        defaultLevel: 3,
                    },
                    shortcut: 'CMD+SHIFT+H',
                }
          }
   }
   </script>

만약 Header 플러그인을 이용해, 처음부터 페이지에 값을 넣어놓고 싶다면

아래 처럼 data속성의 blocks 배열안에 설정하면 되는 뎅,  곧 editor.js 사용법은 3가지의 반복이당

해당 플러그인 라이브러리 추가, 플러그인 설정, 필요하면 플러그인사용 데이터 입력이당.

맨 처음 긴 소스를 다시 보면, 이제 보이기 시작할 거시당.(그저 연속 반복일 뿌니당!)

	<div id="myHolder></div>
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
    <!-- Header Plugin 추가 -->
    <script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script>
    <script>
    const editor = new EditorJS({
          holder: myHolder,
          data: {
             blocks: [
             	//Header 플러그인 사용 데이터 넣깅!
                {
                   "type": "header",
                   "data": {
                       "text": "이것은 첨부터 보이는 데이터",
                       "level": 2
                    }
                }
           ]
          },
          // 플러그인 설정 부분
          tools:{
                header: {
                    class: Header,
                    config: {
                        placeholder: '헤더를 넣으삼',
                        levels: [1, 2, 3, 4, 5, 6],
                        defaultLevel: 3,
                    },
                    shortcut: 'CMD+SHIFT+H',
                }
          }
   }
   </script>

내용 저장/바꾸깅은 그냥 소스보면 느낌 올거공, 바꾸깅은 data 형식을 한번은 눈여겨 보장!

DB에 저장한다면, 아주 특별한 경우가 아니면, 내용 통짜로 저장하는 것이 정신 건강에 좋공,

내용 통짜 저장이 아니공, 부분 포함, 전체 틀이 정해져 있다면, 구찌 블록에디터를 사용해야

하는 가?(지금 원하는 게 무언가? 저울질)에 대한 고민이 필요하당.(물론 선택과 조율의 문제당!)

 

아기가 섬마을에 굴 따러 가면, 엄마는 집에 남아

바다가 불러주는 자장노래에 꿀잠을 잔당! 

다행이당!  아기가 태어날 때부터 별명이 아기 장사였단당! ~~~ ㅋㅋㅋ

 

아기 장사라닝?  오호! 일반적이지 않당. 예외적이당. 그것도 아주 마닝

이에 리액션하는 태도로 그 사람의 성향이나 때론 지능을 짐작할 수 있당.

난 아기장사가  넘치듯 가득 차고 찬 무건 굴 바구니들을  기럭지 길고 길게 

기차놀이 처럼 끈으로 이어 마을 길을 요리조리 뒤뚱뒤뚱 우스며 가는 상상을 했당.

(평가 결과는 나이 기준 현실성 제로 바보로 나왔당!~~~ 흥!!) 

 

 

사랑을 아무것도 아닌 걸로 맹그는 거, (아기장사?) 

https://www.youtube.com/watch?v=ogoIxkPjRts 

 

관련글 더보기