SpringBoot - TOAST UI Editor 로 게시글 사진 업로드

2024. 4. 24. 14:28Full Stack Course 풀스택과정/SPRING

728x90

배울내용:

SpringBoot 게시글 기본 업로드

TOAST UI Editor 로 업로드

TOAST UI Editor 로 사진 업로드

SpringBoot 게시글

SpringBoot 새 글쓰기

SpringBoot 사진 업로드

스프링부트 새 글 수정

스프링부트 게시글

토스트 UI 에디터 사용법

 

 

이글은 개인공부 기록용으로 작성된 글입니다. 틀린부분이 있을수있습니다. 

 

 

 

 

 

 

 

전체 코드는 아래의 깃헙 주소에서 확인가능하다 

https://github.com/angrybird24/SpringBoot_Projects

 

 

 

아래는 게시글 쓰는창에 붙여넣기하면 토스트 UI 에디터가 추가가 된다 

 

<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<script>


    const editor = new toastui.Editor({
        el: document.querySelector('#content21'), // 에디터를 적용할 요소 (컨테이너)
        height: '500px',                        // 에디터 영역의 높이 값 (OOOpx || auto)
        initialEditType: 'markdown',            // 최초로 보여줄 에디터 타입 (markdown || wysiwyg)
        initialValue: '',                       // 내용의 초기 값으로, 반드시 마크다운 문자열 형태여야 함
        previewStyle: 'vertical',               // 마크다운 프리뷰 스타일 (tab || vertical)
        placeholder: '내용을 입력해 주세요.',


        /* start of hooks */
        hooks: {
            async addImageBlobHook(blob, callback) { // 이미지 업로드 로직 커스텀
                try {
                    /*
                     * 1. 에디터에 업로드한 이미지를 FormData 객체에 저장
                     *    (이때, 컨트롤러 uploadEditorImage 메서드의 파라미터인 'image'와 formData에 append 하는 key('image')값은 동일해야 함)
                     */
                    const formData = new FormData();
                    formData.append('image', blob);

                    // 2. FileApiController - uploadEditorImage 메서드 호출
                    const response = await fetch('/tui-editor/image-upload', {
                        method : 'POST',
                        body : formData,
                    });

                    // 3. 컨트롤러에서 전달받은 디스크에 저장된 파일명
                    const filename = await response.text();
                    console.log('서버에 저장된 파일명 : ', filename);

                    // 4. addImageBlobHook의 callback 함수를 통해, 디스크에 저장된 이미지를 에디터에 렌더링
                    const imageUrl = `/tui-editor/image-print?filename=${filename}`;
                    callback(imageUrl, 'image alt attribute');

                } catch (error) {
                    console.error('업로드 실패 : ', error);
                }
            }
        }
        /* end of hooks */

    });

    editor.setHTML(document.getElementById('diary-content').value);
</script>

 

 

그중에 

 

editor.setHTML(document.getElementById('diary-content').value);

 

 

이부분이 나중에 content(내용) 에 넣었을때 값을 받아오는것이다 

 

 

 

 

 

 

 

수정 하는부분에서는 Hidden 으로 값을 저장해서 여기서 있는값을 위에있는걸로 불러오는 것이다 

NewDiary.html


    <!-- 아이디 정보 저장 -->
    <input type="hidden" id="article-id" th:value="${diary.id}">
    <input type="hidden" id="diary-content" th:value="${diary.content}">

 

 

 

전체코드는 아래와 같다

NewDiary.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>블로그 글</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <!-- TUI 에디터 CSS CDN -->
    <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />

</head>
<body>
<div class="p-5 mb-5 text-center</> bg-light">
    <h1 class="mb-3">My Diary</h1>
    <h4 class="mb-3">일기장 새로작성.</h4>
</div>

