서버 측에서 변경되지 않고 고정적으로 제공되는 파일이다.
웹 사이트를 구성할 때 HTML뿐만 아니라 CSS, JavaScript, 이미지, 폰트 등의 파일도 필요하다.
이처럼 개발자가 미리 준비해두고 사용자에게 동일하게 제공하는 파일을 정적 파일(Static Files) 이라고 한다.
웹 사이트의 로고는 사용자가 바뀌더라도 동일한 파일을 사용하므로 Static File에 해당한다.
웹 서버가 제공하는 가장 기본적인 자원이 정적파일 이다.
http://example.com/images/logo.png 라는 주소를 입력하면서 이미지를 요청한다./images/logo.png 의 URL을 확인하고 서버에 미리 약속된 디렉토리에서 logo.png 를 찾는다.
Django에서 각 앱의 정적 파일을 저장하는 기본 경로는 다음과 같다.
app폴더/static/
articles 앱에서 CSS 파일을 사용한다면 다음과 같은 구조를 만들 수 있다.

h1 {
color: crimson;
}
Django Template에서 Static File을 사용하려면 먼저 static 태그를 불러와야 한다.
{% load static %}
CSS 파일은 다음과 같이 연결한다.
{% load static %}
<link
rel="stylesheet"
href="{% static 'stylesheets/style.css' %}"
>
{% static %} 태그는 정적 파일에 접근할 수 있는 URL을 만들어준다.
CSS 파일이 정상적으로 적용되어 h1의 색상이 변경된 것을 확인할 수 있다.
또한 {% static %} 태그를 통해 정적 이미지의 URL이 /static/... 형태로 생성되어 브라우저에 정상적으로 출력되는 것을 확인할 수 있다.

{% load static %}Django Template에서 Static 관련 Template Tag를 사용할 수 있도록 불러오는 역할을 한다.
{% load static %}
{% static %}을 사용하려면 Template 파일에서 먼저 선언해야 한다.
단, {% extends %}를 사용하는 경우 load 태그는 상속되지 않기 때문에 필요한 Template 파일에서 직접 작성해야 한다.
{% static %}정적 파일에 접근하기 위한 URL을 생성하는 Template Tag이다.
{% static 'stylesheets/style.css' %}
라고 작성하면 Django가 STATIC_URL 설정을 이용하여 정적 파일의 URL을 만든다.
웹 페이지에서 정적 파일에 접근하기 위해 사용하는 URL의 시작 경로이다.
기본적으로 다음과 같이 설정된다.
STATIC_URL = 'static/'
예를 들어:
{% static 'images/sample-1.png' %}
를 사용한다면 브라우저에서는 다음과 같은 주소로 접근하게 된다.
http://127.0.0.1:8000/static/images/sample-1.png
STATIC_URL은 서버 컴퓨터의 실제 파일 경로가 아니라 브라우저에서 접근하기 위한 URL 주소이다.
Static 폴더에 이미지를 저장할 수 있다.

Template에서는 다음과 같이 사용한다.
{% load static %}
<img
src="{% static 'images/sample-1.png' %}"
alt="sample image"
>
{% static %} 태그를 사용하면 static 폴더에 있는 이미지의 URL을 생성하여 브라우저에 출력할 수 있다.

Django에서는 각 앱 내부의 static 폴더뿐만 아니라 프로젝트에서 공통으로 사용하는 Static 폴더를 추가로 설정할 수 있다.
Django가 기본 Static 경로 이외에 추가적으로 탐색할 정적 파일 경로를 지정하는 설정이다.
기본 경로는 각 앱 내부의:
app/static/
이지만 여러 앱에서 공통으로 사용하는 CSS나 이미지가 있다면 프로젝트 최상단에 Static 폴더를 만들 수도 있다.

이 경로를 Django가 찾을 수 있도록 settings.py에 설정한다.
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
Django는 Static File을 찾을 때 추가 경로를 확인하고 각 앱의 static 폴더도 탐색한다.
Template에서는 다음과 같이 사용한다.
{% load static %}
<img
src="{% static 'sample-2.png' %}"
alt="sample image"
>

