Image 업로드와 media 경로

TeetyWoo·2021년 11월 11일
1

Django

목록 보기
10/11

Django 업로드한 이미지를 불러오는데 생긴 에러사항, 구글링으로 찾은 블로그에 media와 static 차이에 대해 상세하게 잘 기술되어 있어 문제를 해결할 수 있었다.

static 에러상황

class Theme(models.Model):
    themeImage = models.ImageField(upload_to='themeImages/', height_field=None, width_field=None,blank=True)    

Django 모델에서 다음과 같이 이미지 필드를 생성하고 이미지 파일을 업로드했다.
엉뚱하게도 static폴더 안도, 만들고 있는 app폴더 안도 아닌 최상단 루트 폴더 아래 themeImages라는 폴더가 생성되고, 그 안에 이미지가 담긴다.

settings.py
STATIC_URL = '/static/'

example.html
<img src="{% static 'themeImages/example.jpg' %}">

settings.py의 설정을 보니 static 폴더 안에 이미지 파일이 생성되어야 할 것 같았다.
그래서 upload_to='static/themeImages/'와 같이 수정하니 app폴더 안, static 폴더 안에 새로 폴더가 정상적으로 생성되는 것을 확인했다. 그런데 이미지는 계속 엑박으로 뜨는 상황

get_static_prefix?

구글링 끝에

<img src="{% get_static_prefix 'themeImages/example.jpg' %}">

이렇게 src를 수정하고, 외부에서 생성되는 이미지폴더를 드래그앤드랍으로 static폴더 안에 집어넣었더니 갑자기 이미지가 불러와졌다. 이건 영 엉뚱한 해결방안이었는데, 애초에 get_static_prefix의 용도는

<img src="{% get_static_prefix 'themeImages/example/{{example}}.jpg' %}">

처럼 static태그 안에 변수가 들어갔을 때 encode 되어버리지 않도록 하는 용도였기 때문이다.

Django 구조를 더 이해해야 할 것 같아서 구글링하던 도중, 개발자가 가지고 있는 이미지가 아니라, 사용자가 직접 업로드 하는 Image의 경우 media폴더에 들어가는게 맞다는 것을 알게 되었다. 애초에 static폴더에서 이미지를 불러올게 아니라 media폴더에서 불러와지도록 하는 것이 정석이었던 것이다.

Static과 Media

Static

  • 이미 가지고 있는 파일들을 보여주는 것이기에 외부와 통신할 필요 없음.
  • settings.py에서 static 파일이 어디에 있고, 어디로 모을지를 설정해줌.

Media

  • 사용자가 업로드한 파일을 받아와야 하는 것이기에 외부와의 통신 필요
  • settings.py에서 media 파일이 어느 url을 타고, 어디로 모을지를 설정해줌.
  • media 파일이란 FileField 를 통해 저장한 모든 파일을 지칭
  • ImageField 도 FileField 를 상속받은 필드로서 해당 필드로 저장된 파일도 media 파일

    이걸 몰라서 문제가 생겼던 것....
<img src="{% get_static_prefix %}{{theme.themeImage}}"/>
    <img src="{{theme.themeImage.url}}">

Media 폴더 연결 방법

1) 사용자가 이미지를 업로드 하면 이미지가 저장될 장소 설정

setting.py
MEDIA_URL = '/media/'

각 media 파일에 대한 URL 의 고정값을 설정할 수 있습니다.
http://127.0.0.1:8000/media/파일경로 형태가 되게 하기 위해서 저렇게 설정해준 것입니다.

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

미디어 파일이 저장될 경로를 설정한 부분입니다. (나같은 경우 appname, 'media'로 설정)

Static의 경우 개발하며 직접적으로 관리해주어야 하는 파일이지만, Media 파일은 웹사이트 이용자가 올리는 파일이기 때문에 App 차원에서 직접 관리할 필요가 없다.
때문에 STATICFILES_DIR과 다르게 MEDIAFILES_DIRS 설정은 없다.

STATICFILES_DIR?

개발 단계에서 사용하는 정적 파일이 위치한 경로들을 지정하는 설정 항목입니다.
특정 Django App2에만 사용하는 정적 파일이 있거나 혹은 정적 파일을 관리하기 용이하게 하기 위해 여러 경로(path)에 정적 파일을 배치하였다면, 이 경로들을 Python의 list나 tuple로 담으면 됩니다.

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

대개는 static이라는 디렉터리에 정적 파일을 담습니다.
주의할 점은 정적 디렉터리 경로가 하나이더라도 반드시 list나 tuple로 담아야 한다는 점입니다. 흔히 하는 실수는 항목 뒤에 쉼표를 빠뜨리는 것입니다.

2) media에 파일 올리기

앞서 적었듯이 models.ImageField(upload_to='themeImages/') 이런식으로 모델에 이미지 필드와 경로를 설정해 준다. upload_to를 설정하지 않으면 위의 MEDIA_ROOT에서 설정한 곳에 바로 이미지가 올라가게 된다.

2-1) 터미널에서 이미지를 데이터베이스에 올릴 때 사용하는 패키지를 설치

pip install pillow