<div class="container mt-5">
    <div class="row">
        <div class="col-lg-8">
            <article>
                <!-- 아이디 정보 저장 -->
                <input type="hidden" id="article-id" th:value="${diary.id}">
                <input type="hidden" id="diary-content" th:value="${diary.content}">



                <header class="mb-4">
                    <input type="text" class="form-control" placeholder="제목" id="title" th:value="${diary.title}">
                </header>
                <input type="date" class="form-control mt-2" placeholder="날짜" id="start" th:value="${diary.start}"  >




                <h2 style="text-align: center;">TOAST UI Editor 글쓰기 페이지</h2>
                <div id="content21">

                </div>
                <script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
                <script>


                    const editor = new toastui.Editor({
                        el: document.querySelector('#content21'), // 에디터를 적용할 요소 (컨테이너)
                        height: '500px',                        // 에디터 영역의 높이 값 (OOOpx || auto)
                        initialEditType: 'markdown',            // 최초로 보여줄 에디터 타입 (markdown || wysiwyg)
                        initialValue: '',                       // 내용의 초기 값으로, 반드시 마크다운 문자열 형태여야 함
                        previewStyle: 'vertical',               // 마크다운 프리뷰 스타일 (tab || vertical)
                        placeholder: '내용을 입력해 주세요.',


                        /* start of hooks */
                        hooks: {
                            async addImageBlobHook(blob, callback) { // 이미지 업로드 로직 커스텀
                                try {
                                    /*
                                     * 1. 에디터에 업로드한 이미지를 FormData 객체에 저장
                                     *    (이때, 컨트롤러 uploadEditorImage 메서드의 파라미터인 'image'와 formData에 append 하는 key('image')값은 동일해야 함)
                                     */
                                    const formData = new FormData();
                                    formData.append('image', blob);

                                    // 2. FileApiController - uploadEditorImage 메서드 호출
                                    const response = await fetch('/tui-editor/image-upload', {
                                        method : 'POST',
                                        body : formData,
                                    });

                                    // 3. 컨트롤러에서 전달받은 디스크에 저장된 파일명
                                    const filename = await response.text();
                                    console.log('서버에 저장된 파일명 : ', filename);

                                    // 4. addImageBlobHook의 callback 함수를 통해, 디스크에 저장된 이미지를 에디터에 렌더링
                                    const imageUrl = `/tui-editor/image-print?filename=${filename}`;
                                    callback(imageUrl, 'image alt attribute');

                                } catch (error) {
                                    console.error('업로드 실패 : ', error);
                                }
                            }
                        }
                        /* end of hooks */

                    });

                    editor.setHTML(document.getElementById('diary-content').value);
                </script>

                <!-- id가 있을 때는 [수정] 버튼을, 없을 때는 [등록] 버튼이 보이게 함 -->
                <button th:if="${diary.id} != null" type="button" id="modify-btn" class="btn btn-primary btn-sm">수정</button>

                <button th:if="${diary.id} == null" type="button" id="create-btn" class="btn btn-primary btn-sm">등록</button>
            </article>
        </div>
    </div>
</div>

<script src="/js/Diary.js"></script>

</body>
</html>
<body>

</body>
</html>

 

 

 

이것만 가지고는 실행이 안되니 위에 있는 깃헙을 참고하자

 

 

 

 

 

 

만약 위에 있는 코드에서 수정 및 생성 버튼을 클릭했을때 아래와 같은 JS 코드가 작동하게 된다 

Diary.js

// 수정 기능
const modifyButton = document.getElementById('modify-btn');

if (modifyButton) {
    modifyButton.addEventListener('click', event => {
        let params = new URLSearchParams(location.search);
        let id = params.get('id');

        body = JSON.stringify({
            title: document.getElementById('title').value,
            content: editor.getHTML(),
        })

        function success() {
            alert('수정 완료되었습니다.');
            location.replace(`/diaries/${id}`);
        }

        function fail() {
            alert('수정 실패했습니다.');
            location.replace(`/diaries/${id}`);
        }

        httpRequest('PUT', `/api/diaries/${id}`, body, success, fail);
    });
}

// 생성 기능
const createButton = document.getElementById('create-btn');

if (createButton) {
    // 등록 버튼을 클릭하면 /api/diaries 요청을 보낸다
    createButton.addEventListener('click', event => {
        body = JSON.stringify({
            title: document.getElementById('title').value,
            content: editor.getHTML(),
        });

        function success() {
            alert('등록 완료되었습니다.');
            location.replace('/diaries');
        };

        function fail() {
            alert('등록 실패했습니다.');
            location.replace('/diaries');
        };

        httpRequest('POST', '/api/diaries', body, success, fail)
    });
}

 

 

 

이중에서 눈여겨 봐야할것은 아래와 같다

 

Diary.js

body = JSON.stringify({
    title: document.getElementById('title').value,
    content: editor.getHTML(),
});

 

만약 content를 TOAST UI 를 쓰게 되면은 title 과는 다르게 editor.getHTML(), 이라고 해야지 받아온다 

 

 

 

그리고 기존에 th:text 가 되있는걸 th:utext 로 해서 TOAST UI 에서 ol , ul , 글자스타일 변경했을때 자동으로 바뀌게 해주는것도 바꿔줘야한다 

 

Diary.html

<section class="mb-5">
    <p class="fs-5 mb-4" th:text="${diary.content}"></p>
</section>

 


<section class="mb-5">
    <p class="fs-5 mb-4" th:utext="${diary.content}"></p>
</section>

 

 

 

 

 

실행하면 아래와 같다 

 

 

로그인후에 글등록을 하면

 

 

 

 

 

 

이렇게 글 작성창이나오는데 이걸 굵은글씨 또는 ul ol 이런걸 추가하고 

사진도 넣어주자 



 

빨간색 화살표를 클릭해서 업로드 시키면 된다. 

그리고 등록해보면  아래처럼 등록된게 보인다 

 

 

 

 

 

굵은긁씨는 적용이 안됬는데 굵은글씨 클릭후 뛰어쓰기를 해버려서 그런것같다 붙여줘보니 성공적으로 보여지게 된다 

 

 

 

 

 

728x90