사용자가 웹 사이트를 통해 직접 업로드하는 파일이다.
Static File은 개발자가 미리 준비한 파일이라면 Media File은 사용자가 서비스 이용 중 직접 업로드하는 파일이다.
예를 들면:
| 구분 | 예시 |
|---|---|
| Static File | 로고, CSS, 아이콘 |
| Media File | 프로필 사진, 게시글 첨부 이미지 |
Static = 개발자가 준비한 파일
Media = 사용자가 업로드한 파일
이미지를 업로드하기 위해 사용하는 Django Model Field이다.
게시글에 이미지를 추가하려면 Model에 ImageField를 작성한다.
class Article(models.Model):
title = models.CharField(max_length=10)
content = models.TextField()
image = models.ImageField(
upload_to='images/',
blank=True,
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
upload_to업로드한 파일을 어떤 하위 경로에 저장할지 지정한다.
upload_to='images/'
blank=TrueForm에서 해당 필드를 비워도 유효성 검사를 통과할 수 있도록 한다.
즉 이미지를 반드시 첨부하지 않아도 게시글을 작성할 수 있다.
Django에서 ImageField를 사용하려면 이미지 처리를 위한 Pillow 라이브러리가 필요하다.
pip install pillow
설치 후 Migration을 진행한다.
python manage.py makemigrations
python manage.py migrate
필요하다면 의존성도 기록한다.
pip freeze > requirements.txt
사용자가 업로드한 Media File이 실제 서버의 어느 폴더에 저장될지를 지정한다.
MEDIA_ROOT = BASE_DIR / 'media'
예를 들어 사용자가 이미지를 업로드하면 실제 파일은 다음과 같이 저장될 수 있다.
프로젝트/
└── media/
└── images/
└── sample-1.png
MEDIA_ROOT는 실제 파일 저장 위치이다.
Media File을 브라우저에서 접근하기 위한 URL의 시작 경로이다.
MEDIA_URL = 'media/'
예를 들어 실제 파일이 media/images/sample-1.png 에 저장이 되어있으면 브라우저는
http://127.0.0.1:8000/media/images/sample-1.png 로 접근한다.
| 설정 | 역할 |
|---|---|
MEDIA_ROOT | 실제 파일 저장 경로 |
MEDIA_URL | 브라우저 접근 URL |
개발 환경에서 업로드된 Media File을 제공하기 위해 프로젝트의 urls.py에 설정을 추가한다.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('articles/', include('articles.urls')),
] + static(
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT,
)
Django 개발 서버가 /media/로 들어오는 요청을 확인한 뒤 MEDIA_ROOT에 저장된 실제 파일을 찾아 응답하도록 한다.
파일 업로드를 위해서는 Form과 View에서 추가적인 설정이 필요하다.
파일을 서버로 전송하려면 <form> 태그에 다음 속성이 필요하다.
enctype="multipart/form-data"
<form
action="{% url 'articles:create' %}"
method="POST"
enctype="multipart/form-data"
>
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
일반적인 문자 데이터와 달리 파일을 함께 전송할 때는 반드시 multipart/form-data를 사용해야 한다.
게시글 생성 화면에 이미지 파일을 선택할 수 있는 입력 요소가 추가된다.

문자 데이터는 request.POST에 들어가지만 업로드한 파일은 request.FILES에 들어간다.
def create(request):
if request.method == 'POST':
form = ArticleForm(
request.POST,
request.FILES,
)
if form.is_valid():
article = form.save()
return redirect(
'articles:detail',
article.pk,
)
else:
form = ArticleForm()
context = {
'form': form,
}
return render(
request,
'articles/create.html',
context,
)
request.POST : 일반 입력 데이터
request.FILES : 파일 데이터
로 구분할 수 있다.
이미지를 선택하고 게시글을 생성하면 실제 이미지 파일은 MEDIA_ROOT 아래에 저장된다.

DB에는 이미지 파일 자체가 저장되는 것이 아니라 파일의 경로 문자열이 저장된다.
프로젝트의 media/images 폴더에 실제 이미지가 저장된 것을 확인할 수 있다.

게시글 상세 페이지에서 업로드한 이미지를 보여주려면 ImageField의 .url 속성을 사용한다.
{% if article.image %}
<img
src="{{ article.image.url }}"
alt="image"
>
{% endif %}
.url저장된 이미지 파일에 브라우저가 접근할 수 있는 URL을 반환한다.
{{ article.image.url }} 의 결과가 /media/images/sample-1.png 가 된다.
이미지를 업로드하지 않은 게시글에서 바로 .url을 사용하면 오류가 발생할 수 있기 때문에 조건문을 사용한다.
{% if article.image %}
게시글 제목과 내용뿐만 아니라 사용자가 업로드한 이미지가 상세 페이지에 표시된다.


게시글 수정에서도 파일을 업로드할 수 있어야 하기 때문에 생성과 동일하게 설정해야 한다.
<form
action="{% url 'articles:update' article.pk %}"
method="POST"
enctype="multipart/form-data"
>
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
View에서도 request.FILES를 전달한다.
form = ArticleForm(
request.POST,
request.FILES,
instance=article,
)
수정에서는
request.POST : 수정된 일반 데이터
request.FILES : 새로 업로드한 파일
instance = article : 수정할 기존 게시글
을 함께 전달한다.
기존 게시글의 이미지를 다른 이미지로 변경한 뒤 수정된 결과를 확인할 수 있다.


