2024.11.10 - [프로젝트] - 안전한 파일 업로드에 대한 처리란 무엇일까? chapter 1 에서는 대용량 파일 크기 제한에
대해서 고려해보았다.
이번 포스팅에서는 파일 업로드시
고민 할 수 있고 발생할 수 있는 Trade-off에 대해서
이야기 해보고자 한다.
실제로 내가 겪었던 문제들과
해결 방안이다.

발생했던 문제
일단 현재 발생했던 문제의 로직과 구조를 알아보면
음악 파일과 이미지 파일을 네이버 클라우드의 Object Storage에 저장한다.
로직의 순서가
TrackService 의 업로드 로직이다
음악 파일 저장 -> 이미지 파일 저장 -> Track 객체 저장 순서로 로직이 구성되어져 있다.

각 단계에서 발생할 수 있는 Case 들을 나눠보자!!
Case 1: 음악 파일 저장 시 예외 발생
음악 파일을 네이버 클라우드 Object Storage에 저장하는 과정에서 문제가 발생한 경우,
예를 들어 네트워크 오류나 권한 오류가 발생할 수 있다.
이 경우, 예외가 발생하면 음악 파일 저장이 이루어지지 않으므로 후속 로직인 이미지 파일 저장 및 Track 객체 저장은 실행되지 않으며,
시스템은 정상적으로 예외를 처리할 수 있다.
따라서 이 경우의 처리 방식은 후속 작업은 진행되지 않기 때문에
예외를 로깅하거나 사용자에게 적절한 오류 메시지를 반환하는 것으로
충분한 해결 방안이라고 생각한다.
Case 2: 이미지 파일 저장 시 예외 발생
문제는 이미지 파일 저장에서 발생한 예외로 인해 음악 파일이 외부 스토리지에 저장된 상태로 남게 된다.
이 경우 DB에는 트랙 객체가 저장되지 않지만, 음악 파일은 외부 스토리지에 남아 있는 상태가 된다.
이런 상황에서는 파일과 데이터 간의 일관성이 깨질 수 있다.
트랙 객체와 관련된 이미지 파일을 저장하는 과정에서 문제가 발생했지만,
이미 음악 파일은 외부 스토리지에 저장된 상태이기 때문에, 음악 파일은 실제로 트랙과 연결되지 않은 채로 외부 스토리지에 남게 된다.
Case 3: 트랙 객체 저장 시 예외 발생
트랙 객체를 DB에 저장하는 과정에서 예외가 발생할 수 있는 경우이다.
예를 들어, 트랜잭션이 DB 관련 예외를 처리하는 과정에서 문제가 발생할 수 있다.
이 때, 중요한 점은 트랜잭션 롤백이 DB 작업에만 적용된다는 것!!!
즉, 음악 파일과 이미지 파일은 이미 외부 스토리지에 저장된 상태로 남게 된다.
이 경우, 트랙 객체가 DB에 저장되지 않은 반면,
외부 스토리지에는 음악 파일과 이미지 파일이 저장된 상태가 되어,
파일과 데이터 간의 일관성 에 문제가 발생할 수 있습니다.
예를 들어, DB에 트랙 정보는 없지만,
외부 스토리지에는 파일이 존재하는 상황이 된다.
이로 인해 파일과 데이터 간의 불일치가 발생할 수 있다.
고려했던 해결방안
1. 파일 업로드 중 실패 시 외부 스토리지에 남은 파일 정리하기
파일 업로드 중 예외가 발생하거나 DB와 외부 스토리지 간의 불일치가 발생할 경우,
실시간으로 외부 스토리지에서 파일을 삭제하는 대신
주기적으로 잔여 파일을 정리하는 배치 프로그램 방식
일정 주기로 배치 작업을 실행하여 외부 스토리지에 남아있는 불필요한 파일들을 자동으로 삭제한다!!!
로직 절차
- DB와 외부 스토리지에 저장된 파일들 간의 일치 여부를 확인
- DB에 저장된 파일 정보와 외부 스토리지에 실제로 존재하는 파일을 비교
- DB에 없는 파일들이 외부 스토리지에 남아 있으면 이를 삭제한다.
- 이렇게 하면 예외 상황이나 DB와의 불일치로 인해 외부 스토리지에 불필요한 파일이 계속 쌓이는 것을 방지
추가 고려사항
1번 방법과 2번 방법 모두 좋은 방법이라고 생각 했다.
1번 방법은 실시간으로 데이터의 일관성을 맞춰줄 수 있는 작업이라고 생각이 들었고,
2번 방법은 스케줄러를 통한 주기적인 검사를 통해 데이터의 일관성을 맞춰줄 수 있는 작업이라고 생각이 들었다.
결정
결론부터 이야기하자면 나는 1번 방법을 선택했다..
그 이유는 구조적으로 간단하고 명확하며
실시간으로 문제를 해결할 수 있다는 점에서 효율적이라는 생각이 들었다.
하나의 작업 흐름 내에서 모든 처리가 이루어지기 때문에
별도의 추가 프로세스를 만들 필요가 없다.
해결 코드 작성

