| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 상태관리
- 유플텍플
- LG유플러스 유레카 프론트엔드 대면
- 나눔스퀘어
- streaming metadata
- 미니프로젝트
- sentry
- 프론트엔드
- 멀티캠퍼스부트캠프
- 리액트
- 유레카 프론트엔드 대면
- 부트캠프후기
- 멀티캠퍼스it부트캠프
- 마이배티스
- 이미지 파일 관리
- 웹시큐리티
- React
- 유레카프론트엔드대면
- TypeScript
- LG U+
- input="password"
- 유레카
- 입력처리방식
- jandi
- 핏로그
- 엘지유플러스프론트엔드대면
- 스프링부트
- 타입스크립트
- 유레카프론트엔드
- 유레카 부트캠프 프론트엔드
- Today
- Total
joooii
[74일차] Spring Boot에서 이미지 파일 관리하기 본문

미니 프로젝트를 진행하면서 프로필 이미지 업로드 / 삭제 기능을 구현하게 되었다.
프론트엔드에서는 이미지 파일 관리를 해보았던 경험이 있었으나, Spring Boot에서 이미지 파일을 어떻게 관리하는 것이 적절한지에 대한 정리가 필요했고, 이를 계기로 서버에서의 이미지 관리 방식과 실제 구현까지 정리해 보았다.
서버에서 이미지 파일을 관리하는 일반적인 방식
1️⃣ 클라우드 스토리지 사용 (AWS S3, CloudFlare R2 등)
이미지를 백엔드 또는 DB 단에서 업로드하지 않고, AWS나 CloudeFlare 등 클라우드 스토리지에 업로드하여 DB에 url을 저장하는 방식이다.
이 방식이 가장 일반적인 방식이다.
👍🏻 장점
1. DB에는 url만 저장하므로 부담이 적다.
2. CDN을 통해 빠른 이미지 제공이 가능하다.
3. 트래픽 증가에도 확장성이 뛰어나다.
4. 이미지 리사이징, 포맷 변환 등 최적화 기능 활용이 가능하다.
👎🏻 단점
1. 스토리지 및 트래픽 비용이 발생한다.
2. 초기 구현 난이도가 다소 높다.
2️⃣ Base64 형태로 DB에 직접 저장
이미지를 Base64 문자열로 변환한 뒤 DB에 저장하는 방식이다.
👍🏻 장점
1. 외부 서비스 의존성이 없다.
2. 구현이 단순하고 빠르다.
3. 비용이 발생하지 않는다.
👎🏻 단점
1. Base64 인코딩으로 인해 데이터의 값 자체가 엄청 크기 때문에 처리 비용이 발생한다.
2. 대용량 이미지 처리 시 성능 저하가 발생한다.
3. 이미지를 클라이언트에 제공할 때, 디코딩 과정이 필요하다.
3️⃣ blob 형태로 DB에 저장
DB의 데이터 타입 중 하나인 blob 자료형을 사용하여 파일을 저장하는 방식이다. 이미지를 바이너리 데이터, 즉 이진 데이터 그대로 DB에 저장한다.
👍🏻 장점
1. 인코딩 과정이 없어 원본 크기 그대로 저장할 수 있다.
👎🏻 단점
1. 대용량 데이터를 그대로 저장하기 때문에 DB I/O 성능 저하가 발생하기 쉽다.
2. 백업, 복구 비용이 크다.
4️⃣ 클라이언트가 Cloud Storage에 이미지를 업로드하고 경로를 DB에 저장
클라이언트가 S3, R2 같은 클라우드 스토리지에 이미지를 직접 업로드하고, 서버는 해당 이미지의 url만 DB에 저장하는 방식이다.
👍🏻 장점
1. url만 DB에 저장하기 때문에 DB의 부하가 적다.
2. 서버에 부담이 가지 않으므로 대용량, 다중 파일 업로드에 유용하다.
👎🏻 단점
1. 클라우드 스토리지 및 트래픽에 따른 추가 비용이 발생한다.
2. 클라우드 스토리지의 접근 권한 및 권한 관리를 신중히 적용해야 한다.
우리가 적용한 방법
위와 같이 이미지 파일 관리 방식이 여러 개가 있으나, 그중에서도 이번 미니 프로젝트에서는 2️⃣ Base64 형태로 DB에 직접 저장 방식을 채택했다.
미니 프로젝트는 규모가 작기 때문에 클라우드 스토리지를 사용하기에는 비용 대비 효율이 낮다고 판단하여 2️⃣번 방식이 현실적인 선택이라 판단했다.
Base64 형태로 DB에 직접 저장 방식을 적용하면서도
- 이미지 최대 크기 300 * 300px로 제한
- JPEG 압축 적용
=> 결과적으로 2MB -> 약 30~70KB 수준으로 감소시켜 관리하였다.
Spring Boot에서 파일을 받는 방법
1️⃣ MultipartFile
Spring에서 파일 업로드를 처리하는 표준 방식이다.
@PostMapping("/upload")
public ResponseEntity<?> uploadFile(
@RequestParam("file") MultipartFile file
) {
// 파일 처리
}
2️⃣ application.yml 설정
파일 업로드를 위해 반드시 크기 제한을 설정해야 한다.
설정하지 않으면 기본값 (1MB) 때문에 `MaxUploadSizeExceededException`이 발생할 수 있기 때문이다.
spring:
servlet:
multipart:
enabled: true
max-file-size: 5MB
max-request-size: 5MB
3️⃣ Controller 파라미터 방식 비교
(1) @RequestParam
- 단순 파일 업로드에 적합하다.
(2) @RequestPart
- JSON + 파일을 함께 받을 수 있다.
- Swagger에서 Content-Type 이슈가 자주 발생한다.
(3) @ModelAttribute ✅ 최종 선택
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadFile(
@ModelAttribute RequestDTO request
) {
// request.getFile()
}
선택 이유
- Swagger UI에서 각 필드가 분리되어 보여 테스트가 매우 편하다.
- JSON Blob 처리가 불필요하다.
- Content-Type 문제가 없다.
실제 구현한 구조
Controller
- `@ModelAttribute`를 사용해 텍스트 + 이미지를 동시에 처리한다.
- 인증 정보에서 loginId를 추출한다.

