사전 Django
구현체

Django

gabury1고친 사람 github-actions[bot]

Django 는 파이썬으로 웹 서비스를 만들 때 매번 다시 짜게 되는 부분을 대신 맡아 줍니다. 주소를 코드에 잇는 일부터 화면을 그리는 일까지 한 벌로 들어 있습니다. 만드는 사람은 그 위에 자기 서비스에만 있는 규칙을 얹습니다.

쉽고 빠른 이해

Django 는 웹 서비스의 뼈대를 미리 만들어 둔 도구입니다. 게시판을 예로 들면 글 목록 주소를 받는 부분, 글을 데이터베이스에서 꺼내는 부분, 목록 화면을 그리는 부분이 처음부터 있습니다.

이 셋은 어떤 서비스를 만들어도 다시 나옵니다. 매번 새로 짜면 서비스마다 구조가 달라집니다. 보안 처리를 빠뜨리기도 쉽습니다. Django 는 그 부분을 정해진 방법 하나로 묶어 둡니다.

어떻게 도나:

  1. 요청이 오면 주소를 보고 맡은 함수를 고릅니다
  2. 그 함수가 데이터를 꺼내거나 고칩니다. 데이터베이스의 표를 파이썬 클래스로 다룹니다
  3. 화면 틀에 값을 끼워 응답을 돌려줍니다

표가 여럿이고 그것을 사람이 들여다보며 관리하는 서비스에 맞습니다. 대신 Django 가 정한 방식을 따라야 합니다. 데이터를 다루는 부분을 다른 것으로 갈아 끼우면 관리 화면과 로그인처럼 딸려 오던 기능이 함께 떨어져 나갑니다. 기능 하나뿐인 작은 서비스에는 갖춰진 것이 짐이 됩니다.

상세

Django 는 파이썬으로 웹 서비스를 만들 때 쓰는 웹 프레임워크입니다. 내려받아 프로젝트 하나를 만들면 주소 처리와 데이터베이스 접근, 화면 생성, 로그인이 함께 따라옵니다. 아래 소절은 게시판 하나를 만든다고 치고 그때 마주치는 것을 순서대로 풉니다.

라이브러리와 프레임워크의 차이

라이브러리는 필요할 때 불러 쓰는 코드 묶음입니다. 날짜를 다루는 라이브러리라면 내 프로그램이 흐름을 쥐고 있다가 필요한 줄에서 함수를 부릅니다.

프레임워크는 방향이 반대입니다. 흐름은 프레임워크가 쥡니다. 내가 쓴 코드는 정해진 곳에 끼워 넣는 부품이 됩니다. 요청이 들어오면 Django 가 내 함수를 불러 줍니다. 이렇게 부르는 방향이 뒤집힌 것을 제어 역전이라고 부릅니다.

flowchart TD
    subgraph L["라이브러리를 쓸 때"]
        L1["내 코드"] -- 부른다 --> L2["라이브러리"]
    end
    subgraph F["프레임워크를 쓸 때"]
        F1["Django"] -- 부른다 --> F2["내 코드"]
    end

그래서 프레임워크에는 규칙이 많습니다. 파일을 어디에 두고 이름을 어떻게 짓는지가 미리 정해져 있습니다. 대신 정해진 곳에 놓기만 하면 나머지 연결은 Django 가 합니다.

규칙을 받아들이면 되풀이가 줍니다. 주소를 코드에 잇는 부분, 데이터를 꺼내는 부분, 화면을 그리는 부분은 어느 웹 서비스를 만들어도 다시 나옵니다. 서비스마다 새로 짜면 구조가 제각각이 됩니다. 보안 처리를 빠뜨리기도 쉽습니다.

프로젝트와 앱

Django 로 만드는 서비스 하나가 프로젝트입니다. 프로젝트에는 설정 파일이 하나 있습니다. 어떤 데이터베이스를 쓰는지, 어떤 기능을 켤지가 그 파일에 모입니다.

프로젝트 안은 앱이라는 단위로 나뉩니다. 앱 하나는 게시판이나 결제처럼 한 갈래 기능을 담는 폴더입니다. 앱마다 자기 코드와 화면 틀을 자기 폴더에 들고 있어서 다른 프로젝트로 폴더째 옮겨 쓸 수 있습니다.