upload_to는 단순히 고정된 폴더 문자열만 사용할 수 있는 것이 아니다.
업로드 날짜 등을 이용해 동적으로 경로를 만들 수 있다.
image = models.ImageField(
blank=True,
upload_to='%Y/%m/%d/',
)
예를 들어 2100년 1월 1일 업로드한다면 2100/01/01/ 폴더 아래 저장될 수 있다.
복잡한 규칙이 필요하다면 함수를 정의할 수도 있다.
def articles_image_path(instance, filename):
return f'images/{instance.user.username}/{filename}'
image = models.ImageField(
blank=True,
upload_to=articles_image_path,
)
형태로 사용한다.
upload_to 함수는 일반적으로
instance : 파일이 첨부되는 Model 객체
filename : 업로드한 원본 파일 이름
을 전달받는다.
| 구분 | Static File | Media File |
|---|---|---|
| 파일 생성 주체 | 개발자 | 사용자 |
| 예시 | CSS, JS, 로고 | 프로필 사진, 게시글 이미지 |
| 실제 파일 경로 | static 폴더 | MEDIA_ROOT |
| URL 설정 | STATIC_URL | MEDIA_URL |
| 추가 경로 설정 | STATICFILES_DIRS | MEDIA_ROOT |
| Template 사용 | {% static %} | .url |
| 업로드 처리 | 필요 없음 | request.FILES 필요 |
Static File = 개발자가 미리 준비한 파일
Media File = 사용자가 업로드한 파일
Amazon에서 제공하는 클라우드 컴퓨팅 서비스이다.
직접 서버, 파일 저장소, 데이터베이스 장비를 구축하지 않고 인터넷을 통해 필요한 만큼 사용할 수 있다.
대표적인 서비스
| 서비스 | 역할 |
|---|---|
| EC2 | 서버 |
| S3 | 파일 저장소 |
| RDS | 데이터베이스 |
실제 서비스를 운영한다고 가정하면 역할을 다음과 같이 나눌 수 있다.
EC2
→ Django 애플리케이션 실행
S3
→ 이미지, Media File, Static File 저장
RDS
→ 게시글, 사용자 정보 등의 DB 데이터 저장
동작 흐름의 예시는 다음과 같다.
사용자
↓
EC2의 Django 서버
↓
RDS에서 데이터 조회
↓
HTML 응답
↓
필요한 이미지 → S3 요청
↓
사용자에게 이미지 제공
사용자가 이미지를 업로드하는 경우에는 Django 서버가 파일을 받아 S3에 저장하는 방식으로 구성할 수도 있다.

직접 물리 서버를 구매하지 않고 필요한 만큼 사용할 수 있다.
서비스 사용량에 따라 서버나 저장 공간을 조절할 수 있다.
여러 지역의 데이터 센터를 이용하여 다양한 국가의 사용자에게 서비스를 제공할 수 있다.
Django의 ModelForm은 내부적으로 BaseModelForm을 기반으로 동작한다.
BaseModelForm의 생성자에는 여러 인자가 존재하며 그중 파일 데이터를 받는 인자도 존재한다.
files=None
그래서 ModelForm을 생성할 때:
ArticleForm(
request.POST,
request.FILES,
)
처럼 첫 번째 인자에는 일반 데이터, 두 번째 인자에는 파일 데이터를 전달할 수 있다.
data → request.POST
files → request.FILES
형태로 연결된다.
개발자가 미리 준비하여 고정적으로 제공하는 파일이다.
STATIC_URL = 'static/'
추가적인 Static 경로가 필요하다면
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
를 사용한다.
Template에서는
{% load static %}
{% static 'images/logo.png' %}
형태로 접근한다.
사용자가 웹 서비스를 이용하면서 직접 업로드하는 파일이다.
실제 저장 위치
MEDIA_ROOT = BASE_DIR / 'media'
브라우저 접근 주소
MEDIA_URL = 'media/'
이미지 업로드 Model
image = models.ImageField(
upload_to='images/',
blank=True,
)
파일 전송 Form
<form enctype="multipart/form-data">
View
form = ArticleForm(
request.POST,
request.FILES,
)
이미지 출력
{% if article.image %}
<img src="{{ article.image.url }}" alt="image">
{% endif %}
핵심은 다음과 같다.
STATIC_URL→ Static File의 웹 주소
STATICFILES_DIRS→ 추가 Static 탐색 경로
MEDIA_ROOT→ 업로드 파일의 실제 저장 위치
MEDIA_URL→ 업로드 파일의 웹 주소
ImageField→ 이미지 업로드 Model Field
enctype="multipart/form-data"→ 파일 전송을 위한 Form 설정
request.FILES→ 업로드한 파일 데이터
.url→ 업로드한 파일의 접근 URL