본문으로 바로가기

static과 media는 다릅니다. media는 파일을 업로드받은 뒤 해당 파일을 서빙하는 것이고 static은 소스 코드 내에 존재하는 파일을 서빙하는 것입니다.

아래 실습에서는 따로 S3와 같은 스토리지에 저장하지는 않고 일단 소스 코드 내에 저장하는 방식으로 진행했습니다만 실제 배포시에는 다른 스토리지에 업로드한 파일을 올린 후 해당 URL을 불러오는 방식으로 사용해야 합니다.

 

static을 사용하는 방법은 github에 정리해두었습니다.

github.com/DarrenKwonDev/django-study#static-%ED%8C%8C%EC%9D%BC-%EC%84%9C%EB%B9%99%ED%95%98%EA%B8%B0

 

DarrenKwonDev/django-study

Contribute to DarrenKwonDev/django-study development by creating an account on GitHub.

github.com

 

 

@admin.register(models.Photo)
class PhotoAdmin(admin.ModelAdmin):

    """ Photo Admin Definition """
    pass
class Photo(core_models.TimeStampedModel):

    """Photo Model Definition"""

    caption = models.CharField(max_length=80)
    file = models.FileField()
    room = models.ForeignKey(
        "Room", related_name="photos", on_delete=models.CASCADE)

    def __str__(self):
        return self.caption

 

FileField()나 ImageField()를 통해 업로드를 하면 경로에 해당 파일이 업로드 되고, 업로드한 내용을 보려고 클릭하면 경로가 없다고 나옵니다.

 

 

 

그도 그럴 것이, 업로드한 내용은 코드를 작성하고 있는 __dirname에 저장되기 때문입니다.

 

따라서 이미지 파일을 서버 내에서 보기 위해서는 Django에게 파일을 어디에 저장해야 하며, 해당 파일을 클릭했을 때 어느 url로 가야하고 무슨 행동을 해야 하는지 알려줘야 한다.

 

 

아래 설정은 개발 과정에 초점이 맞춰져 있으며 배포 시에는 미디어 파일은 AWS S3 등 다른 서버에 저장하는 것이 일반적입니다.

 

 

 

🚀 MEDIA_ROOT: 파일을 업로드하면 어디에 저장할 것인가?

 

 

Settings | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

MEDIA_ROOT의 Default는 '' (Empty string) 이기 때문에 일반 파일 루트(BASE_DIR)에 저장하게 되는 것입니다.

 

MEDIA_ROOT

Default: '' (Empty string)

Absolute filesystem path to the directory that will hold user-uploaded files.

Example: "/var/www/example.com/media/"

See also MEDIA_URL.

 

 

config/setting.py에 가서 절대 경로를 지정해주면 그곳으로 업로드가 됩니다.

 

현재 경로인 BASE_DIR에 uploads라는 이름의 폴더 내부에 업로드할 사진이나 파일을 몰아 넣기 위해 MEDIA_ROOT를 다음과 같이 설정해줍시다.

 

# config/settings.py

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

MEDIA_ROOT = os.path.join(BASE_DIR, "원하는 폴더 명")

 

 

주의할 사항이 있는데 STATIC ROOT와는 다른 값을 가져야 한다는 것입니다. 

 

Warning
MEDIA_ROOT and STATIC_ROOT must have different values. Before STATIC_ROOT was introduced, it was common to rely or fallback on MEDIA_ROOT to also serve static files; however, since this can have serious security implications, there is a validation check to prevent it.

 

 

 🐱‍👤 upload_to로 폴더 정리

 

그런데, MEDIA_ROOT로 지정한 경로 내에서 또 업로드한 이미지들을 폴더 별로 구분하기 위해서는 upload_to를 사용해야 합니다. 

 

# room_photos 폴더 내에 저장
file = models.ImageField(upload_to="room_photos ")

# avatars 폴더 내에 저장
avatar = models.ImageField(upload_to="avatars", blank=True) 

 

 

 

🚀 MEDIA_URL : 업로드한 이미지를 보려면 어느 경로로?

 

 

MEDIA_URL

