728x90

https://summernote.org/deep-dive/

 

Summernote - Super Simple WYSIWYG editor

Super Simple WYSIWYG Editor on Bootstrap Summernote is a JavaScript library that helps you create WYSIWYG editors online.

summernote.org

 

Pom.xml

  <!-- 대용량 파일 처리 -->
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.5</version>
</dependency>

<!-- gson java인스턴스를 JSN타입의 문자열로 변환해야하는 일 -->
<dependency> 
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.8.6</version>
</dependency>

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.2.1</version>
</dependency>

application.xml ( 업로드 크기 제한 )

<!-- 스프링에서 기본으로 제공하는 multipartResolver는 CommonsMultipartResolver 이므로,
순수한 multipartResolver를 사용하기 위해 빈 이름으로 "multipartResolver"를 등록해야함 +
 프로퍼티를 이용 최대 가능한 업로드 사이즈 지정함 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="100000000"></property>
</bean>

 

JSP

<!-- 서머노트 CDN  -->
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script>
<script src=" https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/lang/summernote-ko-KR.min.js"></script>
<form id="articleForm">
    <h3 style="margin-bottom: 25px;"></h3>
    <div class="form-group">
        <input type="text" class="form-control" name="title" id="title" placeholder="제목" maxlength="30" required>
    </div>
    <div class="form-group">
        <div id="test_cnt">( 0 / 1000 )</div>
        <textarea class="form-control" name="content" id="summernote"></textarea>
    </div>
</form>
<button class="btn btn-primary pull-right" onclick="boardWrite()">저장</button>
 
 
 
 <script>
    $("#summernote").summernote({
        // 에디터 높이
        height: 600,
        placeholder:"1000자 까지 가능",
        // 에디터 한글 설정
        lang: "ko-KR",
        // 에디터에 커서 이동 (input창의 autofocus라고 생각하시면 됩니다.)
        focus : true,
        toolbar: [
            // 글꼴 설정
            ['fontname', ['fontname']],
            // 글자 크기 설정
            ['fontsize', ['fontsize']],
            // 굵기, 기울임꼴, 밑줄,취소 선, 서식지우기
            ['style', ['bold', 'italic', 'underline','strikethrough', 'clear']],
            // 글자색
            ['color', ['forecolor','color']],
            // 표만들기
            ['table', ['table']],
            // 글머리 기호, 번호매기기, 문단정렬
            ['para', ['ul', 'ol', 'paragraph']],
            // 줄간격
            ['height', ['height']],
            // 그림첨부, 링크만들기, 동영상첨부
            ['insert',['picture','link','video']]
            // 코드보기, 확대해서보기, 도움말
            //['view', ['codeview','fullscreen', 'help']]
        ],
        callbacks : {
            onImageUpload : function(files, editor, welEditable) { // 파일 업로드(다중업로드를 위해 반복문 사용)
                for (var i = files.length - 1; i >= 0; i--) {
                    uploadSummernoteImageFile(files[i], this);
                }
            },
            onChange:function(contents, $editable){ //텍스트 글자수 및 이미지등록개수
                 setContentsLength(contents, 0);
            }
        },
        fontNames: ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New','맑은 고딕','궁서','굴림체','굴림','돋음체','바탕체'], // 추가한 글꼴
        // 추가한 폰트사이즈
        fontSizes: ['8','9','10','11','12','14','16','18','20','22','24','28','30','36','50','72']
    });
</script>

 

JSP include JS



// 서머노트 파일 업로드
function uploadSummernoteImageFile(file, el) {

    let data = new FormData();
    data.append("file", file);
    $.ajax({
        data : data,
        type : "POST",
        url : "/board/uploadSummernoteImageFile",
        contentType : false,
        enctype : 'multipart/form-data',
        processData : false,
        success : function(data) {
            if(data.responseCode == "success") {
                setTimeout(function () {
                    $(el).summernote('insertImage', data.url, function ($image) {
                        $image.css('width', "20%");
                    });
                }, 2000);
            }else if(data.responseCode == "extension"){
                showModal("alertModal","gif,jpg,png만 가능합니다.");
                return false;
            }else{
                showModal("alertModal","파일 업로드에 실패 하였습니다.");
                return false;
            }
        }
    });
}

