파일과 이미지는 데이터베이스에 저장하면 안 되나요?
이미지·영상·PDF 같은 파일 자체는 보통 파일 스토리지에 두고, 데이터베이스에는 파일 경로·소유자·형식·공개 여부를 저장해요. 공개 파일과 비공개 파일의 읽기·업로드·삭제 권한을 따로 설계하고 두 저장소가 어긋나는 실패도 처리해야 합니다.
핵심 요약
- 파일 스토리지는 큰 파일을 보관·전달하고 데이터베이스는 파일의 경로와 서비스 정보를 관리합니다.
- 공개 버킷에는 민감한 자료를 두지 말고 비공개 파일은 권한 검사나 짧게 유효한 서명 URL로 전달합니다.
- 고유한 경로와 실제 파일 형식을 검사하고 업로드·DB 저장·삭제 중 일부만 성공하는 상황을 처리합니다.
프로필 이미지, 첨부 문서와 강의 영상을 올리는 기능을 만들다 보면 이런 질문이 생깁니다.
사용자 정보는 데이터베이스에 저장했는데
이미지 파일도 같은 곳에 넣으면 안 되나요?
기술적으로는 파일의 내용을 데이터베이스에 넣을 수 있습니다. 하지만 일반적인 웹 애플리케이션에서는 역할을 나누는 편이 관리하기 쉽습니다.
파일 스토리지
→ 이미지·영상·PDF 같은 파일 자체
데이터베이스
→ 파일 이름·경로·소유자·공개 여부 같은 정보
왜 나누는지, 업로드 기능을 만들 때 무엇을 확인해야 하는지 살펴볼게요.
데이터베이스는 구조가 있는 정보를 다루기 좋아요
예약 정보를 예로 들어볼게요.
| 예약 ID | 사용자 ID | 날짜 | 상태 |
|---|---|---|---|
| 101 | user-a | 2026-08-10 | confirmed |
| 102 | user-b | 2026-08-11 | canceled |
각 행은 같은 열을 가지고, 날짜나 상태를 기준으로 검색하고 정렬할 수 있습니다. 다른 사용자와 결제 기록을 ID로 연결하기도 쉬워요.
데이터베이스는 이런 질문에 빠르게 답하도록 만들어진 도구입니다.
- 이 사용자의 예약은 몇 개인가요?
- 결제 완료된 주문만 보여주세요.
- 오늘 업로드된 문서를 찾아주세요.
- 이 게시글의 대표 이미지는 무엇인가요?
반면 이미지와 영상은 수많은 바이트로 이루어진 큰 덩어리입니다. 파일 내용 안에서 사용자 ID나 공개 상태를 검색하는 것이 주된 목적은 아니에요.
앱의 데이터가 어디에 저장되는지와 데이터베이스가 스프레드시트와 어떻게 다른지를 먼저 읽으면 이 구분을 이해하기 쉽습니다.
파일 스토리지는 큰 파일을 보관하고 전달해요
파일 스토리지는 이미지, 오디오, 영상과 문서를 저장하고 필요할 때 내려주는 서비스입니다. 객체 스토리지라고 부르기도 해요.
파일마다 다음과 같은 고유한 경로를 가집니다.
avatars/user-a/profile.webp
applications/order-101/document.pdf
courses/course-7/lesson-3.mp4
웹 애플리케이션은 이 경로를 이용해 파일을 업로드하거나 내려받습니다. 파일 스토리지는 대용량 전송, 캐시, 업로드 제한과 접근 제어처럼 파일에 필요한 기능을 제공합니다.
Supabase Storage, Amazon S3, Cloudflare R2와 Vercel Blob 등이 이 역할을 할 수 있습니다. Supabase Storage도 일반 파일을 위한 버킷과 접근 제어, CDN과 대용량 업로드 방식을 제공합니다. (Supabase Storage 공식 문서 (새 탭에서 열림))
어떤 서비스를 선택하더라도 기본 구조는 비슷합니다.
브라우저
→ 파일 업로드
→ 파일 스토리지에 실제 파일 저장
→ 파일 경로와 정보를 데이터베이스에 저장
데이터베이스에는 파일의 정보를 저장해요
파일 자체를 스토리지에 두더라도 서비스에서 그 파일을 찾고 관리할 정보는 데이터베이스에 필요합니다.
예를 들어 첨부 파일 테이블에는 이런 값을 저장할 수 있어요.
| 항목 | 의미 |
|---|---|
| id | 서비스 안에서 파일을 구분하는 ID |
| ownerId | 파일을 올린 사용자 |
| storageKey | 스토리지 안의 경로 |
| originalName | 사용자가 올린 원래 파일명 |
| contentType | 이미지, PDF 같은 파일 형식 |
| size | 파일 크기 |
| visibility | 공개 또는 비공개 |
| createdAt | 업로드 시각 |
게시글이나 주문은 파일의 id를 연결합니다.
게시글
→ 대표 이미지 ID
파일 정보
→ 스토리지 경로
파일 스토리지
→ 실제 이미지
전체 URL보다 스토리지의 키나 경로를 저장하면 도메인과 전달 방식이 바뀌어도 대응하기 쉬울 수 있습니다. 다만 사용하는 서비스의 방식에 따라 적절한 값을 선택해야 해요.
파일을 데이터베이스에 넣으면 왜 불편할까요?
이미지의 바이트를 데이터베이스 열에 직접 저장하는 방식이 항상 틀린 것은 아닙니다. 작은 파일을 하나의 트랜잭션으로 강하게 묶어야 하거나 별도 스토리지를 운영하기 어려운 특수한 경우에는 선택할 수 있어요.
하지만 일반적인 웹서비스의 업로드 파일에는 다음 부담이 생깁니다.
- 데이터베이스 크기와 백업이 빠르게 커집니다.
- 목록을 조회하는 데이터와 큰 파일 전송이 같은 자원을 사용합니다.
- 이미지와 영상을 CDN으로 전달하고 변환하기 불편합니다.
- 파일만 별도로 이동하거나 수명 주기를 관리하기 어렵습니다.
- 큰 파일 업로드가 데이터베이스 연결을 오래 차지할 수 있습니다.
반대로 파일 스토리지는 큰 객체를 저장하고 여러 지역에서 전달하는 일에 맞게 설계되어 있습니다.
그래서 보통은 역할을 나눕니다.
데이터베이스
→ 파일에 대한 질문에 답함
파일 스토리지
→ 실제 파일을 보관하고 전달함
공개 파일과 비공개 파일을 먼저 나눠요
파일 업로드 기능에서 가장 중요한 결정입니다.
공개 파일
주소를 아는 누구나 볼 수 있는 파일입니다.
- 블로그 대표 이미지
- 공개 프로필 사진
- 상품 이미지
- 홈페이지에 사용하는 자료
공개 파일은 CDN으로 빠르게 전달하기 좋습니다. 대신 주소가 유출되어도 문제가 없는 내용만 넣어야 해요.
비공개 파일
로그인과 권한 확인을 거친 사람만 볼 수 있는 파일입니다.
- 계약서와 신청 서류
- 구매자 전용 자료
- 개인이 올린 비공개 문서
- 공개 전 영상
- 운영자만 확인할 첨부 파일
비공개 파일은 파일 URL을 데이터베이스에 넣어두는 것만으로 보호되지 않습니다. 파일을 내려받을 때마다 사용자의 권한을 검사하거나 짧은 시간만 유효한 서명된 URL을 발급해야 합니다.
Supabase Storage의 비공개 버킷은 다운로드에도 접근 정책을 적용하고, 제한된 시간 동안 사용할 수 있는 서명 URL을 만들 수 있습니다. 공개 버킷은 파일을 가져올 때 접근 제어를 우회하므로 URL을 가진 누구나 볼 수 있습니다. (Supabase 버킷 접근 방식 (새 탭에서 열림))
“로그인 화면 뒤에 링크를 숨겼으니 비공개”라고 판단하면 안 됩니다. 파일 주소를 복사해 로그아웃 상태나 다른 브라우저에서 열어보세요.
업로드 권한과 다운로드 권한은 따로 확인해요
사용자가 파일을 볼 수 있다고 새 파일을 올리거나 기존 파일을 지워도 되는 것은 아닙니다.
파일 작업마다 권한이 다를 수 있어요.
읽기
업로드
교체
삭제
목록 조회
사용자 A는 자신의 문서를 보고 교체할 수 있지만 사용자 B의 문서는 볼 수 없어야 합니다. 일반 사용자는 공개 이미지 파일을 볼 수 있어도 상품 이미지를 새로 올릴 수는 없어야 해요.
Supabase Storage를 브라우저에서 직접 사용한다면 storage.objects에 RLS 정책을 적용해 작업별 권한을 제한할 수 있습니다. 서버에서만 스토리지를 사용한다면 서버가 로그인 사용자와 파일 소유자를 확인해야 합니다. 서비스 키는 접근 제어를 우회할 수 있으므로 브라우저에 노출해서는 안 됩니다. (Supabase Storage 접근 제어 (새 탭에서 열림))
파일 이름을 그대로 경로로 사용하지 마세요
두 사용자가 모두 profile.jpg를 올릴 수 있습니다. 원래 파일명을 스토리지의 유일한 경로로 사용하면 먼저 올린 파일을 덮어쓸 수 있어요.
파일 경로에는 서비스가 만든 고유한 값을 사용하는 편이 안전합니다.
나쁜 예
uploads/profile.jpg
구분 가능한 예
users/user-a/550e8400-e29b-41d4-a716-446655440000.webp
원래 파일명은 사용자에게 보여주기 위한 정보로 데이터베이스에 따로 저장할 수 있습니다.
경로에 이메일, 주민등록번호와 같은 개인정보를 넣지 마세요. 공개 파일의 URL이나 로그를 통해 노출될 수 있습니다.
확장자만 믿지 말고 파일을 검사해요
photo.jpg라는 이름만 보고 안전한 이미지라고 판단할 수 없습니다. 파일명과 확장자는 사용자가 바꿀 수 있어요.
업로드할 때 다음을 확인해야 합니다.
- 허용할 파일 종류
- 실제 콘텐츠 형식
- 최대 파일 크기
- 이미지의 가로·세로 크기
- 사용자 한 명이 올릴 수 있는 수량
- 파일명과 메타데이터 처리
- 악성 파일 검사 필요 여부
이미지만 필요한 기능이라면 PDF, 실행 파일과 HTML 업로드를 허용하지 마세요. SVG는 이미지이지만 안에 스크립트나 외부 내용을 포함할 수 있어 서비스 방식에 따라 별도 검토가 필요합니다.
브라우저에서 선택 가능한 형식을 제한하는 것은 사용자 편의를 위한 첫 단계입니다. 서버나 스토리지 설정에서도 크기와 형식을 다시 확인해야 합니다.
큰 파일은 서버를 거치지 않고 올릴 수 있어요
작은 프로필 이미지는 브라우저가 내 서버로 보내고 서버가 스토리지에 저장해도 충분할 수 있습니다.
하지만 큰 영상이 모두 내 애플리케이션 서버를 통과하면 업로드 시간 동안 서버 자원을 오래 사용하고 요청 크기 제한에 걸릴 수 있어요.
이때는 서버가 업로드 권한과 경로를 확인한 뒤 짧게 유효한 업로드 URL을 발급하고, 브라우저가 파일을 스토리지로 직접 보내는 방식을 사용할 수 있습니다.
브라우저가 업로드 요청
→ 서버가 사용자·파일 조건 확인
→ 제한된 업로드 URL 발급
→ 브라우저가 스토리지로 직접 업로드
→ 완료 결과를 서버에 등록
영상처럼 처리 시간이 긴 파일은 업로드 직후 바로 재생할 수 없을 수도 있습니다. 변환 서비스가 준비 완료 이벤트를 보내면 그때 데이터베이스 상태를 바꾸는 흐름이 필요해요.
처음부터 이 구조가 항상 필요한 것은 아닙니다. 파일 크기와 서비스의 사용량에 맞춰 선택하세요.
파일과 데이터베이스가 서로 어긋날 수 있어요
파일 스토리지와 데이터베이스는 서로 다른 시스템이므로 한쪽만 성공할 수 있습니다.
파일 업로드 성공
→ DB 저장 실패
→ 주인을 찾을 수 없는 파일이 남음
DB 기록 생성
→ 파일 업로드 실패
→ 열리지 않는 첨부 정보가 남음
이 상황을 고려해 상태를 나누고 정리 방법을 준비해야 합니다.
- 업로드 전
pending기록을 만들고 완료 뒤ready로 바꿉니다. - DB 저장이 실패하면 방금 올린 파일을 삭제합니다.
- 오래된 미완료 파일을 정기적으로 정리합니다.
- 파일을 교체할 때 새 파일을 먼저 확인한 뒤 이전 파일을 지웁니다.
- 삭제 실패를 로그로 남겨 나중에 다시 처리합니다.
사용자가 버튼을 두 번 누르거나 업로드 중 브라우저를 닫는 경우도 함께 테스트하세요.
삭제와 백업의 범위도 따로 생각해요
데이터베이스를 백업했다고 업로드 파일까지 모두 백업된 것은 아닐 수 있습니다.
Supabase도 파일의 메타데이터와 실제 객체가 별도로 존재한다고 안내합니다. 완전한 복구를 준비하려면 데이터베이스 기록과 파일 객체의 백업 범위를 모두 확인해야 해요. (Supabase 파일 다운로드와 메타데이터 (새 탭에서 열림))
삭제할 때도 질문이 필요합니다.
- 게시글을 지우면 대표 이미지도 바로 지우나요?
- 같은 파일을 여러 곳에서 사용하고 있나요?
- 사용자가 탈퇴하면 업로드한 공개 콘텐츠는 어떻게 하나요?
- 법적·운영상 보관해야 할 문서가 있나요?
- 실수로 삭제했을 때 복구할 기간이 있나요?
데이터베이스의 행이 사라질 때 무조건 같은 경로의 파일을 삭제하는 간단한 규칙은 공유 파일이나 보관 의무가 있는 문서에서 문제가 될 수 있습니다.
AI에게 저장 구조부터 설계시켜요
이 웹 애플리케이션에 파일 업로드 기능을 추가하려고 해.
아직 코드를 수정하지 말고 저장 구조와 위험을 먼저 정리해줘.
1. 업로드할 파일 종류와 최대 크기
2. 공개 파일과 비공개 파일의 구분
3. 누가 읽기·업로드·교체·삭제할 수 있는지
4. 실제 파일을 저장할 스토리지
5. 데이터베이스에 저장할 파일 정보
6. 충돌과 개인정보 노출이 없는 파일 경로 규칙
7. 서버를 거칠지 직접 업로드 URL을 사용할지
8. 업로드 성공 후 DB 저장이 실패했을 때 정리 방법
9. 교체·삭제·탈퇴 때 파일을 처리하는 규칙
10. 데이터베이스와 파일을 함께 백업·복구하는 방법
현재 프로젝트가 Supabase Data API와 RLS를 사용하는지,
서버에서 스토리지와 DB에 접근하는지도 확인해줘.
서비스 키를 브라우저에 노출하지 말고,
확인하지 못한 보안 설정은 추측하지 마.
출시 전 체크리스트
- 파일 자체와 파일 정보를 어디에 저장할지 구분했습니다.
- 공개되어도 되는 파일과 로그인이 필요한 파일을 나눴습니다.
- 비공개 파일 URL을 로그아웃 상태와 다른 사용자로 확인했습니다.
- 읽기·업로드·교체·삭제 권한을 각각 검사합니다.
- 서비스 키와 비밀값이 브라우저 코드에 없습니다.
- 파일 경로에 고유한 값이 있고 개인정보가 들어가지 않습니다.
- 확장자뿐 아니라 형식, 크기와 수량을 제한합니다.
- 너무 큰 파일이 애플리케이션 서버 제한에 걸리지 않습니다.
- 중복 업로드와 중간 취소를 처리합니다.
- 파일과 DB 중 한쪽만 성공했을 때 정리할 수 있습니다.
- 파일 교체가 성공하기 전에 이전 파일을 지우지 않습니다.
- 데이터베이스와 실제 파일의 백업 범위를 모두 확인했습니다.
정리
- 데이터베이스는 검색하고 연결할 구조화된 정보를 저장하는 데 적합합니다.
- 파일 스토리지는 이미지, 영상과 문서 같은 큰 파일을 보관하고 전달하는 역할을 맡습니다.
- 보통 실제 파일은 스토리지에, 경로·소유자·형식·공개 여부는 데이터베이스에 저장합니다.
- 공개 버킷의 파일은 URL을 가진 누구나 볼 수 있으므로 민감한 자료를 넣으면 안 됩니다.
- 비공개 파일은 다운로드할 때 권한을 확인하거나 짧게 유효한 서명 URL을 사용합니다.
- 업로드, 읽기, 교체와 삭제는 서로 다른 권한입니다.
- 파일명과 확장자를 그대로 믿지 말고 고유 경로, 크기와 실제 형식을 검사하세요.
- 스토리지와 데이터베이스 중 한쪽만 성공하는 상황과 백업 범위를 준비해야 합니다.
파일 업로드는 “파일 선택 버튼을 붙이는 기능”이 아닙니다. 누가 어떤 파일을 올리고 볼 수 있는지 정하고, 실제 파일과 서비스 정보를 끝까지 일치시키는 기능입니다.
인스타그램 @ddukddak.build · 페이스북 뚝딱







