---
description: 210725, 210726
---

# 정적 파일(static files) & 미디어 파일(media files)

## 정적파일 관리하기 - 포스트 목록 페이지에 부트스트랩 적용하기

예전에 1, 2강에서 사용했던 부트스트랩을 사용하려고 한다. 이를 불러온다. 위치는 `blog/static/blog/bootstrap` 안에 css 파일들을 모두 불러오면 된다.

그리고 예전에 사용한 `blog_list.html` 을 모두 복사해서 지금 가지고 있는 `post_list.html` 로 붙여넣는다.

이 때 서버를 실행하면 부트스트랩이 적용이 되지 않는다. 이유는 올바른 부트스트랩 경로를 참조하고 있지 않기 때문. 일단 장고에서는 무조건 urls.py를 통해 경로 접근이 가능해야 한다. 근데 여기서 static 파일들을 바로 접근할 수 있도록 하는 코드가 있다. 바로 이것.

```markup
<!DOCTYPE html>
{% load static %}
<html>
<head>
    <title>상만두의 웹사이트</title>
    <link href="{%  static 'blog/bootstrap/bootstrap.min.css' %}" rel="stylesheet" type="text/css">
    <script src="https://kit.fontawesome.com/726bbd6862.js" crossorigin="anonymous"></script>
</head>
```

`{% load static %}` 을 써주면 된다. 그리고 이후에 css 파일을 참조할 때 6번 줄과 같이 써주면 된다.



내 게시글 수 만큼 Blog Post를 반복하게 하려면 다음과 같이 for문을 써서 동적으로 만들어 주면 된다. 이 때 포스트 제목과 내용, 연결 링크까지 수정해주자.

```markup
{% for p in post_list %}
<!-- Blog post-->
<div class="card mb-4">
    <a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
        <div class="card-body">
        <div class="small text-muted">January 1, 2021</div>
        <h2 class="card-title h4">{{ p.title }}</h2>
        <p class="card-text">{{ p.content }}</p>
        <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
    </div>
</div>
{% endfor %}
```

그리고, created\_at 을 추가해주자. 또, 추후에 `models.py` 를 수정해서 author 까지 추가해줘야 하는데 까먹지 말라고 명시해두자

```markup
<div class="card-footer text-muted">
    Posted on {{ p.created_at }} by
    <a href="#">작성자명 쓸 위치</a>
</div>
```

## 정적파일 관리하기 - 블로그 포스트 상세 페이지에 부트스트랩 적용하기

블로그 포스트 디자인도 부트스트랩을 적용하려고 한다.

{% embed url="https://github.com/StartBootstrap/startbootstrap-blog-post/blob/fb72b8094a1ce45bbc5a92d20ef7d70a942d3bb5/dist/index.html" %}

* 현재는 5.0 버전이고 강의는 4.5.3 버전. 이 링크는 4.6버전이다.

여기서 `index.html` 의 코드를 다운받고 `post_detail.html` 에 붙여넣기. 그리고 `models.py` 에 정의되어 있는 데이터들로 좀 바꿔준다. title, created\_at, context 등등



네비게이션 바 클래스로 `fixed-top` 이 있는데, 스크롤을 내리더라도 네비게이션 바가 상단에 유지되게 하는 기능이다. 이 때 body에 있는 내용이 네비게이션바에 가려질 수가 있기 때문에 다음과 같이 추가해야 한다.

`blog_post.css`

```css
body {
    padding-top: 56px;
}
```

당연히 이 css 파일을 불러오려면 static 으로 불러와야 한다

`post_detail.html`

```markup
<link href="{%  static 'blog/css/blog-post.css' %}" rel="stylesheet" />
```



## 미디어 파일 관리하기 - 이미지 파일 업로드를 위한 ImageField

기본적으로 static 파일들은 장고가 접근할 수 있도록 프로젝트 폴더의 `settings.py` 의 `STATIC_URL` 에 정의되어 있다. 사진이나 동영상 같은 미디어 파일들도 접근할 수 있도록 `MEDIA_URL` 에 정의하자.

`settings.py`

```python
import os

BASE_DIR = Path(__file__).resolve().parent.parent

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, '_media')
```

* 기본적으로 `BASE_DIR`이 정의되어있고 미디어 파일을 관리하는 폴더를 `BASE_DIR` 하위 디렉토리에 두기 위해서 `os` 라이브러리를 호출하고 연결한다.

또한, 프로젝트 폴더의 `urls,py` 에서 한번 더 수정해줘야 한다

`urls.py`

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

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

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

* 이 때 `document_root` 를 설정해주지 않으면 C드라이브 하위 디렉토리 `_media` 폴더를 찾게된다. 꼭 `MEDIA_ROOT` 로 설정해줄 것.&#x20;

