장고 기초 #2 프로젝트 셋업 — uv + django-admin startproject

5 분 소요

#1 Django 란에서 Django가 풀스택이라는 지점을 잡았습니다. 이번 글은 빈 디렉터리 → 동작하는 첫 페이지 까지의 한 호흡을 봅니다. 도구는 모던 파이썬 실전 #1에서 쓴 것과 같은 uv로 통일합니다.

사전 준비 — Python과 uv #

Django 5.x는 Python 3.10 이상을 요구합니다. 이 시리즈는 3.13으로 진행합니다. uv가 이미 있다면 건너뛰고, 없다면:

uv 설치 (macOS / Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh
uv 설치 (Windows)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

프로젝트 만들기 #

빈 디렉터리에 uv가상환경을 만들고 Django를 설치합니다.

프로젝트 디렉터리
mkdir myblog && cd myblog
uv init --python 3.13
uv add django

uv 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/
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.pyrunserver, 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 8080 LAN 노출: 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.

blog 앱 생성
uv run python manage.py startapp blog

만들어지는 구조:

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에 등록해야 합니다.

config/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 핵심 설정 #

처음 만져볼 만한 항목들:

config/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-environ
config/settings.py 상단
import 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"])
.env (git에서 제외)
SECRET_KEY=django-insecure-replace-me-in-production
DEBUG=True
ALLOWED_HOSTS=127.0.0.1,localhost

.gitignore.env 추가 필수입니다.

첫 view 한 번 — 시리즈의 출발점 #

blog/views.py에 가장 작은 view 한 줄:

blog/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, blog!")

blog/urls.py를 새로 만듭니다 (Django가 자동으로 안 만든다 했죠).

blog/urls.py
from django.urls import path

from . import views

app_name = "blog"

urlpatterns = [
    path("", views.index, name="index"),
]

최상위 config/urls.py에서 blog의 URL 들을 포함시킵니다.

config/urls.py
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 적용
createsuperuserAdmin 슈퍼유저 생성 (#7)
shellDjango 컨텍스트의 Python REPL
dbshellDB shell 직접
collectstaticstatic 파일 수집 (운영용, #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_ZONE
  • django-environ으로 시크릿 분리, .env는 git 제외
  • 첫 view — views.py + 앱별 urls.py + 최상위 include

다음 글(#3 Models와 ORM 기초)에서는 blog/models.pyPost 모델을 정의하고, makemigrationsmigrate 흐름과 함께 Django ORM의 QuerySet을 처음으로 만져봅니다.

X