flowchart TD
    subgraph P["프로젝트 · 서비스 하나"]
        S["설정 파일"]
        subgraph A1["앱 · 게시판"]
            C1["코드"]
            H1["화면 틀"]
        end
        A2["앱 · 결제"]
    end
    S -. 앱 목록에 등록 .-> A1
    S -. 앱 목록에 등록 .-> A2

굵은 상자 안에 든 것은 그 폴더에 담긴다는 뜻입니다. 점선은 담는 관계가 아니라 가리키는 관계입니다. 설정 파일의 앱 목록에 이름을 올려야 Django 가 그 앱을 읽습니다. 올리지 않은 폴더는 파일이 다 갖춰져 있어도 없는 것으로 칩니다.

요청이 지나가는 길

브라우저에서 요청이 들어오면 Django 는 정해진 순서로 넘깁니다. 아래 상자에 적힌 다섯 낱말은 그림 다음에 하나씩 풉니다.

flowchart TD
    A[브라우저 요청] --> B[미들웨어]
    B --> C[URL 설정]
    C --> D[뷰 함수]
    D --> E[모델 · 데이터베이스]
    E --> D
    D --> F[템플릿]
    F --> G[응답]
    G --> B
    B --> H[브라우저로]

미들웨어는 요청과 응답이 반드시 지나가는 중간 처리 묶음입니다. 로그인한 사람인지 확인하거나, 여러 요청에 걸쳐 같은 사용자를 기억하는 세션을 붙이는 일이 그 안에서 일어납니다. 모든 요청에 똑같이 걸어야 하는 일을 한군데 모으는 것이 미들웨어입니다.

그다음은 URL(Uniform Resource Locator, 웹 주소)과 코드를 잇는 목록인 URL 설정입니다. /posts/ 같은 주소 모양을 적고 그 옆에 부를 함수 이름을 적습니다. 위에서부터 맞춰 보다가 처음 맞는 줄에서 멈춥니다.

요청을 받아 응답을 돌려주는 그 함수를 Django 에서는 뷰라고 부릅니다. 이름과 달리 화면을 그리는 함수가 아닙니다. 무엇을 보여줄지 정하는 함수입니다. 화면 모양은 다음에 오는 화면 틀이 맡습니다.

이 이름은 MVC(Model-View-Controller, 모델-뷰-컨트롤러)라는 오래된 구분과 어긋납니다. 거기서 뷰는 화면을 그리는 쪽입니다. 요청을 받아 무엇을 보여줄지 고르는 쪽은 컨트롤러라고 부릅니다.

Django 는 그 고르는 일을 뷰에 맡깁니다. 요청을 어느 뷰로 넘길지는 프레임워크 자신이 합니다. 그래서 자기 구조를 MTV(Model-Template-View, 모델-템플릿-뷰)라고 부릅니다.

템플릿은 값이 들어갈 구멍을 뚫어 둔 화면 틀입니다. 뷰가 넘긴 값이 그 구멍에 끼워지면 브라우저가 읽는 HTML(HyperText Markup Language, 하이퍼텍스트 마크업 언어)이 완성됩니다.

모델과 데이터베이스

모델은 데이터베이스의 표 하나를 파이썬 클래스로 적은 것입니다. 클래스 안에 적은 항목 하나가 표의 열 하나가 됩니다.

Python
class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

마지막 줄은 열 하나가 다른 모델을 가리키게 한 것입니다. 글 하나에 글쓴이 하나가 붙는다는 뜻이고, 데이터베이스에서는 표와 표를 잇는 외래 키가 됩니다.

이 클래스를 적어 두면 표를 만들고 읽고 쓰는 일은 Django 가 합니다. 데이터베이스에 보내는 SQL(Structured Query Language, 구조화 질의 언어) 문장을 직접 쓰지 않습니다. 이렇게 표의 행을 프로그래밍 언어의 객체로 바꿔 주는 층이 ORM(Object-Relational Mapping, 객체 관계 매핑)입니다.

읽을 때는 모델에 딸린 조회 도구를 씁니다. 코드에서는 모델 뒤에 objects 라는 이름으로 붙어 있습니다. 조회 조건을 담아 두는 물건이 쿼리셋입니다.

Python
Post.objects.filter(id=1)   # 쿼리셋
Post.objects.count()        # 12

주석은 그 줄이 내는 값입니다. 첫 줄은 조건만 담은 쿼리셋을 돌려줍니다. 둘째 줄은 글 수를 센 숫자를 돌려줍니다.

