django에서 collectstatic 명령으로 static 파일 수집시 에러

안녕하세요, django를 공부하는 중에 해결이 안되는 부분이 있어서 여쭤봅니다!

점프 투 장고 에서 python manage.py collectstatic, 명령어를 이용해 django 웹서비스에 admin으로 접속할 때 CSS파일을 가져올 수 있게 하려고 하면... 예외가 발생하는데요,
일단 전 현재 Ubuntu 20.04.4 버전을 사용하고 있고 '점프 투 장고' 책에서 사용한 NginxGunicorn을 사용하고있습니다.

에러가 발생하는 부분을 살펴보자면,

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
Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/core/management/base.py", line 414, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 209, in handle
    collected = self.collect()
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 135, in collect
    handler(path, prefixed_path, storage)
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 368, in copy_file
    if not self.delete_file(path, prefixed_path, source_storage):
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 278, in delete_file
    if self.storage.exists(prefixed_path):
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/core/files/storage.py", line 362, in exists
    return os.path.lexists(self.path(name))
  File "/home/devadmin/venvs/bio_platform/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 39, in path
    raise ImproperlyConfigured(
django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path.

구글링도 해보고 STACKOVERFLOW도 뒤져봤는데요, STATIC_ROOT가 제대로 설정되어있지 않을때 발생하는 예외라고 하더군요,

일단 프로젝의 디렉토리 구성은 아래와 같습니다 :

└── bio_platform
    ├── common
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-310.pyc
    │   │   ├── __init__.cpython-38.pyc
    │   │   ├── __init__.cpython-39.pyc
    │   │   ├── admin.cpython-310.pyc
    │   │   ├── admin.cpython-38.pyc
    │   │   ├── admin.cpython-39.pyc
    │   │   ├── apps.cpython-310.pyc
    │   │   ├── apps.cpython-38.pyc
    │   │   ├── apps.cpython-39.pyc
    │   │   ├── forms.cpython-310.pyc
    │   │   ├── forms.cpython-38.pyc
    │   │   ├── forms.cpython-39.pyc
    │   │   ├── models.cpython-310.pyc
    │   │   ├── models.cpython-38.pyc
    │   │   ├── models.cpython-39.pyc
    │   │   ├── urls.cpython-310.pyc
    │   │   ├── urls.cpython-38.pyc
    │   │   ├── urls.cpython-39.pyc
    │   │   ├── views.cpython-310.pyc
    │   │   ├── views.cpython-38.pyc
    │   │   └── views.cpython-39.pyc
    │   ├── admin.py
    │   ├── apps.py
    │   ├── forms.py
    │   ├── migrations
    │   │   ├── __init__.py
    │   │   └── __pycache__
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── config
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-310.pyc
    │   │   ├── __init__.cpython-38.pyc
    │   │   ├── __init__.cpython-39.pyc
    │   │   ├── settings.cpython-310.pyc
    │   │   ├── settings.cpython-38.pyc
    │   │   ├── settings.cpython-39.pyc
    │   │   ├── urls.cpython-310.pyc
    │   │   ├── urls.cpython-38.pyc
    │   │   ├── urls.cpython-39.pyc
    │   │   ├── wsgi.cpython-310.pyc
    │   │   ├── wsgi.cpython-38.pyc
    │   │   └── wsgi.cpython-39.pyc
    │   ├── asgi.py
    │   ├── settings
    │   │   ├── __pycache__
    │   │   ├── base.py
    │   │   ├── local.py
    │   │   └── prod.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    ├── manage.py
    ├── node_modules
    │   └── bootstrap
    │       ├── LICENSE
    │       ├── README.md
    │       ├── dist
    │       ├── js
    │       ├── package.json
    │       └── scss
    ├── package-lock.json
    ├── pybo
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-310.pyc
    │   │   ├── __init__.cpython-38.pyc
    │   │   ├── __init__.cpython-39.pyc
    │   │   ├── admin.cpython-310.pyc
    │   │   ├── admin.cpython-38.pyc
    │   │   ├── admin.cpython-39.pyc
    │   │   ├── apps.cpython-310.pyc
    │   │   ├── apps.cpython-38.pyc
    │   │   ├── apps.cpython-39.pyc
    │   │   ├── forms.cpython-310.pyc
    │   │   ├── forms.cpython-38.pyc
    │   │   ├── forms.cpython-39.pyc
    │   │   ├── models.cpython-310.pyc
    │   │   ├── models.cpython-38.pyc
    │   │   ├── models.cpython-39.pyc
    │   │   ├── urls.cpython-310.pyc
    │   │   ├── urls.cpython-38.pyc
    │   │   ├── urls.cpython-39.pyc
    │   │   └── views.cpython-310.pyc
    │   ├── admin.py
    │   ├── apps.py
    │   ├── forms.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   ├── 0002_question_author.py
    │   │   ├── 0003_answer_author.py
    │   │   ├── 0004_answer_modify_date_question_modify_date.py
    │   │   ├── 0005_comment.py
    │   │   ├── 0006_answer_voter_question_voter_alter_answer_author_and_more.py
    │   │   ├── 0007_auto_20220411_1325.py
    │   │   ├── __init__.py
    │   │   └── __pycache__
    │   ├── models.py
    │   ├── templatetags
    │   │   ├── __pycache__
    │   │   └── pybo_filter.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views
    │       ├── __pycache__
    │       ├── answer_views.py
    │       ├── base_views.py
    │       ├── comment_views.py
    │       ├── question_views.py
    │       └── vote_views.py
    ├── static
    │   ├── bootstrap.min.css
    │   ├── bootstrap.min.js
    │   ├── jquery-3.6.0.min.js
    │   └── style.css
    ├── templates
    │   ├── base.html
    │   ├── common
    │   │   ├── login.html
    │   │   └── signup.html
    │   ├── form_errors.html
    │   ├── navbar.html
    │   └── pybo
    │       ├── answer_form.html
    │       ├── comment_form.html
    │       ├── question_detail.html
    │       ├── question_form.html
    │       └── question_list.html
    └── winehq.key

점프 투 장고에 나오는 것 처럼 세팅파일은 아래와 같이 분리해둔 상태고,

├── settings
    │   │   ├── __pycache__
    │   │   ├── base.py
    │   │   ├── local.py
    │   │   └── prod.py

아래 세팅파일들의 풀 코드를 공유해드립니다. (IP부분은 블랭크 처리했습니다.).

base.py

"""
Django settings for config project.

Generated by 'django-admin startproject' using Django 4.0.3.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'blank' #private part

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['blank'] #private part


# Application definition

INSTALLED_APPS = [
    'common.apps.CommonConfig',
    'pybo.apps.PyboConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        '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',
            ],
        },
    },
]

WSGI_APPLICATION = 'config.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'ko-kr'

TIME_ZONE = 'Asia/Seoul'

USE_I18N = True

USE_TZ = True


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

STATIC_URL = 'static/'
#STATIC_ROOT = os.path.join(BASE_DIR,'static')

STATICFILES_DIRS = [    
    BASE_DIR / 'static',
    ]

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

local.py

from .base import *

ALLOWED_HOSTS = []

prod.py

from .base import *
import os

ALLOWED_HOSTS = ['blank'] #private part
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
#STATIC_ROOT = BASE_DIR / 'static/'
STATICFILES_DIRS = []
# concept addon

일단 추측으로는 STATIC ROOT를 설정해도 경로를 제대로 인식하지 못하고 있는 것이라 생각하고 이것 저것 바꿔보고 있는데, 신통치는 않더군요.
아래는 nignx 서버 자동 구성을 위한 bio_platform.service 파일의 코드입니다.

bio_platform.service

server {
    listen 80;
    server_name blank; #private part

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static {
        alias /home/devadmin/projects/bio_platform/static;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/tmp/gunicorn.sock;
    }
}

일단 구글링해서 시도해본 방법으로는:

내가 해본 방법

STATIC_ROOT 하고 STATIC_URL 변수를 prod.py에 지정해주기,
STATIC_ROOT 하고 STATIC_URL 변수를 base.py에 지정해주기,

등인데요, import os를 통해서 변수를 지정하는 방법도 있고 이것저것 해봤는데 django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path. 는 여전히 절 괴롭히더군요...

이제는 STATIC_ROOT 또는 STATIC_URL, STATICFILES_DIR 변수를 정확히 어디에 집어넣어야 해결될지 감이 잡히지 않습니다...
혹시 비슷한 경우를 겪으신 분이 있을까요?

lackdoy 1736

M 2022년 4월 20일 10:47 오전

@박응용님 안녕하세요 선생님! 댓글주셔서 감사합니다~ T^T 보내주신 링크는... 아마 제가 제일 처음으로 시도해본 방법 같네요.. 책에도 있는 부분이고, Github에서 직접 코드 복사해서 붙여넣기도 해봤는데 예외사항은 여전하더군요! 그래도 일단 다시 한 번 해보겠습니다! - lackdoy님, 2022년 4월 20일 10:54 오전 추천 , 대댓글
+1 python manage.py collectstatic 수행시 설정파일로 prod.y 파일을 사용해야 하는데 그렇지 못한 케이스로 보이네요. 환경변수등을 확인해 보세요. - 박응용님, 2022년 4월 20일 10:54 오전 추천 , 대댓글
+1 @lackdoy님 https://wikidocs.net/75560#_2 여기 참고하여 서버 환경변수 설정을 확인해 보세요. - 박응용님, 2022년 4월 20일 10:56 오전 추천 , 대댓글
@박응용님 감사합니다 선생님! 말씀하신 환경변수 설정 원인이 맞았습니다. ubuntu 환경에서 설정해둔 mysite.sh 파일에서 prod 를 불러오는 경로의 철자가 잘못되어 있었습니다! 정말 감사합니다! - lackdoy님, 2022년 4월 20일 2:05 오후 추천 , 대댓글
python manage.py collectstatic --settings=config.settings.prod 저는 위와 같이 명시적으로 --settings 옵션을 사용하였습니다. - purityboy님, 2024년 5월 27일 12:36 오후 추천 , 대댓글
목록으로