[Backend] Image 모듈 개발기 1편: 왜 우리는 직접 이미지 업로드 모듈을 만들었는가?
한번 이렇게 의식의 흐름이 아니라 뭔가 짜여진..? 흐름의 글도 작성해 보려고 한다... 뭐가 좋을지는.. 모르긋당.. ><
이미지 모듈 개발기 1편: 왜 우리는 직접 이미지 업로드 모듈을 만들었는가?
1. 도입
이미지 업로드는 서비스에서 자주 등장하는 기능이다.
그래서 대부분의 프로젝트에서는 Amazon S3, CloudFront, 혹은 CDN과 연결된 파일 서버에 저장하고 URL을 응답하는 식으로 처리한다.
하지만, 단순히 "파일을 저장했다"는 사실만으로는 우리 서비스에 필요한 기능을 제대로 구현할 수 없었다.
- 사용자가 업로드한 이미지가 실제로 게시글에 쓰이지 않으면?
- 관리자가 사업자 등록증 이미지를 검수하고 나서만 확정해야 한다면?
- 이미지가 삭제되었는데도 여전히 서비스에 노출된다면?
이러한 시나리오를 다루기 위해 우리는 이미지 파일을 도메인 모델로 다루는 것을 선택했고,
그 과정에서 단순 파일 저장이 아닌, 상태 관리와 생명주기 기반의 이미지 모듈을 설계하게 되었다.
2. 설계 목표
이번 이미지 모듈의 설계는 단순히 파일을 저장하는 기능이 아니라,
**"이미지가 도메인 객체로서 어떤 상태를 거치고 어떻게 소멸하는가"**에 집중했다.
목표는 다음과 같았다:
- ✅ 파일 업로드 직후
TEMP상태로 저장 - ✅ 실제 사용 확정 시점에
CONFIRMED상태로 변경 - ✅ 사용되지 않은 이미지 자동 삭제 (
@Scheduled) - ✅ 관리자/사용자에 따라 권한 검증 및 유효성 검사 처리
- ✅ 도메인(referenceId) 기준으로 이미지 그룹 관리
- ✅ 썸네일 및 WebP 포맷 변환 지원
- ✅ CDN/nginx 기반 퍼블릭 URL 경로 지원
즉, 이미지는 상태를 가진 일급 도메인 객체로 설계하되,
서비스의 복잡도를 높이지 않고 외부에 의존하지 않는 구조로 구현하는 것을 목표로 했다.
3. 설계 고려사항
설계 초기, 아래와 같은 고려사항들을 검토했다:
1) 상태 기반 모델링
이미지는 단순한 파일이 아니다.
업로드 → 미사용 → 삭제 혹은
업로드 → 게시글에 사용 → 삭제 요청 → 삭제 처리
같은 복잡한 흐름을 갖는다. 이를 명확히 하기 위해 상태 기반 도메인 설계가 필요했다.
TEMP→CONFIRMED→PENDING_DELETE→삭제
2) 도메인과의 느슨한 연결
게시글, 유저, 밴드룸, 사업자 등록증 등 여러 도메인과 연관될 수 있는 이미지는
하드 코딩된 foreign key 관계가 아닌 referenceId 문자열 기반으로 연결되도록 했다.
이 방식은 BFF에서 imageId 리스트를 관리하거나, Kafka 이벤트로 referenceId를 바인딩하기에 적합하다.
3) 퍼블릭 URL 반환 방식
이미지 모듈은 저장 경로만 저장하고, 클라이언트에는 CDN or nginx 경로를 붙여서 제공한다.
4) 유효성 검증 분리
- 사용자 본인이 업로드한 이미지인지
- 이미지가
TEMP상태인지 - 해당
category에 맞는 이미지인지
이런 검증은ImageValidator라는 별도 컴포넌트에서 수행하도록 분리했다.
4. 구현 설명
이번 1편에서는 전체 흐름을 조망하고, 주요 컴포넌트들에 대한 간단한 설명만 포함한다.
| 구성요소 | 책임 설명 |
|---|---|
Image |
Entity. 이미지 메타 정보 및 상태 관리 |
ImageService |
비즈니스 로직 총괄. 업로드, 확정, 삭제, 조회 |
ImageValidator |
유저/상태/category 유효성 검증 담당 |
ImageStatusChanger |
상태 전이 전용 유틸. TEMP → CONFIRMED 등 제한된 흐름만 허용 |
LocalImageStorage |
이미지 저장소 구현체. 현재는 local, 이후 S3로 전환 가능 |
ImageUtil |
WebP 변환 및 썸네일 처리 유틸 클래스 |
5. 테스트 전략
단위 테스트는 책임 중심으로
ImageValidatorTest: 각 유효성 검증 로직이 상황에 맞게 예외를 던지는지ImageStatusChangerTest: 상태 전이가 정확히 동작하는지ImageUrlHelperTest: 퍼블릭 URL 생성이 정확한지
서비스 테스트는 시나리오 중심으로
upload(): 저장 경로, 파일 포맷, DB 저장 여부 확인confirmImages(): 유효성 검사 후 상태 전이 및referenceId업데이트deleteImage():PENDING_DELETE로 상태 전이getImageUrls():CONFIRMED상태 필터링 및 URL 응답 확인
6. 회고 및 확장 가능성
지금까지 구현하면서 배운 점:
- 상태 기반 도메인 설계는 강력한 모델링 도구였다.
- 책임 분리 없이 ImageService에 모든 로직을 넣었다면 테스트가 매우 어려웠을 것
- 이벤트 기반 아키텍처(Kafka 등)와도 쉽게 통합될 수 있도록 구조를 잡아두니 확장에 유리하다
다음 편에서 다룰 내용:
실제로 어떻게
ImageStatus를 다루고,TEMP → CONFIRMED상태로 전이되는지,
썸네일 생성은 어디서 일어나며, 어떻게 도메인과 연동되는지에 대해 다룰 예정이다.