두 줄이 갈리는 곳이 여기입니다. 쿼리셋은 만들자마자 데이터베이스에 묻지 않습니다. 위의 filter 줄이 그렇습니다. 값이 정말 필요해지는 줄에서 한 번에 묻습니다. 숫자를 바로 내야 하는 count 줄이 그때입니다.

sequenceDiagram
    participant 앱
    participant 쿼리셋
    participant 데이터베이스
    앱->>쿼리셋: 조건을 붙인다
    앱->>쿼리셋: 조건을 하나 더 붙인다
    Note over 앱,쿼리셋: 여기까지 데이터베이스로 안 간다
    앱->>쿼리셋: 값을 쓴다
    쿼리셋->>데이터베이스: 질의 한 번
    데이터베이스-->>쿼리셋: 결과
    쿼리셋-->>앱: 값

덕분에 조건을 여러 줄에 나눠 붙여도 데이터베이스에는 한 번만 갑니다.

이 늦춤이 거꾸로 문제를 만들기도 합니다. 글 목록을 돌면서 글마다 post.author 로 글쓴이를 꺼내면, 글 하나마다 글쓴이 표에 묻는 질의가 따로 나갑니다. 목록 질의 한 번에 글 수만큼이 더 붙습니다. 이것이 N+1 문제입니다. 이어진 모델을 처음부터 함께 가져오라고 알려 주는 select_related 가 그 해결책입니다.

모델을 고칠 때의 마이그레이션

서비스를 굴리다 보면 담을 항목이 늘어납니다. 그런데 이미 데이터가 들어 있는 표는 클래스만 고친다고 따라 바뀌지 않습니다.

마이그레이션은 표를 어떻게 바꿀지 적어 둔 파일입니다. Django 가 모델 클래스와 지금 표의 차이를 읽어 이 파일을 만들어 줍니다.

터미널
python manage.py makemigrations  # 파일 하나 생김
python manage.py migrate         # 표에 반영됨

두 명령은 도는 기계가 다릅니다. 앞엣것은 모델을 고친 사람의 컴퓨터에서 한 번 돕니다. 뒤엣것은 그 파일을 받은 기계마다 각각 돕니다.

flowchart TD
    A["모델 클래스를 고침"] --> B["makemigrations"]
    B --> C["마이그레이션 파일"]
    C --> D["저장소"]
    D --> E["다른 개발자 기계 · migrate"]
    D --> F["운영 서버 · migrate"]

그래서 이 파일은 코드와 함께 저장소에 넣습니다. 그러면 다른 개발자의 컴퓨터와 운영 서버가 같은 변경을 같은 순서로 겪습니다. 표의 모양이 기계마다 갈리는 일을 이렇게 막습니다.

딸려 오는 관리 화면

모델을 관리 화면에 등록하면 그 표를 읽고 고치고 지우는 웹 화면이 저절로 생깁니다. 이 화면이 관리 화면입니다.

여기에는 로그인한 직원만 들어갑니다. 계정과 권한을 다루는 기능이 Django 에 함께 들어 있어서 따로 만들지 않아도 됩니다.

운영하는 사람이 값을 직접 고칠 수 있으니 서비스 초기에 손이 덜 갑니다. 다만 일반 사용자에게 내보낼 화면은 아닙니다. 표의 모양과 열 이름이 그대로 드러나기 때문입니다.

기본으로 켜져 있는 보안 장치

웹 서비스에서 되풀이되는 공격 몇 가지를 Django 는 처음부터 막아 둡니다.

모델로 만든 질의는 문장과 값을 따로 보냅니다. 사용자가 넣은 글자가 질의문의 일부로 해석되지 않습니다. 남의 입력이 질의문을 바꿔치기하는 SQL 인젝션이 이렇게 막힙니다.

템플릿은 값에 든 태그 기호를 글자로 바꿔 내보냅니다. 남이 써 넣은 스크립트가 다른 사람 브라우저에서 실행되는 크로스 사이트 스크립팅을 막습니다.

입력 칸을 채워 서버로 보내는 화면이 폼입니다. 폼을 보낼 때는 예측할 수 없는 값 하나를 함께 보내게 합니다. 다른 사이트가 사용자 몰래 요청을 보내는 크로스 사이트 요청 위조를 막습니다. 이 값이 없는 요청은 뷰에 닿기 전에 미들웨어가 되돌립니다.