Service – 이미지 처리 로직
이미지 처리 흐름은 아래와 같다:
- 파일 크기 / MIME 타입 검증
- 이미지 로드
- 300×300 비율 유지 리사이징
- JPEG 압축
- Base64 인코딩 후 Data URL 형태로 반환
핵심 포인트
- 이미지 수정 / 삭제 여부를 shouldUpdateImage 플래그로 명확히 구분한다.
- 이미지가 없는 경우에도 기존 이미지를 건드리지 않도록 처리한다.

Mapper.java

MyBatis Mapper.xml
- `profileImage == null` + `shouldUpdateImage == true` → 이미지 삭제
- 다른 필드만 수정할 경우, 이미지 컬럼은 그대로 유지한다.

프론트엔드 구현 – FormData 기반 프로필 수정 처리

- FormData를 사용하여 텍스트와 파일 동시 전송이 가능한 `multipart/form-data` 요청으로 구현했다.
- FormData는 존재하는 값만 서버로 전달하도록 구성하였다.
아래 표와 같이 이미지 업로드, 삭제, 변경 없음 총 3가지로 구분하여 업데이트를 처리하였다.

결과
Swagger 테스트


DB

프론트엔드

회고
항상 프론트엔드만 담당했었기 때문에 이미지 파일을 단순히 보내기만 하면, 백엔드 쪽에서 처리를 해서 다시 반환해 줬기 때문에 구현이 이렇게 어려울 것이라 상상을 하지 못했다.
특히 이미지 파일을 저장하는 방식에서 처음에는 AWS S3를 사용하려고 이미지 업로드 / 삭제 기능을 구현했지만, 후반부로 다가갈수록 시간이 매우 촉박하기도 하고 미니 프로젝트인지라 대용량 파일이 없어서 Base64 방식을 채택했는데 그것이 조금 아쉽게 다가오기도 했다.
그래도 이러한 시행착오 덕분에 다음 종합 프로젝트에서는 비용을 재고 따져서 프로젝트에 적합한 이미지 파일 관리 방법을 채택할 수 있을 것 같다는 생각이 들었다!
새삼 같이 프로젝트했었던 백엔드 팀원분들께 정말 너무너무 감사하고 감사하다..

참고자료
백엔드에서 이미지 업로드는 어떻게 하면 좋을까?
시험 기간으로 밀려서 마저 올리지 못했던 프로젝트에서 발생한 궁금증과 그 해결 시간이다.이번에 프로젝트하면서 단일 이미지 업로드, 다중 이미지 업로드를 처리하는데 많은 시간과 어려움
seungyong20.tistory.com
'TIL' 카테고리의 다른 글
| [85일차] 애자일과 JIRA (0) | 2026.01.06 |
|---|---|
| [79일차] UML와 종류 (0) | 2025.12.23 |
| [65일차] 웹 시큐리티 (0) | 2025.12.08 |
| [64일차] JWT (0) | 2025.12.02 |
| [58일차] Zustand (0) | 2025.11.24 |