스프링 부트 CKEditor
스프링 부트 CKEditor 18 - 이미지 저장 (백엔드)
https://youtu.be/IvWPLV4-8bc?si=laD3zBEVK2B6IHLF
스프링 부트 CKEditor 18 - 이미지 저장 (백엔드)
- 스프링 부트 CKEditor 18 - 이미지 저장 (백엔드)
- 1. 전체 흐름 이해
- 2. 왜 로컬 저장 후 S3 업로드를 할까?
- 3. 이미지 업로드 전체 백엔드 흐름
- 4. ImageController 생성
- 5. 업로드 API 생성
- 6. MultipartHttpServletRequest란?
- 7. 왜 @ResponseBody를 사용할까?
- 8. ImageService 생성
- 9. S3 객체 주입
- 10. application.properties 버킷명 가져오기
- 11. 업로드 메소드 생성
- 12. 업로드 파일 추출
- 13. 원본 파일명 가져오기
- 14. 확장자 추출
- 15. UUID 적용 이유
- 16. UUID 적용
- 17. 로컬 저장 경로 생성
- 18. 로컬 File 객체 생성
- 19. MultipartFile → File 저장
- 20. AWS S3 업로드
- 21. Public Read 권한 설정
- 22. S3 URL 가져오기
- 23. 로컬 파일 삭제
- 24. 최종 URL 반환
- 25. Controller에서 서비스 호출
- 26. CKEditor 응답 형식 만들기
- 27. 최종 반환
- 28. 최종 Controller 전체 코드
- 29. 실행 결과
- 30. S3에서 확인되는 모습
- 31. 실무에서 중요한 개선 포인트
- 32. 현재 구조의 장점
- 33. 이번 단계 핵심 이해 포인트
- 정리
- 핵심 요약
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 형식 반환