Default: '' (Empty string)

 

URL that handles the media served from MEDIA_ROOT, used for managing stored files. It must end in a slash if set to a non-empty value. You will need to configure these files to be served in both development and production environments.

 

 

MEDIA ROOT를 servered하는 url을 지정합니다. 빈 값을 넣지 않으려면 반드시 /로 끝나야 합니다. 그래야 /뒤에 업로드한 파일의 이름이 서빙될테니깐요. 앞서 지정한 MEDIA ROOT와 동일하게 설정해도 되지만 다르게 설정해도 됩니다. 일부러 다른 값인 media/를 넣어 보았습니다.

# config/setting.py

MEDIA_URL = "media/"

 

이제 업로드한 이미지를 클릭해보면 다음과 같은 주소로 표현됩니다. 경로가 media/나머지 경로로 설정되었습니다.

http://127.0.0.1:8000/admin/rooms/photo/4/change/media/room_photos/%EC%BA%A1%EC%B2%98.PNG

 

그런데 이 경우 admin 경로 등의 정보가 url에 그대로 나타나있습니다. 보안상으로 문제가 있는 구조입니다. 이는 MEDIA_URL에 설정한 media/가 상대 경로이기 때문입니다.

 

MEDIA_URL의 앞에 /를 붙여주면 다음과 같이 url 수정됩니다.

MEDIA_URL = "/media/"
http://127.0.0.1:8000/media/room_photos/%EC%BA%A1%EC%B2%98.PNG

 

그러나 해당 경로로 접속해보면 다음과 같은 오류를 볼 수 있습니다.

 

Using the URLconf defined in config.urls, Django tried these URL patterns, in this order:

  1. admin/

The current path, media/room_photos/캡처.PNG, didn't match any of these.

 

config.urls를 사용해서 url로 접속을 시도했는데 일치하는 경로가 없다는 것이다.

우리의 링크가 /media/...로 시작하는 반면 config에서는 admin/을 받도록 설정되어 있기 때문입니다.

# config/urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls)
]

 

 

 

🚀 url 설정

 

따라서 우리가 설정한 config/setting을 import하여 활용합시다. 여기서, from . import settings로 가져올 수 없습니다. config 파일의 이름이 달라져도 django.conf에서 가져오면 파일 이름이 무엇이든 config를 가져옵니다.

 

from django.contrib import admin
from django.urls import path

# 새로 추가
# from . import settings 하면 안됩니다
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls)
]

# config/settings.py의 DEBUG가 True일 때만 작동
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

 

이제, 업로드한 이미지를 클릭해서 볼 수 있습니다. 다만, 배포 시에는 DB, 이미지를 담고 있는 서버 등을 코드 서버와 분리하는 것이 일반적이므로 (AWS S3에 동영상, 사진 등을 저장하는 방식이 일반적), 개발 시에만 이런 방식으로 작업한다는 것을 유념합시다.

 

 

🚀 mark_safe

 

✔ tip) 어드민 패널에 img 태그를 통해 이미지 보여주기

 

어드민 패널에서 파일의 url을 클릭해서 이미지를 확인할 수 있게 HTML 태그를 달아주면 작동하지 않습니다. 만약 악의적인 어드민이 웹을 열어 JS 스크립트를 심어 놓고 작동시키면 보안상의 위협이 있기 때문입니다.

 

flask에서도 마찬가지로 | safe를 붙여줬듯이 django에서도 조치를 취해줘야 합니다.

 

이 경우 django.utils 에서 mark_safe를 불러와 html tag를 감싸줘야 합니다.

from django.contrib import admin
from django.utils.html import mark_safe
from . import models

@admin.register(models.Photo)
class PhotoAdmin(admin.ModelAdmin):

    """ Photo Admin Definition """

    list_display = (
        "__str__", "get_thumbnail"
    )

    def get_thumbnail(self, obj):
        # dir(obj.file을 찍어보면 size, height 등 많은 속성 존재.)

        return mark_safe(f"<img width='150px' src={obj.file.url}/>")
    get_thumbnail.short_description = "Thumbnail"

    pass

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체