코드에서는 fileService.uploadFile 메서드 호출 시 반환된 경로를 변수에 저장하고,
예외 발생 시 deleteIfFileUploaded 메서드를 호출하여 해당 경로에 남아 있는 파일을 삭제한다.
이를 통해 예외가 발생해도 외부 스토리지에 불필요한 파일이 남지 않도록 처리했다.

정상 반환된 (즉 외부 스토리지에 정상적으로 저장이 된 파일만 삭제)
만들고나서 고민해봤던 것
결국에는 1번 방법도 네트워크 문제나 외부 스토리지 호출 문제 등 파일 삭제가 실패할 가능성이 있다.
즉 한계가 존재한다고 생각이 들었다.
그렇게 되면 사용하지 않는 더미 데이터들이 외부 스토리지에 쌓이게 된다 결국에는 똑같지 않은가?
솔직히 이 데이터 정합성에 대해서 100%를 보장하는 방법이 가능한지는 아직도 고민중이고 잘 모르겠다 ㅠㅠ
개발자의 역할은 모든 상황에서 완벽한 정답을 찾는 것이 아니라,
주어진 환경과 제약 속에서 가장 합리적이고 현실적인 최적의 해답을
도출하는 데 있다고 생각한다.

즉시 삭제를 기본으로 처리하는 방식과
주기적인 배치 작업으로 보완하는 방식을 함께 사용한다면
조금이나마 줄일 수 있지 않을까? 라는 고민을 해보았다..
현재 내 프로젝트 규모를 보면 두 가지 방법을 같이 쓰는 건 관리하기 복잡해지고,
오히려 시스템에 부담만 될 수 있다 생각이 들었다.
그래서 그냥 즉시 삭제 방식 하나만 쓰는 게 더 깔끔하고 효율적일 거라고 판단하고
현재 상황에서는 하나로 해결하는 게 낫다고 생각이 들었다.
추후 규모가 커지면 배치 프로그램을 보완하는 것이 더 좋은 방식이라고 생각이 들었다!
'프로젝트' 카테고리의 다른 글
| Docker 도입하여 개발 환경을 셋팅해보자 (0) | 2025.12.22 |
|---|---|
| 좋아요 버튼의 숨겨진 딜레마: DB Lock 관련 (1) | 2024.12.02 |
| 좋아요 버튼의 숨겨진 딜레마: @Transactional과 synchronized가 함께 풀지 못한 동시성 이슈 (0) | 2024.11.26 |
| 동적 쿼리와 배치 사이즈 최적화로 1+N 문제 해결 및 성능 개선 (0) | 2024.11.23 |
| Offset 페이징에서 효율적인 대댓글 처리 방법 (0) | 2024.11.23 |