//글자수 체크 //태그와 줄바꿈, 공백을 제거하고 텍스트 글자수만 가져옵니다.
function setContentsLength(str, index) {
    var textCnt = 0;                                    //총 글자수
    var maxCnt = 1000;                                  //최대 글자수
    var editorText = f_SkipTags_html(str);              //에디터에서 태그를 삭제하고 내용만 가져오기
    //editorText = editorText.replace(/\s/gi,"");         //줄바꿈 제거
    //editorText = editorText.replace(/&nbsp;/gi, "");    //공백제거
    textCnt = editorText.length;
    if(maxCnt > 0) {
        if(textCnt > maxCnt) {
            $('#summernote').summernote('code', str.slice(0 , -1 ));
            return false;
        }else{
            $("#test_cnt").html( "( "+ textCnt + " / " + maxCnt + " )");
        }
    }
}

//에디터 내용 텍스트 제거
function f_SkipTags_html(input, allowed) { // 허용할 태그는 다음과 같이 소문자로 넘겨받습니다. (<a><b><c>)
    allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('');
    var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
    commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
    return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
        return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
    });
}



// 글저장하기
function boardWrite(){

    if($("#title").val() == ""){
        showModal("alertModal","제목을 입력하세요");
        return false;
    }

    if ($('#summernote').summernote('isEmpty')) {
        showModal("alertModal","내용을 입력하세요");
        return false;
    }

    var formData = $("#articleForm").serialize();
    $.ajax({
        type: "POST",
        enctype: 'multipart/form-data',
        processData: false,
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        url: "/board/insertBoard",
        data: formData,
        dataType: "json",
        async : false,
        success : function(data) {
            alert(data);
        }
    });
}

 

Controller

/** 게시판 - 자유게시판 쓰기 :: 서머노트 이미지 파일 저장 */
@RequestMapping(value="/uploadSummernoteImageFile", produces = "application/json; charset=utf8")
@ResponseBody
    public String uploadSummernoteImageFile( @RequestParam("file") MultipartFile multipartFile, HttpServletRequest request )  {
    JsonObject jsonObject = new JsonObject();

    /* String fileRoot = "C:\\summernote_image\\"; // 외부경로로 저장을 희망할때. */
    String fileRoot = request.getServletContext().getRealPath("resources/images/board/");  // 내부경로로 저장
    String originalFileName = multipartFile.getOriginalFilename();                          //오리지널 파일명
    String extension = originalFileName.substring(originalFileName.lastIndexOf("."));   //파일 확장자

    final String[] ALLOW_EXTENSION = {".gif",".GIF", ".jpg",".JPG",".png",".PNG",".jepg",".JEPG"};   //확장자 검사
    if(!Arrays.asList(ALLOW_EXTENSION).contains(extension)){
        jsonObject.addProperty("responseCode", "extension");
        String a = jsonObject.toString();
        return a;
    }

    String savedFileName = UUID.randomUUID() + extension;  // 파일 이름이 한글로 들어왔을 때 그걸 다시 영어와 숫자로 이루어진 문자열로 만들기 위해서 쓰는 것 : UUTID이다
    File targetFile = new File(fileRoot + savedFileName);
    try {
        InputStream fileStream = multipartFile.getInputStream();
        FileUtils.copyInputStreamToFile(fileStream, targetFile);

        // 파일 저장
        jsonObject.addProperty("url", "/resources/images/board/"+ savedFileName); // contextroot + resources + 저장할 내부 폴더명
        jsonObject.addProperty("responseCode", "success");

    } catch (IOException e) {
        FileUtils.deleteQuietly(targetFile);   //저장된 파일 삭제
        jsonObject.addProperty("responseCode", "error");
        e.printStackTrace();
    }
    String a = jsonObject.toString();
    return a;
}


/** 게시판 - 자유게시판 쓰기 ::  글 저장하기 */
@ResponseBody
@PostMapping("/insertBoard")
public String insertBoard( HttpSession session, @ModelAttribute("boardVo") BoardDTO boardDTO){
    String user_id = (String)session.getAttribute("user_id");
    String user_nm = (String)session.getAttribute("user_nm");
    boardDTO.setWriter(user_nm);
    boardDTO.setWriter_id(user_id);
    int idx = boardService.insertBoard(boardDTO);

    if(idx > 0){ return "success"; }
    else{ return "fail"; }
}

 

728x90

+ Recent posts