❓ Information

  • 정적 파일(이미지)을 백엔드가 URL로 제공해서 프런트에서 표시하는 방법

❗ Relevant data

📦 Information Resources

gpt: 백단에서 이미지 URL 로 서빙 방법 질문

🔰 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 노출 방지, 인증 처리 가능.
  • 단점: 백엔드에 트래픽 부하.

👉 정리하자면, **가장 널리 쓰이는 건

  1. 정적 파일 URL 제공
  2. CDN 경로 반환
  3. 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 response

X-Accel-Redirect는 Nginx가 특별하게 해석하는 응답 헤더

동작 원리:

  1. 클라이언트(React)가 GET /api/file/<file_path>/<file_id>/ 같은 요청을 Django에 보냄.
  2. Django 뷰에서 인증/권한 검사를 함 → 접근 가능 여부를 확인.
  3. Django는 실제 파일 내용을 전송하지 않고, 대신 HTTP 응답 헤더에 넣음
    • 여기서 /protected/ 은 외부에는 노출되지 않는 Nginx 내부 location 경로.
    • 클라이언트는 실제 이 경로를 알지도 못하고, 접근도 못함.
  4. 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가 쓰지는 못하고 읽기만 가능.

한계점

  1. 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>에서도 로그인 세션 유지됨.

  2. JWT를 localStoragesessionStorage에 저장한 경우

    • <img> 요청에는 절대 Authorization 헤더를 붙일 수 없음 → 브라우저가 지원 안 함.
    • 해결 방법은 두 가지:
      1. 서명된 URL 방식: 백엔드에서 /api/file/signed_file/... 요청 시 미리 짧은 TTL이 있는 signed URL을 발급하고, 그걸 <img src="...">에 넣음.
        • AWS S3 Presigned URL 같은 원리
        • Django에서는 DRF 뷰에서 X-Accel-Redirect로 내부 경로를 열어주면 됨
      2. 프록시 엔드포인트: React에서 fetch로 이미지를 가져올 때 Authorization 헤더 붙여서 요청 → blob 변환 → <img src={URL.createObjectURL(blob)} />