또, 이미지를 추가하기 위해 `models.py` 를 수정한다.

`blog/models.py`

```python
class Post(models.Model):
    head_image = models.ImageField(upload_to='blog/images/%Y/%m/%d/')
```

* 년/월/일/ 폴더에 저장할 것이다.
* 장고에서는 `ImageField` 타입도 제공한다.
  * 이 때 Pillow를 설치해야 한다. 파이썬에서 이미지를 다룰 수 있게 해주는 패키지
* black=True는 다음과 같이 빈칸으로 두었을 때 에러메시지가 나오는 것을 사라지게 해준다.
  * 기본값은 False 기 때문에 제목이나 내용을 빈칸으로 둘 수 없었다.
* 이 때 model의 데이터가 바뀌는 것이므로 `python manage.py makemigrations` 와 `python manage.py migrate`를 꼭 해주자.

이 때 이미지를 추가해주면,

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfX9Fstx-CWU4l8xmK5%2Fimage.png?alt=media\&token=169711de-9ca7-4c36-b0a7-5116daf2b55c)

미디어 파일들을 관리하는 폴더가 알아서 생긴 모습

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfX9P-b1v06pkBVPvou%2Fimage.png?alt=media\&token=93a91477-7d89-4b8b-86ef-c59a95fc7b3d)

이 때 폴더 관리를 위처럼 하는 이유가 있다. 만약 이미지 이름이 2021-07-26-줌배경.jpg 였다면, 모든 파일들의 이름을 체크해야 하는 번거로움이 있다. 디렉토리 구조를 다음과 같이 나눠놓으면 이미지 탐색시간이 훨씬 줄어들게 된다.



그리고, `post_list.html` 에서도 이 이미지를 출력할 수 있게 수정해준다

```markup
<a href="#!"><img class="card-img-top" src="{{ p.head_img.url }}" alt="{{ p.title }}" /></a>
```

* 이 때 리로드를 하면 오류가 난다.
  * 왜냐하면 세번째 포스트에만 이미지가 있고 두번째, 첫번째 포스트에는 이미지가 없기 때문
  * 원래는 if 문으로 처리해야 한다.
    * 이는 나중에 다룰 것

모든 포스트에 이미지를 업로드해주면 정상적으로 로딩이 되는 모습

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfXBTLRjl9tcl4BD7lE%2Fimage.png?alt=media\&token=8eeca992-0d73-4183-a54f-aecaa9d6994b)

이 때 업로드 한 파일이 같으면 자동으로 다른 이름으로 저장해준다.

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfXCJCDJeb8WMMf63zV%2Fimage.png?alt=media\&token=2f962e49-7169-4701-8ec8-9274c4632d4d)

깃에서 media 파일까지 관리할 필요없게 ignore에 미디어 폴더를 추가한다. 아직 `post_detail.html` 에 있는 이미지는 바뀌지 않았다. 따라서 이것도 수정해준다.

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfXCTpATYHFcl03ExL4%2Fimage.png?alt=media\&token=7d6414a6-89a1-4109-97a7-f01ead021c0e)

```markup
<img class="card-img-top img-fluid rounded" src="{{ post.head_image.url }}" alt="{{ post.title }}"/>
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfX5xmxz8AApcbP9PpN%2F-MfXD1g4_Z6lRTgtsEtg%2Fimage.png?alt=media\&token=d19cec36-25fb-4e8c-b411-f56e1a59252d)



## 미디어 파일 관리하기 - 파일 업로드를 위한 FileField

이번에는, 파일을 업로드 해보자.

`models.py`

```python
class Post(models.Model):
    file_upload = models.FileField(upload_to='blog/files/%Y/%m/%d/', blank=True)
```

&#x20;파일 업로드 기능이 추가된 모습(migrate 필수)

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfXD_Kc9uF8aKiZbwPb%2F-MfXE2NjyB0Avni3_2ni%2Fimage.png?alt=media\&token=2a73d043-5e2b-4ccd-9025-3781ad641709)

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfXD_Kc9uF8aKiZbwPb%2F-MfXEEUO4nr7B4AE5a0U%2Fimage.png?alt=media\&token=0e86c40f-4239-4d3f-a670-9a5ea4499cff)

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfXD_Kc9uF8aKiZbwPb%2F-MfXEJ9M-l4m9iccd7IT%2Fimage.png?alt=media\&token=21e469ae-45f2-49ef-8820-e0a74fb3505e)



이 때, `post_detail.html` 에도 업로드 된 파일을 출력해주거나 다운받을 수 있는 기능을 제공하도록 코드를 작성해줄 수 있다. 이것은 이후에 if문을 배운 뒤 해볼 것.