장치가 켜져 있다고 모든 공격이 막히지는 않습니다. 장치를 비켜서게 하는 줄이 둘 있습니다. 질의문을 직접 문자열로 이어 붙이는 줄이 하나입니다. 템플릿에서 그 값을 손대지 말고 내보내라고 표시하는 줄이 다른 하나입니다.

포기한 것

Django 는 갖춰 주는 대신 몇 가지를 내려놓았습니다.

포기한 것 무엇이 곤란해지나
부품만 따로 쓰기 관리 화면·로그인·폼 검증이 Django 의 모델을 전제로 만들어져 있습니다. 데이터 층을 다른 것으로 바꾸면 이들이 함께 떨어져 나갑니다
표가 아닌 저장소 모델은 표와 행을 전제로 합니다. 문서나 그래프로 담는 저장소를 주 저장소로 삼으면 모델이 주는 것을 대부분 못 씁니다
처음부터 비동기 한 요청을 한 흐름이 처음부터 끝까지 처리하는 방식으로 출발했습니다. 뒤에 비동기 처리가 더해졌지만 비동기로 도는 부분과 동기로 도는 부분이 섞입니다
부품을 직접 고르는 자유 화면 틀과 데이터 층을 하나씩 골라 조립하는 대신 정해진 묶음을 그대로 받습니다. 취향에 맞지 않는 부품도 바꾸려면 붙어 있는 것까지 건드려야 합니다
요청 안에서 오래 걸리는 일 메일 보내기나 영상 변환은 요청을 붙들고 처리하지 않습니다. 나중에 처리할 일을 쌓아 두는 작업 큐를 옆에 따로 세웁니다. Celery 가 그 도구입니다

쓰는 곳과 피하는 곳

고를 때 짚어 볼 것이 넷입니다.

flowchart TD
    A["주 저장소가 관계형 표인가"] -- 아니오 --> B["다시 생각한다"]
    A -- 예 --> C["화면과 데이터가 함께 있나"]
    C -- 아니오 --> D["Flask · FastAPI"]
    C -- 예 --> E["연결을 길게 붙드나"]
    E -- 예 --> F["비동기를 전제로 만든 도구"]
    E -- 아니오 --> G["Django"]

화면과 데이터가 함께 있는 서비스에 맞습니다. 사내 업무 도구, 쇼핑몰, 기사 발행 도구처럼 표가 여럿이고 그것을 사람이 들여다보며 관리해야 하는 서비스입니다.

주소 하나로 값만 돌려주는 작은 서비스에는 갖춰진 것이 대부분 남습니다. 그럴 때는 더 작은 Flask 나 FastAPI 를 씁니다.

채팅처럼 연결을 길게 붙들고 메시지를 주고받는 서비스도 잘 맞지 않습니다. FastAPI 처럼 비동기를 처음부터 전제로 만든 도구가 이런 일에는 덜 어긋납니다. Django 로 하려면 Django Channels 를 따로 얹습니다.

주 저장소를 표가 아닌 것으로 정해 두었다면 다시 생각해 봅니다. 모델과 관리 화면을 못 쓰면 Django 를 고를 이유가 대부분 사라집니다.

관련 항목

Django 가 올라타는 바탕

Python · WSGI · ASGI · HTTP · gunicorn · nginx

Django 를 이루는 구성 요소

모델 · 뷰 · 템플릿 · URL 설정 · 미들웨어 · 폼 · Django ORM · 쿼리셋 · 마이그레이션 · 관리자 화면 · 시그널 · 세션

Django 가 값을 맡기는 데이터베이스

PostgreSQL · MySQL · SQLite · 관계형 데이터베이스 · 커넥션 풀 · 외래 키

Django 가 기본으로 막는 공격

SQL 인젝션 · 크로스 사이트 요청 위조 · 크로스 사이트 스크립팅 · 클릭재킹

Django 와 같은 역할을 두고 겨루는 웹 프레임워크

Flask · FastAPI · Rails · Spring Boot · Laravel · Express

Django 가 옆으로 넘기는 일과 그 도구

Celery · Redis · Django REST framework · Django Channels · 캐싱 · cache-aside · 정적 파일

Django 에서 자주 터지는 성능 문제

N+1 · 지연 로딩 · 데이터베이스 인덱스 · 슬로 쿼리

Django 가 속하는 상위 분류

웹 프레임워크 · 프레임워크 · 백엔드 · MTV · MVC · 제어 역전 · 라이브러리

다른 이름: 장고