본문 바로가기
Web/Django

Static 파일 관리하기 with AWS S3 연동하기

by DUSTIN KANG 2023. 11. 27.

Static Files

웹사이트를 구현하려면 이미지나 CSS, Javascript 파일도 같이 제공해야한다. Django에서는 이런 파일을 staticfile이라고 하는데 통상적으로도 정적파일(Static File)이라 부르는 건 마찬가지다. 그럼 정적 파일은 어떻게 관리해야 할까?

가장 하단에 정적파일을 서비스할 수 있게 되어있다.

 

정적파일 구성에 관해서는 `settings.py` 하단 쯤에 다음과 같이 정의된 코드들을 볼 수 있다.

`STATIC_URL`은 정적 파일이 저장된 경로를 의미한다. 만약 template 태그로 `{% static 'css/style.css' %}`라고 설정해두면 `static/css/style.css` 경로로 호출될 것이다. 

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/

STATIC_URL = 'static/'

 

앱 내부 디렉토리를 사용하는 방법 말고도 다른 위치에서 정적 파일을 찾는 방법이 존재한다. `STATICFILES_DIRS`에 전체 경로를 포함하는 정적파일 위치를 담으면  `STATICFILES_FINDERS`가 정의한 순서대로 정적 파일을 찾게 된다.

  • `.FileSystemFinder` : `DIRS`경로에 정적 파일을 탐색하는 함수
  • `.AppDirectoriesFinder` : 앱 디렉토리 내부에 정적 파일을 탐색하는 함수
  • `.DefaultStorageFinder` : 기본 파일 저장소에서 정적 파일을 찾는 함수 공식문서 참조
STATICFILES_DIRS = [
    BASE_DIR / 'static', # 프로젝트 디렉토리의 static
    ("downloads", "/opt/webfiles/stats") # (네임스페이스, 경로)
]

STATICFILES_FINDERS = [
	'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder'
]

 

4.2버전 이전에는 `STATICFILES_STORAGE`를 통해 정적 파일을 수집할 수 있었지만 더 이상 사용하지 않는다. 대신 정적 파일 저장 엔진은 `STORAGES`에서 구성할 수 있다. 

  • default : 파일을 관리하기 위한 스토리지 엔진이다. 
  • staticfiles : 정적 파일을 관리하기 위한 스토리지 엔진이다. 
STORAGES = {
    # ...
    "default": {
        "BACKEND": "django.core.files.storage.FileSystemStorage",
        "OPTIONS": {
            "location": "/example",
            "base_url": "/example/",
        },
    "staticfiles": {
        "BACKEND": "django.core.files.storage.StaticFilesStorage",
        # MenifestStaticFilesStorage : 접두사에 'Manifest'를 추가해 MD5 해시로 파일 이름 새로 생성할 수 있다.
        # AWS S3 : 'storages.backends.s3boto3.S3Boto3Storage'
    },
}

 

 

Media File

이번엔 클라이언트 사용자가 제공한 미디어 파일을 관리하는 방법이다. 기존에 웹사이트를 구현하기 위해 필요한 정적 파일이 아니라 사용자에 의해 어떤 요청을 했을 때 생기는 미디어 파일이다. Static File 구성과 거의 비슷하다. 

우선, 사용자는 `FileField(upload_to="상대 경로")`나 `ImageField(upload_to="상대 경로")`를 통해 모델에 파일을 올릴 것이다.  이미지를 사용하고자 할땐 `Pillow`라이브러리를 설치해야 한다. 

 

그러면 업로드한 파일의 위치를 지정해주면 된다. `MEDIA_URL`은 빈값이라도 슬래시를 넣어줘야 한다.

MEDIA_ROOT = ''
MEDIA_URL = '/'

 

`MEDIA_ROOT`는 사용자가 업로드한 파일을 보관할 디렉토리 경로이다. 폴더 이름이라고 생각하면 된다. 

그리고 `MEDIA_URL`은 파일을 처리하기 위해 사용되는 URL이다. 만약 Template에 해당 파일을 올린다고 하면 템플릿 옵션에 다음 코드를 추가해주면 된다.

        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.media',
            ],

파일 관리

