장고 기초 #2 프로젝트 셋업 — uv + django-admin startproject
#1 Django 란에서 Django가 풀스택이라는 지점을 잡았습니다. 이번 글은 빈 디렉터리 → 동작하는 첫 페이지 까지의 한 호흡을 봅니다. 도구는 모던 파이썬 실전 #1에서 쓴 것과 같은 uv로 통일합니다.
사전 준비 — Python과 uv #
Django 5.x는 Python 3.10 이상을 요구합니다. 이 시리즈는 3.13으로 진행합니다. uv가 이미 있다면 건너뛰고, 없다면:
curl -LsSf https://astral.sh/uv/install.sh | shpowershell -c "irm https://astral.sh/uv/install.ps1 | iex"프로젝트 만들기 #
빈 디렉터리에 uv가상환경을 만들고 Django를 설치합니다.
mkdir myblog && cd myblog
uv init --python 3.13
uv add djangouv init이 만드는 것:
pyproject.toml— 프로젝트 메타데이터, 의존성.python-version— 사용할 Python 버전.venv/— 가상환경 (자동 생성됨, git에서 제외)
uv add django로 설치된 뒤 버전 확인:
uv run django-admin --version
# 5.1.x 같은 출력django-admin startproject — 프로젝트 골격 생성
#
Django의 모든 CLI 진입점은 두 개입니다 — django-admin (프로젝트 만들기 전), manage.py (만든 뒤). 첫 골격은 django-admin으로 만듭니다.
uv run django-admin startproject config .마지막의 .가 중요합니다. 안 쓰면 config/config/...처럼 한 단계 더 들어갑니다. .를 붙이면 현재 디렉터리에 직접 만들어집니다.
이름을 config로 한 이유 — mysite, myproject 같은 이름은 의미가 없고, 설정의 모음이라는 본질을 드러내는 게 좋습니다. 커뮤니티 컨벤션입니다.
만들어진 구조 #
myblog/
├── .python-version
├── .venv/
├── pyproject.toml
├── uv.lock
├── manage.py # Django CLI 진입점 (이제부터 모든 명령은 이걸로)
└── config/
├── __init__.py
├── settings.py # 모든 설정값
├── urls.py # 최상위 URL 라우팅
├── asgi.py # ASGI 진입점 (async, Channels 용)
└── wsgi.py # WSGI 진입점 (전통 동기 배포 용)각 파일의 쓰임:
manage.py—runserver,migrate,createsuperuser등 모든 명령을 이걸로settings.py— DB, 앱 목록, 미들웨어, 시크릿 키 등urls.py—/admin/,/posts/같은 최상위 URL 매핑asgi.py/wsgi.py— 서버 진입점. 개발 단계에선 직접 안 만짐
첫 실행 — runserver
#
uv run python manage.py runserver기본 포트 8000으로 뜹니다. 브라우저에서 http://127.0.0.1:8000을 열면 “The install worked successfully! Congratulations!” 라는 로켓 페이지가 보입니다.
처음 실행 시 콘솔에 빨간 글씨로 You have N unapplied migrations 같은 경고가 뜰 수 있습니다. 다음 단계에서 해결합니다.
포트 변경:
uv run python manage.py runserver 8080LAN 노출:uv run python manage.py runserver 0.0.0.0:8000
첫 마이그레이션 — built-in 앱들의 테이블 #
Django는 설치하자마자 내부 앱 (auth, sessions, admin, contenttypes, messages, staticfiles)을 활성화합니다. 이 앱들이 사용하는 테이블 (auth_user, django_session 등)을 DB에 만들어야 합니다.
uv run python manage.py migrate기본 DB는 SQLite이고, 프로젝트 루트에 db.sqlite3 파일이 자동으로 만들어집니다. PostgreSQL이나 MySQL로 바꾸는 건 중급 #6에서.
마이그레이션이 끝나면 콘솔에 Applying auth.0001_initial... OK 같은 로그가 줄줄이 찍히고, 더 이상 빨간 경고가 뜨지 않습니다.
첫 앱 — startapp blog
#
Django에서 app은 재사용 가능한 기능 단위 입니다. 한 project 안에 여러 app — 예: blog, accounts, payments.
uv run python manage.py startapp blog만들어지는 구조:
blog/
├── __init__.py
├── admin.py # Admin 등록 (#7)
├── apps.py # 앱 메타데이터
├── migrations/
│ └── __init__.py
├── models.py # 모델 정의 (#3)
├── tests.py # 테스트
└── views.py # 뷰 (#4)(템플릿 / static / urls 디렉터리는 자동으로 안 만들어집니다 — 직접 추가합니다. #4, #5에서.)
INSTALLED_APPS 등록
#
만든 app을 Django가 알아채려면 settings.py에 등록해야 합니다.
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog", # 추가
]리스트 맨 끝에 "blog" 한 줄. 더 정확히는 "blog.apps.BlogConfig" 라고 쓸 수도 있는데 (apps.py의 클래스), 짧게 "blog"만 써도 동작합니다.
settings.py 핵심 설정
#
처음 만져볼 만한 항목들:
# 보안 — 외부에 절대 노출 금지
SECRET_KEY = "django-insecure-..."
# 디버그 — 운영에서는 반드시 False
DEBUG = True
# DEBUG=False 일 때 어떤 호스트 허용할지
ALLOWED_HOSTS = []
# 설치된 앱
INSTALLED_APPS = [...]
# 미들웨어 (요청/응답 파이프라인)
MIDDLEWARE = [...]
# 최상위 URL 모듈
ROOT_URLCONF = "config.urls"
# 템플릿 설정
TEMPLATES = [...]
# 데이터베이스 — 기본 SQLite
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# 국제화
LANGUAGE_CODE = "ko-kr" # 기본 en-us → 한국어로
TIME_ZONE = "Asia/Seoul" # UTC → 서울
USE_I18N = True
USE_TZ = True # DB는 UTC로 저장, 표시할 때 변환
# 정적 파일 (#5)
STATIC_URL = "static/"**DEBUG=True**는 개발 전용입니다. 운영에 그대로 올리면 에러 페이지에 코드와 환경변수가 다 노출됩니다. 운영 배포는 고급 #7에서 다룹니다.
환경변수 분리 — django-environ
#
FastAPI #1에서 pydantic-settings로 한 것처럼, Django도 환경변수로 시크릿을 분리하는 게 표준입니다.
uv add django-environimport environ
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env(
DEBUG=(bool, False),
)
environ.Env.read_env(BASE_DIR / ".env")
SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["127.0.0.1", "localhost"])SECRET_KEY=django-insecure-replace-me-in-production
DEBUG=True
ALLOWED_HOSTS=127.0.0.1,localhost.gitignore에 .env 추가 필수입니다.
첫 view 한 번 — 시리즈의 출발점 #
blog/views.py에 가장 작은 view 한 줄:
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, blog!")blog/urls.py를 새로 만듭니다 (Django가 자동으로 안 만든다 했죠).
from django.urls import path
from . import views
app_name = "blog"
urlpatterns = [
path("", views.index, name="index"),
]최상위 config/urls.py에서 blog의 URL 들을 포함시킵니다.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]서버를 다시 띄우고 http://127.0.0.1:8000/blog/을 열면 **Hello, blog!**가 뜹니다.
URL / view의 더 자세한 패턴은 #4에서.
자주 쓰는 manage.py 명령 #
기억해 두면 좋은 명령들:
| 명령 | 용도 |
|---|---|
runserver | 개발 서버 실행 |
startapp <name> | 새 앱 생성 |
makemigrations | 모델 변경 → 마이그레이션 파일 생성 |
migrate | 마이그레이션 → DB 적용 |
createsuperuser | Admin 슈퍼유저 생성 (#7) |
shell | Django 컨텍스트의 Python REPL |
dbshell | DB shell 직접 |
collectstatic | static 파일 수집 (운영용, #5) |
check | 설정/모델 무결성 검사 |
test | 테스트 실행 (중급 #7) |
uv run python manage.py shell은 특히 자주 씁니다. 모델을 import 해서 ORM을 바로 만져볼 수 있습니다.
정리 #
이번 글에서 잡은 것:
uv init+uv add django로 의존성 분리django-admin startproject config .— 프로젝트 골격manage.py— 이후의 모든 명령runserver— 개발 서버, 자동 리로드migrate— built-in 앱들의 테이블 생성startapp blog— 첫 앱 생성,INSTALLED_APPS등록settings.py핵심 항목 —DEBUG,INSTALLED_APPS,DATABASES,LANGUAGE_CODE,TIME_ZONEdjango-environ으로 시크릿 분리,.env는 git 제외- 첫 view —
views.py+ 앱별urls.py+ 최상위include
다음 글(#3 Models와 ORM 기초)에서는 blog/models.py에 Post 모델을 정의하고, makemigrations → migrate 흐름과 함께 Django ORM의 QuerySet을 처음으로 만져봅니다.