본문 바로가기
공부/TroubleShootings

ckeditor4 사용하기 (spring, jsp, ckeditor 4)

by thegreatjy 2023. 12. 25.
728x90

괜찮은 에디터로는 tinymce, ckeditor가 있다.
나는 spring framework를 사용, 뷰로는 jsp를 사용할 것이다.

  • tinymce
    • jsp에서 안 되는 듯 하다.. html 정적으로는 되는데 tomcat 서버를 통해 jsp로 출력이 되지 않는다...그냥 내가 잘 몰라서 그런 걸 수도..
  • ckeditor 5
    • npm 으로 어찌저찌 js 라이브러리 같은 걸 깔긴 했는데.. 그.. 파일 업로드 방법을 잘 모르겠다.. 아무리해도 사진이 서버에 업로드 되지 않는걸...
  • ckeditor 4
    • 관련 블로그 글이 많아서 성공! ckeditor5로 해내고 싶었는데.. ㅠㅠ 공식 문서 보는 방법을 더 익힌 후에 시도해보는 걸로 하자!

아래 링크는 참고할만한 블로그 글을 모아놨다...


trouble shootings


나의 코드

깃허브에서 전체 코드 확인하기

https://github.com/thegreatjy/ChunjaeFullStack/tree/main/Spring_Study/ckeditor

실행환경

  • java jdk 11
  • ckeditor v4.22.1

https://velog.io/@joon1106/CKeditor4-사용사진-업로드#게시판-작성-수정-만든뒤-구현이-잘되는것을-확인한뒤-ckeditor-추가하는것을-추천-1

이미지 업로드 부분은 이 윗 분의 코드를 거의 복사했더니 됐다 !

  • 디렉터리 구조

  • pom.xml

    • 필요없는 것들도 포함되어있다.. 귀찮아서 전체 복사를 한다.
    • commons-io, commons-fileupload, org.json 이건 추가해주어야 한다..아마도?
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>ckeditor</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>ckeditor</name>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <junit.version>5.9.2</junit.version>

        <java-version>11</java-version>
        <org.springframework-version>5.3.29</org.springframework-version>
        <org.aspectj-version>1.9.6</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <!--<scope>runtime</scope>-->
        </dependency>

        <!-- @Inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- jakarta servlet -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- lombok -->
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>

        <!-- DB -->
        <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>3.3.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>

        <!-- fileupload(사진 업로드) -->
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.15.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.5</version>
        </dependency>

        <!-- ckeditor json -->
        <!-- https://mvnrepository.com/artifact/org.json/json -->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20231013</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
        </plugins>
    </build>
</project>
  • servlet-context.xml
<!-- 사진 업로드 -->
    <beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

    <!-- ckeditor통해 업로드된 사진의 저장 경로 -->
    <resources mapping="/uploads/**" location="/resources/uploads/" />
  • ckeditor4.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.7.1.slim.js" integrity="sha256-UgvvN8vBkgO0luPSUl2s8TIlOSYRoGFAX4jlCIm9Adc=" crossorigin="anonymous"></script>
  <script src="/resources/ckeditor/ckeditor.js"></script>
  <!-- <script src="ckeditor.js"></script> -->
</head>
<body>

<div class="mb-3">
    <form method="post" action="/upload">
    <textarea class="form-control" id="content" name="content" rows="3"></textarea>
    <script>
        var ckeditor_config = {
            width: "100%",
            height:"400px",
            image_previewText: '이건어디에',
            resize_enabled : false,
            enterMode : CKEDITOR.ENTER_BR,
            shiftEnterMode : CKEDITOR.ENTER_P,
            filebrowserUploadUrl : "/food/imageUpload.do"
    };

    CKEDITOR.replace("content", ckeditor_config);

    //이미지 업로드가 끝나고 실행하는 함수
    CKEDITOR.on( 'dialogDefinition', function( ev ) {
        // Take the dialog name and its definition from the event data.
        let dialogName = ev.data.name;
        let dialogDefinition = ev.data.definition;

        let uploadTab = dialogDefinition.getContents( 'Upload' );
        let uploadButton = uploadTab.get('uploadButton');

        uploadButton['filebrowser']['onSelect'] = function( fileUrl, errorMessage ) {

        }

    });
    </script>
    <input type="submit" value="전송">
    </form>
</div>

</body>
</html>
  • testController.java
