본문 바로가기
IT/Spring boot

[Spring boot] Ck Editor5 파일 업로드 구현

by kyu-nahc 2024. 8. 12.
반응형

개발 환경

  • Spring boot 3.3.0
  • Java 21
  • IntelliJ
  • Maven
  • Ck Editor 5
  • Thymeleaf

 

CK Editor 설정

CK Editor는 게시글을 작성 시 글쓰기 툴을 제공하는 JavaScript 프레임워크이다.

CK Editor를 통해 글씨 크기와 굵기, 도표, 사진 및 링크까지 추가할 수 있다.

해당 기능을 직접 구현할 수 있지만, 이미 구현되어 있는  프레임워크를 사용하여,

소요 시간을 줄이고 좀 더 기능적인 완성도를 높일 수 있다.

먼저 CK Editor를 사용하기 위해 HTML 파일CDN을 연결해 준다.

 

HTML CDN 연결

<label for="editor" class="editor">Q&A Content</label>
<textarea class="form-control content" 
	id="editor" placeholder="내용을 입력해주세요">
</textarea>

<script src="https://cdn.ckeditor.com/ckeditor5/12.4.0/classic/ckeditor.js"></script>
<script src="https://ckeditor.com/apps/ckfinder/3.5.0/ckfinder.js"></script>

 

  • textarea 태그를 생성하고 고유한 id를 하나 부여한다.
    해당 id의 textarea를 CK Editor로 이용한다.
  • CDN 2개를 연결한다.
    첫 번째 CDN은 CK Editor Tool을 이용하기 위한 것이고,

    ckfinder는 CK Editor에서 이미지 첨부를 위한 것이다.

JS Editor 생성

let myEditor;
ClassicEditor
    .create(document.querySelector("#editor"),{
        removePlugins: ['MediaEmbed'],
        ckfinder: {
            uploadUrl : '/private/board/upload'
        }
    })
    .then(editor => {
        myEditor = editor;
        console.log('Editor was initialized');
    })
    .catch(error => {
        console.log(error);
    });
  • ClassEditor 객체의 .create() 메소드를 사용하여 Editor를 생성한다.
  • removePlugins
    Editor에서 사용하지 않을 툴 플러그인을 제거할 수 있다. 
    여기서 MediaEmbed는 동영상 삽입으로 해당 플러그인은 제거하였다.
  • ckfinder
    ckfinder 속성에는 uploadUrl을 반드시 설정해야 한다.
    CK Editor 편집창에서 이미지를 첨부할 때, 이미지를 처리하기 위한 Contoller Url을 넣어준다.

 

HTML에서 CDN 및 textarea 태그를 하나 추가하고,

자바스크립트에서 Editor 생성 코드를 작성하면 다음과 같이 Editor가 생성되는 것을 볼 수 있다.

 

 

Java API 생성

Config Class 

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Bean
    public MappingJackson2JsonView jsonView(){
        return new MappingJackson2JsonView();
    }
}

 

첫 번째로는 config 파일을 하나 만들어줘야 한다.
CK Editor는 return 값으로 uploadedurl값을 json형식으로 받는데
Controller가 modelAndView를 통해 json 형식으로 return 할 수 있도록 해줘야 한다.

따라서 자바 객체를 JSON으로 변환해서 보여주는 뷰 구현 클래스인

MappingJackson2JsonView를 Bean으로 설정하여 사용한다.

MappingJackson2JsonView는 내부적으로 Jackson 라이브러리와

ObjectMapper를 이용해서 자바객체를 JSON 형식으로 변환해 준다.

Controller Class

@Controller
@RequestMapping("/private/board")
@RequiredArgsConstructor
public class PrivateBoardController {

    private final FileService fileService;

    @PostMapping("/upload")
    public ModelAndView uploadCKImage(MultipartHttpServletRequest request){
        ModelAndView mav = new ModelAndView("jsonView");

        String uploadPath = fileService.ckFileTempUpload(request);
        mav.addObject("uploaded",true);
        mav.addObject("url",uploadPath);
        return mav;
    }
}

 

JS에서 설정한 uploadUrl과 동일한 Url 경로의 Controller Class와 메소드를 하나 만들어준다.

ckfinder에서 설정한 uploadUrl이 '/private/board/upload' 이므로

해당 Mapping이 동일하도록 Class 및 메소드를 설정한다.

 

