SWLUG(2026)/유지보수

[SV 2주차] 이미지 로드 실패

batterygj 2026. 7. 8. 00:59

 

문제 : 이미지 로드 실패 

원인 : 서버의 무상태성(Stateless)으로 인한 파일 휘발

 


 

1. 이미지가 어떻게 로드되고 표시되는지, 현재 이미지 로드에 실패한 이유는 무엇인지 알기위해 

BlogController -> BlogService를 봄

 

- BlogController.java : 프론트엔드(웹사이트 화면)에서 사진을 보냈을 때, 백엔드 애플리케이션의 어느 문(엔드포인트)을 열고 들어오는지 확인

  •  @PostMapping("/upload-image")라는 문 확인
  • 들어온 파일이 blogService.saveImage(file)라는 로직으로 넘겨진다는것 확인

 

-BlogService.java

  • 컨트롤러가 넘겨준 파일을 가지고 실제 무슨 비즈니스 로직을 하는지 알기 위해

이미지 저장 로직

Path destinationFile = uploadPath.resolve(newFilename);
Files.copy(file.getInputStream(), destinationFile, ...);
  1. 사용자가 블로그에 이미지를 올리면, 스프링 부트 서버는 uploadPath라는 물리적인 디렉터리 경로를 잡음
  2. Files.copy 명령어를 통해 그 폴더 안에 실제 파일(예: image_123.png)을 복사해서 저장
  3. DB나 브라우저에는 "/api/blog/images/image_123.png"라는 주소를 넘김. 브라우저가 이 주소로 이미지를 보여달라고 요청하면, 서버는 내 하드디스크에 저장된 파일을 읽어서 보여주는 구조.

+) 내 컴퓨터에서 실행할 때는 프로젝트 폴더가 그대로 유지되니 서버를 껐다 켜도 사진이 잘 남아있었던 것

 


현재 코드는 "서버 컴퓨터가 절대 안 바뀌고 영원히 켜져 있을 것"을 가정하고 짠 코드. 하지만 실제 배포 환경은 주기적으로 서버 컴퓨터가 교체되고 초기화되기 때문에 파일이 날아갈 수밖에 없음.

이 문제를 해결하려면 서버 가상 컴퓨터 내부 디스크가 아니라, 서버가 재시작되어도 지워지지 않는 인터넷상의 독립된 저장소(오브젝트 스토리지)로 파일이 저장되게끔 코드를 수정해야 함


해결방안 

:외부 object storage도입

 

후보

1. Cloudflare R2 (이게 나음!)

- 매달 용량 10GB무료 / 쓰기 요청 100만건 / 읽기 요청 1000만건 무료 / 트래픽비용 없음

- S3 호환 : AWS의 AmazonS3를 말함 (업계 표준 규격) 

-> 백엔드 코드는 AmazonS3Client라는 표준 라이브러리로 딱 한 번만 짜두고,

다른 S3호환 서비스를 사용하려면 코드 수정없이 설정파일(yml)의 주소(endpoint)와 인증키만 그 서비스용으로 바꾸면 됨

 

2.Cloudinary

- AI기반 API를 제공하여 이미지 및 비디오 관리, 변환, 최적화된 대규모 전송 자동화

- 사용자 3명, 계정1개는 월25크레딧(약 25GB) 무료 (저장용량+다운로드 트래픽+이미지변환 다 합쳐서 차감됨)

- 이미지 변환을 “URL 문자열 조합”으로 처리

https://burn.eone.one/posts/2026/cloudinary-overview/?lang=ko

- 독자적 코드를 사용해야함 -> 다른 스토리지로 이동하려면 백엔드 코드를 통째로 엎어야함