// 에디터 문자열 콘솔에 확인
@PostMapping("/upload")
    public void ckeditorUpload(Content content){
        System.out.println(content);
    }

    // 이미지 업로드
    @PostMapping("/food/imageUpload.do")
    public void imageUpload(HttpServletRequest request,
                            HttpServletResponse response, MultipartHttpServletRequest multiFile
            , @RequestParam MultipartFile upload) throws Exception {
        // 랜덤 문자 생성
        UUID uid = UUID.randomUUID();

        OutputStream out = null;
        PrintWriter printWriter = null;

        //인코딩
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        try {
            //파일 이름 가져오기
            String fileName = upload.getOriginalFilename();
            byte[] bytes = upload.getBytes();

            //이미지 경로 생성
            System.out.println("\n\n ===== 현재 경로 : " + request.getContextPath());
            //String path = "/Users/Desktop/Spring_Study/ckeditor/src/main/webapp/resources/uploads/";    // 이미지 경로 설정(폴더 자동 생성)
            String path = "/resources/uploads/";    // 이미지 경로 설정(폴더 자동 생성)
            String ckUploadPath = path + uid + "_" + fileName;
            File folder = new File(path);
            System.out.println("path:" + path);    // 이미지 저장경로 console에 확인
            //해당 디렉토리 확인
            if (!folder.exists()) {
                try {
                    folder.mkdirs(); // 폴더 생성
                } catch (Exception e) {
                    e.getStackTrace();
                }
            }
            out = new FileOutputStream(new File(ckUploadPath));
            out.write(bytes);
            out.flush(); // outputStram에 저장된 데이터를 전송하고 초기화

            String callback = request.getParameter("CKEditorFuncNum");
            printWriter = response.getWriter();
            String fileUrl = "/food/ckImgSubmit.do?uid=" + uid + "&fileName=" + fileName; // 작성화면

            // 업로드시 메시지 출력
            printWriter.println("{\"filename\" : \"" + fileName + "\", \"uploaded\" : 1, \"url\":\"" + fileUrl + "\"}");
            printWriter.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (printWriter != null) {
                    printWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return;
    }

    // 서버로 전송된 이미지 뿌려주기
    @RequestMapping(value="/food/ckImgSubmit.do")
    public void ckSubmit(@RequestParam(value="uid") String uid
            , @RequestParam(value="fileName") String fileName
            , HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //서버에 저장된 이미지 경로
        //String path = "/Users/Desktop/Spring_Study/ckeditor/src/main/webapp/resources/uploads/";    // 저장된 이미지 경로
        String path = "/resources/uploads/";    // 이미지 경로 설정(폴더 자동 생성)
        System.out.println("path:" + path);
        String sDirPath = path + uid + "_" + fileName;

        File imgFile = new File(sDirPath);

        //사진 이미지 찾지 못하는 경우 예외처리로 빈 이미지 파일을 설정한다.
        if (imgFile.isFile()) {
            byte[] buf = new byte[1024];
            int readByte = 0;
            int length = 0;
            byte[] imgBuf = null;

            FileInputStream fileInputStream = null;
            ByteArrayOutputStream outputStream = null;
            ServletOutputStream out = null;

            try {
                fileInputStream = new FileInputStream(imgFile);
                outputStream = new ByteArrayOutputStream();
                out = response.getOutputStream();

                while ((readByte = fileInputStream.read(buf)) != -1) {
                    outputStream.write(buf, 0, readByte);
                }

                imgBuf = outputStream.toByteArray();
                length = imgBuf.length;
                out.write(imgBuf, 0, length);
                out.flush();

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                outputStream.close();
                fileInputStream.close();
                out.close();
            }
        }
    }

path를 내 로컬 컴퓨터의 절대경로로 했을 때 사진 전송 및 저장이 잘 된다..

하지만 aws 배포를 하게 되거나 다른 팀원이 테스트할 때에는 경로를 바꿔 주어야 하는데 아직 해결하지 못했다.

request.getContextPath로 하면 빈 문자열이 반환되어 어떻게 해야할지 모르겠다..

⇒ 해결 : https://ninedc.tistory.com/entry/JAVA-Spring-위지윅-CK-에디터CKEditor-4-파일업로드

  1. pom.xml

@Resource 애너테이션 사용을 위함

<!-- ckeditor @Resource -->
        <!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
  1. servlet-context.xml에 업로드 경로를 추가
<!-- ckeditor통해 업로드된 사진의 저장 경로 -->
    <beans:bean class="java.lang.String" id="uploadPath">
        <beans:constructor-arg value="/Users/Desktop/Spring_Study/ckeditor/src/main/webapp/resources/"></beans:constructor-arg>
    </beans:bean>
    <resources mapping="/uploads/**" location="/resources/uploads/" />
  1. testController.java에 멤버변수로 String path 설정해준다. @Resource통해 servlet-context.xml에 지정해놓은 경로와 맵핑.
package com.example.ckeditor;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.*;
import java.util.UUID;

@Controller
public class testController {
    @Resource(name="uploadPath")
    private String uploadPath;

    ... 다른 메서드들 생략...
}
  1. 경로 사용법
// 이미지 업로드
    @PostMapping("/food/imageUpload.do")
    public void imageUpload(HttpServletRequest request,
                            HttpServletResponse response, MultipartHttpServletRequest multiFile
            , @RequestParam MultipartFile upload) throws Exception {
            ckUploadPath = uploadPath + File.separator + "uploads" + File.separator + uid + "_" + fileName;

ckUploadPath = uploadPath + File.separator + "uploads" + File.separator + uid + "_" + fileName;

이 부분이다. 멤버변수인 uploadPath을 사용하여 경로 지정을 한다. 풀 코드는 바로 위 url에서 얻을 수 있다.

  1. 결과

System.out.println(uploadPath);

System.out.println(ckUploadPath);

을 했을 때, servlet-context.xml에 지정한 경로가 나온다.

728x90