CK Editor는 이미지 업로드 후 이미지를 표시하기 위해 uploaded와 url 값을 json 형태로 받아야 하므로
ModelAndView를 사용하여 json 형식으로 보내기 위해 ModelAndView 객체를 만들어준다.

여기서 jsonView라는 이름은 위에서 @Bean으로 등록해 준 MappingJackson2JsonView 객체이다.

ModelAndView의 addObject 메소드를 통해 uploadedtrue,

url에는 해당 이미지 파일의 상대경로를 값으로 넣어준다.

FileService Class

@Service
@RequiredArgsConstructor
@Slf4j
public class FileService {

    private final ServletContext servletContext;

    public String ckFileTempUpload(MultipartHttpServletRequest request){

        MultipartFile uploadFile = request.getFile("upload");
        assert uploadFile != null;

        String originalFileName = uploadFile.getOriginalFilename();
        assert originalFileName != null;
        String saveFileName = getFetchFileName(originalFileName);

        String realPath = servletContext.getRealPath("/upload/temp/");
        String savePath = realPath + saveFileName;
        uploadFile(savePath,uploadFile);
        log.info("Save ck upload File. originalFileName-{} , saveFileName-{}", originalFileName,saveFileName);
        // Spring boot resource relative path
        return request.getContextPath() + "/upload/temp/" + saveFileName;
    }

    private String getFetchFileName(String originalFileName) {
        String ext = originalFileName.substring(originalFileName.lastIndexOf("."));
        return UUID.randomUUID() + ext;
    }
    
    private void uploadFile(String savePath, MultipartFile uploadFile) {
        File file = new File(savePath);
        try {
            uploadFile.transferTo(file);
        } catch (IOException e) {
            throw new RuntimeException("Failed to upload the file", e);
        }
    }
}

 

이제 Service 계층에서 파일 업로드를 구현하고 해당 파일 저장경로를 컨트롤러에게 전달하면 된다.

먼저 MultipartHttpServletRequestgetFile() 메소드를 통해 해당 파일 객체를 받아온다.

CK Editor에서 파일을 서버로 보낼 때 "upload"라는 이름으로 보내기 때문에, 

getFile("upload")를 통해 MultipartFile 객체로 받을 수 있다. 

 

그리고 getFetchFileName() 메소드를 통해 확장자를 추출한 후,

파일명이 겹치는 것을 방지하기 위해 UUID.randomUUID()를 통해 파일명을 랜덤값으로 지정한다.

그 후 ServletContext 객체를 이용하여, 경로를 지정하면 된다.

먼저 실제 파일을 저장하기 위해서 getRealPath()를 이용하여 프로젝트의 절대 경로를 가져온다.

단 여기서 주의할 점ServletContext의 getRealPath() 메소드는 

프로젝트 폴더 구조에서 resources가 아닌 webapp 폴더를 우선으로 찾고,

해당 폴더가 존재하지 않으면 임시 폴더를 찾아가게 된다.

 

따라서 main 아래에 webapp 폴더를 생성하고,

추가적인 경로를 설정해야 한다.

여기서는 /upload/temp라는 경로를 사용하여, temp라는 폴더 안에 이미지들이 저장될 것이다. 

그리고 해당 이미지의 경로를 return 해주어야 하는데,

여기서는 절대 경로가 아닌 프로젝트의 path +  '/upload/temp' + 파일명을  return 해주도록 한다.

 

이렇게 하면 CK Editor 상에서 이미지 업로드를 진행할 수 있다.

하지만 제출된 게시물의 이미지만 저장하는 것이 아닌,

CK Editor를 통해 업로드한 이미지를 모두 서버에 저장하게 된다.

이는 비효율적으로 게시물로 제출된 글의 이미지만 저장할 필요가 있다.

이에 대한 문제 해결은 아래 포스트에 나와있다.

 

[Spring boot] Ck Editor5 파일 업로드 최적화 과정

개발 환경Spring boot 3.3.0Java 21IntelliJMavenCk Editor 5Thymeleaf 파일 업로드 최적화 구현 방안 저번 포스트에서 CK Editor 설정 및 Java Api 코드를 설명하였다. [Spring boot] Ck Editor5 파일 업로드 구현개발 환경

kyu-nahc.tistory.com

 

 

 

 

반응형

loading