[2026.01.26]
[2026.01.27]
[2026.01.28]
[2026.01.29]
[2026.01.30]
2026.01.26 ✅
serializers
| 비교 항목 | ModelSerializer | Serializer |
|---|
| 입력 데이터 형태 | [{"genre": 1}, {"genre": 2}] | {"genre_ids": [1, 2]} |
| 매핑 방식 | 1 JSON 객체 = 1 DB Row | 1 JSON 필드(List) = N DB Rows |
| 검증 로직 | 개별 객체 단위로 수행 (쿼리 N번 발생 가능) | 리스트 전체를 한 번에 검증 (id__in 사용 가능) |
| 생성 로직 | 기본적으로 save()를 N번 호출 (느림) | bulk_create로 한 번에 저장하기 쉬움 |
| 사용 목적 | 일반적인 CRUD (생성, 수정, 조회) | 특수 목적의 액션 (일괄 등록, 로그인, 이메일 발송 등) |
serializers.ModelSerializer
ModelSerializer 는 "DB 테이블 모양 그대로 데이터를 받고 싶을 때" 사용
- 입력받는 데이터의 형태와 저장되는 모델의 형태가 1:1로 매칭되지 않으면 사용하지 않음
[
{"genre": 1},
{"genre": 2},
{"genre": 3}
]
——————————————————————————————————————[비교]—————————————————————————————————————————
{
"genre_ids": [1, 2, 3]
}
학습
- serializer.validated_data
- 딕셔너리 형태(
{'genre_ids': [1, 2]})
constraints = [models.UniqueConstraint(...)]:
- 한 유저(user)가 동일한 장르(genre)를 중복해서 가질 수 없도록 제약조건 걸기
ignore_conflicts=True:
- Model에 UniqueConstraint를 추가했다면, 중복 데이터 삽입 시 에러가 발생함
- 이 옵션을 켜면 에러를 내지 않고, 중복된 건은 건너뛰고 새로운 건만 저장
2026/01/27 ✅
번역
googletrans
- Python에서 Google 번역 API를 무료로 사용할 수 있게 해주는 비공식 라이브러리
- 별도의 API 키 발급 없이 구글 번역기의 기능을 간편하게 코드에 구현할 수 있어,
- 가벼운 프로젝트나 프로토타입 개발에 자주 사용됨
특징
- 사용량 제한이 엄격하지 않고 무료
- 입력된 텍스트의 언어를 자동으로 식별
- 구글 번역기가 지원하는 100개 이상의 언어를 사용 가능
- 안정적인 동작을 위해서는 특정 버전(4.0.0-rc1)을 설치하는 것이 좋음
사용 예시
from googletrans import Translator
translator = Translator()
"""
1. Translator():
- 번역 작업을 수행하는 핵심 클래스 인스턴스를 생성합니다.
- 이 객체를 통해 translate()와 detect() 메서드를 호출합니다.
"""
text_to_translate = "Hello, I am studying coding."
result = translator.translate(text_to_translate, dest='ko')
"""
2. translator.translate(text, dest='ko'):
- text: 번역할 원본 문자열입니다.
- dest: 도착 언어(destination language) 코드입니다. 'ko'는 한국어를 의미합니다.
- src: 출발 언어(source language)는 생략 시 자동으로 감지(auto)됩니다.
"""
text_to_detect = "Bonjour tout le monde"
detection = translator.detect(text_to_detect)
"""
3. translator.detect(text):
- 입력된 텍스트가 어떤 언어인지 분석하여 반환합니다.
- 'fr'(프랑스어) 같은 언어 코드가 반환됩니다.
"""
print(f"번역 결과: {result.text}")
print(f"발음(발음이 가능한 경우): {result.pronunciation}")
print(f"감지된 언어: {detection.lang}")
"""
4. 반환 객체 (result):
- result.text: 번역된 최종 텍스트입니다.
- result.src: 원본 텍스트의 감지된 언어입니다.
- result.pronunciation: 번역된 텍스트의 발음 기호(로마자 표기)입니다.
"""
주요언어 코드
| 언어 (Language) | 코드 (Code) | 예시 (Example) |
|---|
| 한국어 | ko | 안녕하세요 |
| 영어 | en | Hello |
| 일본어 | ja | こんにちは |
| 중국어 (간체) | zh-cn | 你好 |
| 중국어 (번체) | zh-tw | 你好 |
| 프랑스어 | fr | Bonjour |
| 스페인어 | es | Hola |
2026/01/29 ✅
- 현재 페이지를 렌더링하는 데 SQL 쿼리가 몇 개나 실행되었는지
- 시간은 얼마나 걸렸는지 시각적으로 보여주는 강력한 도구
설치
- pip install django-debug-toolbar
settings
- INSTALLED_APPS, MIDDLEWARE, INTERNAL_IPS 세 가지를 설정
INSTALLED_APPS = [
'django.contrib.staticfiles',
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
INTERNAL_IPS = [
'127.0.0.1',
]
urls.py
- 프로젝트의 메인 urls.py 파일에 툴바 전용 URL을 연결
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
]
if settings.DEBUG:
import debug_toolbar
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
| 패널 이름 (Panel) | 주요 기능 | 쿼리 낭비 체크 포인트 |
|---|
| SQL | 실행된 모든 SQL 쿼리 조회 | Duplicated(중복) 횟수가 높은지 확인 비슷한 쿼리가 수십 개 반복된다면 N+1 문제 의심 |
| Time | CPU, 브라우저 처리 시간 분석 | SQL 처리 시간이 전체 응답 시간 중 얼마나 차지하는지 확인 |
| Templates | 템플릿 렌더링 과정 및 Context 확인 | 뷰(View)에서 넘겨준 데이터가 템플릿에서 올바르게 쓰이는지 확인 |
| Headers | 요청/응답 헤더 정보 | 캐싱(Cache) 설정이 제대로 동작하는지 헤더 값을 통해 확인 |
미들웨어 위치
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware",
"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",
]
도커 사용
- 도커 컨테이너 내부에서 실행될 때,
- 장고는 요청을 보낸 IP를 127.0.0.1이 아닌 도커 게이트웨이 IP(예: 172.17.0.1)로 인식함
- 그래서 단순히 127.0.0.1만 추가하면 툴바가 뜨지 않음
- 그래서 파이썬의 socket 라이브러리를 사용해
- 도커의 게이트웨이 IP를 동적으로 찾아 추가해주는 코드를 추가해야함
INTERNAL_IPS = [
"127.0.0.1",
]
import socket
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [ip[:-1] + "1" for ip in ips]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
]
INTERNAL_IPS = [
"127.0.0.1",
]
import socket
try:
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [ip[:-1] + "1" for ip in ips]
except Exception:
pass
- IP 검사를 아예 무시하고 "그냥 보여줘!"라고 강제하는 방식
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda request: True,
}
if DEBUG:
import mimetypes
mimetypes.add_type("application/javascript", ".js", True)
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda request: True,
}
| 구분 | 강제 설정 (아까 거절한 방식) | Socket 방식 (현재 방식) |
|---|
| 원리 | IP 검사를 아예 무시함 | IP를 정확히 찾아서 등록함 |
| 보안성 | 낮음 (실수로 켜두면 위험) | 높음 (지정된 네트워크만 허용) |
| 유연성 | 도커 IP가 바뀌어도 상관없음 | 도커 IP가 바뀌면 자동으로 감지해서 적용함 |
| 코드량 | 1줄 | 약 5~6줄 |
테스트 오류
- "SHOW_TOOLBAR_CALLBACK": lambda request: "test" not in sys.argv,
- 아래의 무조건 보여줘는 설정이 너무 강해서 위의 테스트시에는 꺼달라 필요
- 테스트(manage.py test)를 돌릴 때는 Django가 내부적으로 DEBUG = False로 설정을 잠시 바꿈
- 이때 urls.py에서 debug_toolbar의 URL들이 빠지게 되는데
- 툴바 미들웨어는 "무조건 보여줘" 설정 때문에 계속 작동하려고 하다가
- "어? 내 URL(djdt)들이 어디 갔지?" 하고 길을 잃어버려서 에러가 남
if DEBUG:
import mimetypes
mimetypes.add_type("application/javascript", ".js", True)
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda request: True,
"IS_RUNNING_TESTS": False,
}
최종 필요 코드
DJANGO_APPS = [
...
]
THIRD_PARTY_APPS = [
"rest_framework",
"drf_spectacular",
"debug_toolbar",
]
CUSTOM_APPS: list[str] = [
...
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + CUSTOM_APPS
MIDDLEWARE = [
...
"debug_toolbar.middleware.DebugToolbarMiddleware",
...
]
.
.
.
INTERNAL_IPS = [
"127.0.0.1",
]
if DEBUG:
import mimetypes
mimetypes.add_type("application/javascript", ".js", True)
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda request: "test" not in sys.argv,
"IS_RUNNING_TESTS": False,
}
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar
urlpatterns += [
path("__debug__/", include(debug_toolbar.urls)),
]
깃헙 올린 후 공지
쿼리 최적화를 위해 Django Debug Toolbar를 추가했습니다.
패키지 설정이 변경되었으니, 코드 pull 받으신 후 아래 두 가지를 꼭 진행해 주세요!
1. 로컬 환경 갱신 (빨간 줄 방지)
poetry install
2. 도커 서버 재빌드 (서버 에러 방지)
docker-compose up --build
도커 진행 방식
도커
- 왜 도커로 서버를 켜면 굳이 소켓이나 강제 설정을 해야할 까
- 서버를 도커로 켜게되면 Django는 사용자를 127.0.0.1로 인식하지 못함
- 그래서 도커 전용 설정(IP 추가 or 강제 표시)이 필수
예시
- 상황
- 배송과정
- 사용자(브라우저)가 아파트 경비실(Docker)에 택배를 맡깁니다. (주소: 127.0.0.1:8000)
- 경비 아저씨가 택배를 들고 친구의 집(Container)으로 가서 문을 두드립니다.
- 친구(Django)가 문을 열고 "누가 보냈어요?"라고 묻습니다.
- 이때, 문 앞에 서 있는 건 님이 아니라 경비 아저씨(Docker Gateway IP)입니다.
- 친구 입장에서 택배를 건네준 사람은 사용자(127.0.0.1)가 아니라 경비 아저씨(172.xx.xx.1)
| 구분 | 브라우저 (나)의 시각 | Django (서버)의 시각 |
|---|
| 접속 주소 | http://127.0.0.1:8000 | (요청을 받음) |
| 누가 요청했나? | 내가 (127.0.0.1) 보냄 | 도커(172.17.0.1)가 전달해 줌 |
| 사이트 접속 | 성공 (주소는 맞으니까) | 성공 (누가 줬든 페이지는 보여줌) |
| 툴바 표시 여부 | 당연히 보여야지? | 안 보여줌 (보낸 사람이 127.0.0.1이 아니네? 모르는 사람이다!) |