스프링 부트 CKEditor

스프링 부트 CKEditor 18 - 이미지 저장 (백엔드)

https://youtu.be/IvWPLV4-8bc?si=laD3zBEVK2B6IHLF

스프링 부트 CKEditor 18 - 이미지 저장 (백엔드)


Spring Boot + CKEditor : AWS S3 이미지 업로드 백엔드 구현하기

이번 글에서는 CKEditor 5에서 업로드한 이미지를 Spring Boot 백엔드에서 처리하여 AWS S3에 저장하는 과정을 구현한다.

이번 단계에서 핵심은:

이미지를 로컬에 임시 저장한 뒤 AWS S3로 업로드하고, 업로드된 URL을 다시 CKEditor에 반환하는 것

이다.


1. 전체 흐름 이해

이미지 업로드 전체 구조는 다음과 같다.

CKEditor
   ↓
Spring Boot Controller
   ↓
Service
   ↓
로컬 임시 저장
   ↓
AWS S3 업로드
   ↓
S3 URL 반환
   ↓
CKEditor 이미지 출력

2. 왜 로컬 저장 후 S3 업로드를 할까?

Spring Boot는 업로드된 파일을:

MultipartFile

형태로 받는다.

하지만 AWS S3 SDK는:

File 객체

기반 업로드가 가장 안정적이다.

따라서:

MultipartFile
→ 로컬 File 변환
→ S3 업로드

흐름으로 구현한다.


3. 이미지 업로드 전체 백엔드 흐름


STEP 1

CKEditor가:

POST /image-upload

요청 전송


STEP 2

Controller에서 이미지 수신


STEP 3

Service로 이미지 전달


STEP 4

로컬에 임시 저장


STEP 5

S3 업로드


STEP 6

S3 URL 획득


STEP 7

로컬 파일 삭제


STEP 8

CKEditor로 URL 반환


4. ImageController 생성


기본 구조

@Controller
public class ImageController {
}

5. 업로드 API 생성

@PostMapping("/image-upload")
@ResponseBody
public Map<String, Object> imageUpload(
        MultipartHttpServletRequest request) {

}

6. MultipartHttpServletRequest란?

이미지 업로드는:

multipart/form-data

형태로 전달된다.

따라서 일반 RequestBody가 아니라:

MultipartHttpServletRequest

사용


7. 왜 @ResponseBody를 사용할까?

이번 API는:

HTML 페이지 반환 X

이다.

대신:

{
  "uploaded": true,
  "url": "이미지주소"
}

형태 JSON 반환 필요


8. ImageService 생성


기본 구조

@Service
public class ImageService {
}

9. S3 객체 주입

private final AmazonS3 amazonS3;

public ImageService(AmazonS3 amazonS3) {
    this.amazonS3 = amazonS3;
}

10. application.properties 버킷명 가져오기

@Value("${cloud.aws.s3.bucket}")
private String bucket;

11. 업로드 메소드 생성

public String uploadImage(
        MultipartHttpServletRequest request)
        throws IOException {

}

12. 업로드 파일 추출

CKEditor는 기본적으로:

upload

키 이름으로 파일 전송


파일 추출

MultipartFile file = request.getFile("upload");

13. 원본 파일명 가져오기

String originalFileName =
        file.getOriginalFilename();

14. 확장자 추출

String extension =
        originalFileName.substring(
                originalFileName.lastIndexOf("."));

15. UUID 적용 이유

실무에서 가장 중요한 부분 중 하나다.


문제 상황

사용자들이:

image.png

동일 파일명 업로드 가능


문제 발생

S3에서는:

동일 파일명 = 덮어쓰기

발생


16. UUID 적용

String uuid =
        UUID.randomUUID().toString();

새 파일명 생성

String saveFileName =
        uuid + extension;

결과 예시

e7d1c2a1-xxxx-xxxx.png

17. 로컬 저장 경로 생성

String localPath =
        "C:/upload/" + saveFileName;

Linux 예시:

String localPath =
        "/home/upload/" + saveFileName;

18. 로컬 File 객체 생성

File localFile = new File(localPath);

19. MultipartFile → File 저장

file.transferTo(localFile);

이 순간:

서버 로컬 디스크 저장 완료

20. AWS S3 업로드


핵심 코드

amazonS3.putObject(
        bucket,
        saveFileName,
        localFile
);

21. Public Read 권한 설정

이미지를 웹에서 조회하려면:

PutObjectRequest request =
        new PutObjectRequest(
                bucket,
                saveFileName,
                localFile
        ).withCannedAcl(
                CannedAccessControlList.PublicRead
        );

amazonS3.putObject(request);

왜 필요한가?

이 설정 없으면:

403 Forbidden

발생 가능


22. S3 URL 가져오기

String imageUrl =
        amazonS3
                .getUrl(bucket, saveFileName)
                .toString();

23. 로컬 파일 삭제

S3 업로드 완료 후:

localFile.delete();

왜 삭제할까?

서버 디스크 누적 방지

실무에서 매우 중요


24. 최종 URL 반환

return imageUrl;

25. Controller에서 서비스 호출

String imageUrl =
        imageService.uploadImage(request);

26. CKEditor 응답 형식 만들기

CKEditor는 특정 JSON 형식을 요구한다.


응답 객체 생성

Map<String, Object> response =
        new HashMap<>();

업로드 성공 여부

response.put("uploaded", true);

이미지 URL

response.put("url", imageUrl);

27. 최종 반환

return response;

28. 최종 Controller 전체 코드

@PostMapping("/image-upload")
@ResponseBody
public Map<String, Object> imageUpload(
        MultipartHttpServletRequest request)
        throws IOException {

    String imageUrl =
            imageService.uploadImage(request);

    Map<String, Object> response =
            new HashMap<>();

    response.put("uploaded", true);
    response.put("url", imageUrl);

    return response;
}

29. 실행 결과

이미지 업로드 시:

CKEditor
↓
Spring Boot
↓
S3 저장
↓
URL 반환
↓
자동 이미지 출력

완료


30. S3에서 확인되는 모습

버킷 내부:

UUID_파일명.png

형태로 저장


31. 실무에서 중요한 개선 포인트


1) 파일 검증 필요

현재는 모든 파일 업로드 가능

실무에서는:

  • MIME TYPE 검사
  • 확장자 제한
  • 파일 크기 제한

필수


2) 악성 파일 방지

실무에서는:

  • 이미지 위장 실행파일
  • 스크립트 삽입

방어 필요


3) Presigned URL 방식 고려

대규모 서비스에서는:

Spring Boot 서버를 거치지 않고
브라우저 → S3 직접 업로드

구조 사용


32. 현재 구조의 장점

현재 방식은:

  • 구현 쉬움
  • 학습용 최적
  • 구조 이해 쉬움

장점 존재


33. 이번 단계 핵심 이해 포인트

이번 글 핵심은:

MultipartFile
↓
File 변환
↓
S3 업로드
↓
URL 반환

흐름 이해

이다.


정리

이번 글에서는:

  • CKEditor 이미지 업로드 처리
  • MultipartFile 수신
  • UUID 파일명 생성
  • 로컬 임시 저장
  • AWS S3 업로드
  • 이미지 URL 반환

전체 흐름을 구현했다.


핵심 요약

  • MultipartHttpServletRequest 사용
  • MultipartFile 추출
  • UUID로 파일명 충돌 방지
  • 로컬 저장 후 S3 업로드
  • 업로드 완료 후 로컬 파일 삭제
  • CKEditor 요구 JSON 형식 반환

© 2020. All rights reserved.

SIKSIK