❓ Information
- 정적 파일(이미지)을 백엔드가 URL로 제공해서 프런트에서 표시하는 방법
❗ Relevant data
📦 Information Resources
🔰 Content ->
1. 정적 파일(static files) 경로 제공
- 방법: 서버에 이미지를 정적 파일로 두고,
/static/...혹은/media/...같은 URL을 응답에 포함. - 프론트:
<img src={imageUrl} />형태로 그대로 사용. - 장점: 단순, 빠르게 적용 가능.
- 단점: 서버 부하 가능성, CDN 최적화 필요.
2. CDN(URL 변환) 활용
- 방법: 백엔드에서 이미지 파일 경로를 CDN 주소(
https://cdn.example.com/...)로 변환해서 반환. - 프론트: 백엔드 응답에서 받은 CDN URL을
<img src={...}>에 바인딩. - 장점: 로딩 속도 빠름, 캐싱·트래픽 관리 가능.
- 단점: CDN 설정 비용/작업 필요.
3. Signed URL (Pre-signed URL, 인증된 URL)
- 방법: AWS S3, GCP, Azure 같은 스토리지에 저장 후, 접근 가능한 pre-signed URL을 백엔드에서 발급.
- 프론트: 응답에서 받은 URL을
<img>태그에 넣으면 브라우저가 직접 스토리지에서 다운로드. - 장점: 인증·보안 관리에 유리, 서버 부하 감소.
- 단점: URL 만료 시간이 있어서 재발급 로직 필요.
4. Base64 인코딩된 이미지 전달
- 방법: 백엔드가 이미지를 Base64로 변환해 JSON으로 전달 (
data:image/png;base64,...). - 프론트:
<img src="data:image/png;base64,...">로 표시. - 장점: 별도 파일 경로 필요 없음.
- 단점: 문자열 크기가 크고, 캐싱·성능에 불리해서 일반적으로 작은 아이콘 등에만 사용.
5. 이미지 프록시(백엔드 → 프런트)
- 방법: 이미지가 외부 URL에 있을 때, 백엔드에서 프록시 역할을 하여
/api/image?id=xxx같은 경로 제공. - 프론트: API 엔드포인트를 그대로
<img src>에 사용. - 장점: 외부 URL 노출 방지, 인증 처리 가능.
- 단점: 백엔드에 트래픽 부하.
👉 정리하자면, **가장 널리 쓰이는 건
- 정적 파일 URL 제공
- CDN 경로 반환
- S3 Presigned URL 발급
이고, 나머지는 특수 케이스에서 선택적으로 사용합니다.
Signed URL을 Django에서 구현 하는법
Nginx X-Accel-Redirect 방식
1. Django는 파일을 직접 읽지 않고 “이 파일은 유효하다”는 신호만 응답 헤더에 넣음.
from django.urls import path
from .views import *
urlpatterns = [
path("signed_file/<int:file_path>/<str:filename>", signed_file, name="signed_file"),
]from django.http import HttpResponse
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def signed_file(request, file_path, filename):
response = HttpResponse()
response["X-Accel-Redirect"] = f"/protected/{file_path}/{filename}"
return responseX-Accel-Redirect는 Nginx가 특별하게 해석하는 응답 헤더
동작 원리:
- 클라이언트(React)가
GET /api/file/<file_path>/<file_id>/같은 요청을 Django에 보냄. - Django 뷰에서 인증/권한 검사를 함 → 접근 가능 여부를 확인.
- Django는 실제 파일 내용을 전송하지 않고, 대신 HTTP 응답 헤더에 넣음
- 여기서
/protected/은 외부에는 노출되지 않는 Nginx 내부 location 경로. - 클라이언트는 실제 이 경로를 알지도 못하고, 접근도 못함.
- 여기서
- Nginx가 이 응답을 받으면
X-Accel-Redirect헤더를 보고 “아, 이 요청은 내가 대신/protected/{file_path}/{filename}파일을 전송해야 하는 거구나” 하고 직접 디스크에서 파일을 읽어 전송함. 즉, Nginx는 Django의 응답 바디는 무시하고, 대신 파일을 클라이언트로 스트리밍.
2. Nginx 설정:
location /protected/ {
internal;
alias /app/media/; # 실제 호스트/컨테이너 파일 경로
}internal;: 외부에서 직접/protected/...경로를 호출할 수 없게 막음alias: 실제 파일이 저장된 디렉토리 연결
👉 이렇게 하면 Django는 인증/검증만 하고, 실제 파일 읽기와 전송은 Nginx가 처리.
Nginx는 정적 파일 서빙에 최적화되어 있어서 부하를 훨씬 잘 버팀.
3. Nginx 컨테이너에서 파일 접근
Nginx 컨테이너가 직접 파일을 읽으려면 파일이 들어있는 디렉토리를 Nginx 컨테이너에 마운트해야 함.
# docker-compose.yml
services:
nginx:
image: nginx:latest
volumes:
- ./media:/app/media:ro # 호스트 media 디렉토리 → 컨테이너 /app/media
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80":ro 옵션을 주면 read-only 마운트라서 Nginx가 쓰지는 못하고 읽기만 가능.
한계점
-
JWT를
쿠키(HttpOnly, SameSite, Secure)에 저장한 경우<img>요청에도 자동으로 쿠키가 붙음.- 다만 이 경우, CORS 설정과 쿠키 전송 허용이 필요.
백엔드 Django DRF + Nginx 환경이라면:
- Django:
SESSION_COOKIE_SAMESITE=None,SESSION_COOKIE_SECURE=True설정 - Nginx:
proxy_set_header Cookie $http_cookie;넣어주기 - React 프론트엔드:
<img src="...">는 별도 옵션이 없으니, 쿠키가 자동으로 포함됨
👉 이렇게 하면
<img>에서도 로그인 세션 유지됨. -
JWT를
localStorage나sessionStorage에 저장한 경우<img>요청에는 절대 Authorization 헤더를 붙일 수 없음 → 브라우저가 지원 안 함.- 해결 방법은 두 가지:
- 서명된 URL 방식: 백엔드에서
/api/file/signed_file/...요청 시 미리 짧은 TTL이 있는 signed URL을 발급하고, 그걸<img src="...">에 넣음.- AWS S3 Presigned URL 같은 원리
- Django에서는 DRF 뷰에서 X-Accel-Redirect로 내부 경로를 열어주면 됨
- 프록시 엔드포인트: React에서
fetch로 이미지를 가져올 때 Authorization 헤더 붙여서 요청 → blob 변환 →<img src={URL.createObjectURL(blob)} />
- 서명된 URL 방식: 백엔드에서