개발 모드의 경우 Django에 정적 파일을 제공하거나 사용자가 업로드한 파일을 제공할 때의 경우이다.

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

urlpatterns = [
    # ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

collectstatic

해당 정적 파일들을 배포할 때 `STATIC_ROOT`의 경로로 연결되어있는 정적 파일들을 한 곳으로 모을 수 있다. 그리고 `python manage.py findstatic`를 사용하면 모든 위치에 파일이 검색된다. 공식 문서 참조

python manage.py collectstatic

AWS S3와 Django 연동

이번엔 S3(Simple Storage Service)를 이용해 Django의 정적 파일과 이미지등을 관리하는 방법이다.

버킷은 다음과 같이 리전 단위로 존재하며 하나의 리전에서 여러개의 버킷을 만들 수 있다. 버킷(Bucket)은 리소스를 저장하는 공간을 말한다.

버킷은 lower-case, 3~64자로 유니크한 이름으로 지정한다.

 

버킷을 생성할 때 중요하게 생각하는 것은 버전관리, 퍼블릭 액세스 설정, 소유권 설정, 액세스 제어 설정이다.  그중에서 사용자가 버킷에 접근할 수 있는지를 판단하는 퍼블릭 액세스 설정은 기본적으로 차단되어 있다. 그렇기 때문에 접근 권한에 맞게 퍼블릭 액세스를 풀어야 해당 리소스에 접근할 수 있다.  

버킷에 접근하기 위한 퍼블릭 액세스 편집

 

이번엔 객체에 접근하기 위해 ACL를 설정해야한다. 

만약, 객체 ACL이 활성화되어 있지 않으면 해당 AWS 계정만 해당 객체에 접근이 가능하며 정책을 통해서만 접근할 수 있다. 해당 포스팅에서는 비활성화하여 버킷 정책에 대해서만 지정할 수 있게 했다.

만약, 위 그림처럼 ACL를 활성화했으면 액세스 제어 목록(ACL)에서 객체 권한을 부여해주어야 한다.

 

만일 객체 권한을 부여하지 않으면 객체에 접근했을 때 거절당하게 된다. 

 

 

설정이 끝났고, 아래 터미널을 입력해 AWS 서비스를 사용하기 위한 라이브러리를 설치한다.

설치가 완료되었으면 `INSTALLED_APPS`에 `storages`를 추가한다. 

pip install boto3 django-storages boto3

 

그리고 base.py에 다음과 같이 추가한다.

AWS_ACCESS_KEY_ID = '접근_키'
AWS_SCERET_ACCESS_KEY = '비밀_키'
AWS_STORAGE_BUCKET_NAME = '버킷_이름'
AWS_S3_REGION_NAME = '리전_이름' # 서울이면 ap-northeast-2
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com' # 버킷이름.s3.AWS리전.amazonaws.com 형식으로 작성


STATIC_URL = 'f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'


MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/' 
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

 

위와 같이 작성을 하였으면, `collectstatic`으로 모든 정적 파일을 모아준다.

python manage.py collectstatic                             

You have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: yes

130 static files copied.

 

 

s3에 들어가면 데이터가 S3로 제대로 옮겨진 것을 확인할 수 있다.

그러나, 여기서 문제가 있다. 실제로 프로젝트를 실행해보면 이미지가 깨지고 photo에 저장이 되지 않은 문제가 발생한다.

IAM를 사용하는 내가 버킷 정책을 추가하지 않아서 그렇다. 모든 사용자가 버킷을 읽지 못하기 때문에 아래 처럼 버킷 정책을 추가해준다.

 

 

이러면 사진이 제대로 불러왔음을 볼 수 있습니다.

 

 

참고로, AWS ElasticBeanStalk을 이용하면 해당 배포 파일은 S3에 저장된다.

'Web > Django' 카테고리의 다른 글

[Django] CRUD 구현  (0) 2023.11.29
User Model 커스텀 하기  (0) 2023.11.28
[Django] 개발환경 세팅하기  (0) 2023.11.22
[Django] 애플리케이션 요청 및 처리  (0) 2023.11.21
[Django] 데이터베이스 모델링  (0) 2023.11.18