- 발행일
중복 쿼리라고 뺀 열을 닷새 뒤 되돌렸다 — 측정 없이 뺐고, 측정 없이 되돌렸다
중복 쿼리라고 뺀 열을 닷새 뒤 되돌렸다
이 글은 2026년 9월에 당시 커밋 이력을 다시 보며 정리한 것입니다. 날짜는 작업한 날 기준입니다.
카탈로그 관리 화면에는 "등록 가능한 표준데이터" 표가 있었습니다. 카탈로그를 만들거나 고치는 폼 아래에 붙어서, 이 카탈로그의 분류체계에 속한 표준데이터를 보여 주는 표예요. 열은 여섯 개였습니다. 이름, 제목, 상태, 등록자, 분류체계, 카테고리.
이 표에서 분류체계 열을 뺐다가 닷새 뒤에 되돌렸습니다. 커밋 두 개가 서로를 정확히 지웁니다.
bd7e49ae 2025-09-19 19:01 fix : 카탈로그의 표준데이터 목록 테이블에서 분류체계 항목 제거(중복 쿼리 이슈) > catalog_item 누락 추가
870e44cf 2025-09-24 15:18 fix : 실제 운영 사이트에서 이상 없어서 롤백
diff는 둘 다 세 줄입니다. header_list, field_names, field_align에서 "분류체계"·"scheme"·"l"을 빼고, 닷새 뒤 그대로 다시 넣었어요.
뺀 이유는 내 것이 아니었다
커밋 제목 끝의 "catalog_item 누락 추가"가 사정을 말해 줍니다. 이 열을 빼자고 정한 건 제가 아니라 동료였어요.
| 시각 | 누가 | 무엇을 |
|---|---|---|
| 9월 9일 | 동료 | Dataset.scheme 프로퍼티 추가 — 첫 번째 개념의 분류체계 이름 |
| 9월 18일 22:08 | 동료 | 상세 화면 둘에서 분류체계 열 제거, 뷰에 prefetch 정리. 제목이 "중복 쿼리 이슈" |
| 9월 19일 09:02 | 동료 | Dataset.scheme 프로퍼티 자체를 삭제. concept은 prefetch 캐시를 읽도록 |
| 9월 19일 19:01 | 나 | 동료가 빠뜨린 관리 폼 템플릿에서 같은 열 제거 |
| 9월 24일 15:18 | 나 | 관리 폼 템플릿만 롤백. "실제 운영 사이트에서 이상 없어서" |
9월 9일에 들어온 프로퍼티는 이렇게 생겼습니다.
@property
def scheme(self):
"""첫 번째 concept의 scheme 이름을 반환"""
first_concept = self.concepts.select_related('scheme').first()
return first_concept.scheme.name if first_concept and first_concept.scheme else ""
표의 행마다 이 프로퍼티를 읽으니 행 수만큼 쿼리가 나갑니다. 동료는 그걸 "중복 쿼리"라고 불렀고, 열을 빼고 프로퍼티도 지웠어요. 저는 그 결정을 폼 화면에 옮겨 적었습니다. 당시 저는 select_related와 prefetch_related의 차이를 막 구분하기 시작한 때라, 동료가 문제라고 하면 문제였습니다.
되돌릴 때도 세지 않았다
닷새 뒤에 되돌린 이유는 커밋 메시지 한 줄이 전부입니다. "실제 운영 사이트에서 이상 없어서."
운영 화면을 열어 봤더니 느리지 않았다는 뜻으로 읽힙니다. 그런데 그때 저장소에는 쿼리를 세는 도구가 이미 있었어요.
def count_queries(label="Function Query Counter", show_queries=True, analyze_patterns=True):
def decorator(func):
def wrapper(*args, **kwargs):
is_enabled = getattr(settings, 'USE_SQL_PROFILE', False)
USE_SQL_PROFILE = True가 설정에 켜져 있었고, 카탈로그 상세 뷰의 get_context_data에는 이 데코레이터가 붙어 있었습니다. 열을 넣고 빼면서 콘솔에 찍히는 숫자를 한 번만 봤어도 됐어요. 그 기록이 커밋에도, 제 기억에도 없습니다. 뺄 때도 재지 않았고 되돌릴 때도 재지 않았습니다. 판단의 근거가 양쪽 다 "남이 그랬다"와 "보기에 괜찮다"였어요.
되돌린 열은 비어 있었다
이건 이번에 이력을 다시 보다가 알았습니다.
제가 열을 되돌린 9월 24일에는 Dataset.scheme 프로퍼티가 이미 없었습니다. 동료가 19일 아침에 지웠으니까요. 표 매크로는 data[field]로 값을 읽는데, 이 프로젝트의 Jinja 환경은 기본 Undefined를 씁니다. 없는 속성을 읽으면 예외가 아니라 빈 문자열이 나와요.
{%- if data[field] is not none %}
...
{%- else %}
{{ data[field] -}}
Undefined는 none이 아니므로 첫 조건을 통과하고, 출력은 빈 칸입니다. 그러니까 제가 되돌린 "분류체계" 열은 머리글만 있고 값은 한 줄도 없는 열이었습니다. 그 상태로 두 달을 갔고, 11월 19일에 동료가 그래프 연결 작업을 하면서 다시 뺐어요(62185fec). 그때까지 누가 그 빈 열을 봤는지, 제가 봤는지는 커밋에 남아 있지 않습니다.
"운영에서 이상 없어서"가 맞긴 했습니다. 빈 열은 쿼리를 안 만드니까요. 다만 제가 확인한 건 성능이 아니라 "화면이 뜬다"였고, 값이 나오는지는 안 봤습니다.
1년 뒤에는 세고 나서 고쳤다
같은 종류의 문제를 1년 뒤에 다시 만났습니다. 표준데이터 목록에서 카드 한 장마다 쿼리가 붙던 것을 39개에서 19개로 줄였는데, 그때는 먼저 세고, 어느 줄이 만드는지 찾고, 고친 뒤 다시 셌어요. 2025년 9월의 저와 다른 점은 도구가 아닙니다. 도구는 그때도 있었어요. 다른 건 "느리다"를 숫자로 바꾸고 나서 손대는 순서입니다.
검증
당시 검증 기록은 없습니다. 아래는 이 글을 쓰면서 확인한 것들이에요.
bd7e49ae와870e44cf의 diff가 같은 세 줄을 정확히 반대로 바꾸는 것.- 동료의
b5d82ed9(9/18)가 상세 템플릿 둘과 뷰 둘을 고치고 관리 폼 템플릿은 안 건드린 것 — "누락"의 뜻. 1800dcc4(9/19 09:02)에서def scheme이std_data/models.py에서 지워진 것. 그 뒤로Dataset에scheme속성이 다시 생긴 적 없음.sdms/jinja2.py의Environment(**options, line_statement_prefix="%")에undefined지정이 없어 기본Undefined인 것.62185fec(11/19)에서 같은 템플릿의 열이 다시 빠진 것. 현재 브랜치의field_names에scheme없음.
남은 것 · 한계
- 되돌린 열이 두 달간 비어 있었다는 건 지금의 추론입니다. 당시 화면 캡처가 없어요. Jinja 기본
Undefined의 동작으로 미루어 그렇다고 보는 것이고, 누군가 그 열을 보고 이상하다고 말한 기록도 없습니다. - 실제 쿼리 수는 그때도 지금도 모릅니다. 9월 9일판 프로퍼티가 행마다 한 번씩 조회했을 거라는 것까지만 코드로 말할 수 있어요.
- 동료의 원래 판단("중복 쿼리")이 맞았는지도 재지 않았습니다. 프로퍼티를 지웠으니 열을 빼는 건 어차피 필요했고, 순서가 반대였다면 다른 이야기였을 겁니다.
- 이 표는 이후 개념체계 용어 변경, Tailwind 전환을 거치며 모양이 바뀌었지만 열 구성은 "이름·제목·상태·등록자·카테고리" 다섯으로 남아 있습니다.
관련 글: Django N+1 쿼리 문제와 해결법 · 같은 표준데이터를 두 화면이 198건과 134건으로 세고 있었다 · 트리에서 뺀 신청 상태 표시를 일주일 뒤 되살렸다