2-2) DB에 migrate, admin.py에 모델 가져오기

(3) upload.html

<form action="{% url 'upload_create' %}" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    제목 <br>
    <input type="text" name="title"> <br>
    내용 <br>
    <input type="file" name="image"><br>
    <input type="submit" value="Submit" />
</form>

method 는 POST로, enctype 도 다음과 같이 설정을 해주셔야 합니다.
이를 설정하지 않으신다면 파일은 넘어오지 않고 파일 이름만 넘어오게 됩니다.
method 속성값이 “post”인 경우에만 사용할 수 있습니다.

속성값이 "multipart/form-data"라는 의미는 모든 문자를 인코딩하지 않는다는 것을 명시합니다.

enctype?

전송 데이터 형식 설정, 해당 데이터가 인코딩되는 방법을 명시한다.
method 속성값이 “post”인 경우에만 사용할 수 있다.
encoding type 의 약자, 종류는 세가지 있다.

  1. application/x-www-form-urlencoded
    디폴트 값. 서버로 전송되기 전에 url-encode 된다는 뜻.
  2. mutipart/form-data
    이미지나 파일을 서버로 전송할 경우 이 방식을 사용
    모든 문자를 인코딩하지 않는다는 것을 명시
  3. text/plain
    인코딩을 하지 않은 문자 그대로의 상태를 전송한다는 의미.

(4) upload.html에 view,url연결

views.py

def upload(request):
    return render(request,'upload.html')

def upload_create(request):
    form=Profile()
    form.title=request.POST['title']
    try:
        form.image=request.FILES['image']
    except: #이미지가 없어도 그냥 지나가도록-!
        pass
    form.save()
    return redirect('/myprofile/profile/')  

여기서 특이점은 파일 및 이미지를 받을 때는 request.FILES을 이용해야 한다는 점입니다.(request.POST가 아님)
이때 파일의 저장경로가 문자열로 db에 들어가고 실제 저장은 해당경로에 되게 됩니다.

(5) urls.py

path('myprofile/upload/',myprofile.views.upload,name="upload"),    
path('myprofile/upload_create/',myprofile.views.upload_create,name="upload_create"),

static파일의 경우 STATIC_URL로 설정한 폴더에 있는 이미지 파일이고, 서버가 어떤 경로에 어떤 파일이 있는지 알지만,
media파일의 경우 서버는 그 파일이 있는지 없는지, 그 이름은 무엇인지를 알 수 없습니다.

즉, static 파일은 쟝고에서 직접 serving 해줬지만, media 파일은 serving하기 위한 url을 짜주어야 합니다.

이때 static메소드를 이용해 media파일의 url과 경로를 관리해줍니다.

#urls.py

from django.conf import settings
from django.conf.urls.static import static

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
#or
urlpatterns[(생략)] +static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

MEDIA_URL과 MEDIA_ROOT에 저장되어 있다는 것을 알려줘야 하니, settings를 import 해줬습니다.

static 함수는 정적 파일들의 url을 관리하는 함수입니다.
첫 번째 인자로 Media파일의 URL을, 키워드 인자 document_root로 Media파일이 위치한 경로를 전달했습니다.

이는 “Media파일이 있으면 URL에 MEDIA_URL을 통해(-/media), 실제 파일은 MEDIA_ROOT에서 가지고와줘.”라는 의미라고 보시면 됩니다.
이때 static 함수를 urlpateerns라는 리스트에 바로 넣지 않고 +=를 한 것은 static 함수가 여러개의 url을 return 할 수도 있기 때문입니다.

(6) 실제 html에 적용

<!-- 1번 -->
<img src="{{ file.image }}" >
<!-- 2번 -->
<img src="{{ file.image.url }}" >
<!-- 3번 -->
<img src="{{ file.image.path }}" >

1번의 경우, db에는 파일의 경로가 문자열로 저장되어 있기 때문에 저 부분은 그저 문자열을 참조하게 될 것입니다.
2번과 3번은 모두 우리가 원하는 값을 받을 수 있는데, url 은 media 폴더 부터 시작하는 상대 경로를 의미하고, path 는 절대경로를 나타냅니다.

둘 다 media 파일을 참조하기 때문에 이미지를 불러올 수 있습니다.

Media 파일 처리 순서

views에서 HttpRequest.FILES 를 통해 파일이 전달
views,form 로직을 통해 유효성 검사 수행
ImageField(FileField)에 문자열로 경로를 저장
settings.MEDIA_ROOT 경로에 파일을 저장 (원한다면 AWS등의 클라우드 스토리지에도 저장이 가능합니다)

문제 해결

이와 같이 Media 파일이 업로드 되는 경로와 urls.py를 수정하고 html에

<img src="{% 'theme.themeRevImage.url' %}>

이런식으로 경로를 수정했더니 잘 출력된다


참조 블로그
[Django-Media파일, 파일업로드] https://free-eunb.tistory.com/43
[Django 정적 파일 기능 이해하기] https://blog.hannal.com/2015/04/start_with_django_webframework_06/
[form 태그 속성 중 enctype]
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=freakout1242&logNo=143392199

0개의 댓글

관련 채용 정보