관리자 패널 인덱싱: 자주 쓰는 필터를 먼저 최적화하세요
관리자 패널 인덱싱: 사용자가 가장 자주 클릭하는 필터(상태, 담당자, 날짜 범위, 텍스트 검색)를 실제 쿼리 패턴에 따라 우선 최적화하세요.

관리자 패널 필터가 느려지는 이유
관리자 패널은 처음에는 빠르게 느껴집니다. 리스트를 열고 스크롤하고 레코드를 클릭하면 금방 끝납니다. 느려짐은 사람들이 실제로 작업하는 방식대로 필터링할 때 나타납니다: "열린 티켓만", "Maya에게 할당된 항목", "지난주에 생성된 것", "주문 ID에 1047 포함"처럼요. 클릭할 때마다 대기 시간이 생기고 리스트가 끈적거리기 시작합니다.
같은 테이블이라도 어떤 필터에서는 빠르고 다른 필터에서는 극도로 느릴 수 있습니다. 상태 필터는 작은 행 집합만 건드려 빠르게 반환될 수 있습니다. 반면 "두 날짜 사이에 생성됨" 필터는 데이터베이스가 큰 범위를 읽게 만들 수 있습니다. 담당자 필터는 단독으로는 괜찮다가 상태와 정렬이 결합되면 느려집니다.
인덱스는 데이터베이스가 전체 테이블을 읽지 않고 일치하는 행을 찾는 지름길입니다. 하지만 인덱스는 공짜가 아닙니다. 저장 공간을 차지하고 삽입 및 갱신을 약간 느리게 합니다. 너무 많이 추가하면 쓰기가 느려지고 실제 병목을 해결하지 못할 수 있습니다.
모든 것을 인덱싱하기보다는 다음 필터를 우선시하세요:
- 자주 사용되는 것
- 많은 행을 건드리는 것
- 눈에 띄는 대기 시간을 만드는 것
- 간단하고 잘 맞는 인덱스로 안전하게 개선할 수 있는 것
범위를 좁게 잡은 이유는 의도적입니다. 관리자 리스트에서 처음 제기되는 성능 불만은 거의 항상 네 가지 필터 유형에서 옵니다: 상태(status), 담당자(assignee), 날짜 범위, 텍스트 필드. 이들이 왜 다르게 동작하는지 이해하면 다음 단계는 명확합니다: 실제 쿼리 패턴을 보고, 그에 맞는 가장 작은 인덱스를 추가하고, 느린 경로가 개선되었는지 검증하세요.
실제 관리자 작업 뒤의 쿼리 패턴
관리자 패널이 느려지는 이유는 하나의 거대한 리포트 때문이 아닙니다. 하루 종일 사용되는 몇몇 화면이 있고, 그 화면들이 자주 작은 쿼리를 반복 실행하기 때문에 느려집니다.
운영팀은 보통 티켓, 주문, 사용자, 승인, 내부 요청 같은 몇 개의 작업 큐를 자주 사용합니다. 이 페이지들에서 반복되는 필터는 다음과 같습니다:
- 상태(Status): 워크플로를 반영합니다(새로움, 열림, 보류, 완료)
- 담당자(Assignee): "내 항목"이나 "미할당"을 보기 위해 필요합니다
- 날짜 범위: 누군가 항상 "지난주에 무슨 일이 있었나?"라고 묻습니다
- 검색: 특정 항목으로 바로 이동(주문번호, 이메일)하거나 텍스트(노트, 미리보기)를 훑어보기 위함입니다
데이터베이스 작업은 의도에 따라 달라집니다:
- 최신 항목 둘러보기(Browse newest): 스캔 패턴입니다. 보통 "최신 항목을 보여줘, 상태로 좁힐 수도 있고, 생성 시간으로 정렬" 같은 형태이며 페이지네이션이 적용됩니다.
- 특정 항목 찾기(Find a specific item): 조회 패턴입니다. 관리자가 이미 ID, 이메일, 티켓 번호, 참조를 알고 있어 데이터베이스가 바로 소수의 행으로 점프하길 기대합니다.
관리자 패널은 또한 예측 가능한 방식으로 필터를 조합합니다: "Open + Unassigned", "Pending + Assigned to me", "지난 30일 완료" 등. 인덱스는 열 목록과 맞추기보다는 실제 쿼리 형태와 맞출 때 가장 잘 작동합니다.
AppMaster로 관리자 도구를 만들면, 이런 패턴은 가장 많이 쓰이는 리스트 화면과 기본 필터만 봐도 드러나는 경우가 많습니다. 그러면 실제로 일상 작업을 움직이는 것들을 인덱싱하기가 쉬워집니다.
무엇을 먼저 인덱싱할지 고르는 법
인덱싱은 트리아주처럼 다루세요. 필터 드롭다운에 나오는 모든 컬럼을 인덱싱하는 것부터 시작하지 마세요. 사람들을 짜증나게 하는 자주 실행되는 몇 가지 쿼리부터 시작하세요.
사람들이 실제로 쓰는 필터 찾기
아무도 안 쓰는 필터를 최적화하는 것은 시간 낭비입니다. 실제로 뜨거운 경로를 찾으려면 여러 신호를 결합하세요:
- UI 분석: 어떤 화면이 가장 많이 조회되며 어떤 필터가 가장 자주 클릭되는가
- 데이터베이스 또는 API 로그: 가장 빈번한 쿼리와 가장 느린 상위 퍼센트
- 내부 피드백: "검색이 느리다"는 말은 보통 특정 화면을 가리킵니다
- 기본 랜딩 리스트: 관리자가 패널을 열자마자 무엇이 실행되는가
많은 팀에서 기본 뷰는 "오픈 티켓" 또는 "새 주문" 같은 형태입니다. 이 뷰는 누군가 새로고침할 때, 탭을 전환할 때, 회신 후 돌아올 때마다 실행됩니다.
필드 이름이 아니라 쿼리 형태로 그룹화하세요
인덱스를 추가하기 전에 자주 쓰이는 쿼리를 그 동작 방식으로 그룹화하세요. 대부분의 관리자 리스트 쿼리는 몇 가지 버킷으로 나뉩니다:
- 동등 비교(Equality) 필터:
status = 'open',assignee_id = 42 - 범위(Range) 필터:
created_at두 날짜 사이 - 정렬과 페이지네이션:
ORDER BY created_at DESC그리고 페이지 2 가져오기 - 텍스트 조회: 정확 일치(주문 번호), 접두사 일치(이메일 시작), 또는 포함 검색
각 상위 화면에 대해 WHERE, ORDER BY, 페이지네이션을 포함한 형태를 적어두세요. UI에서 비슷해 보여도 데이터베이스에서는 매우 다르게 동작할 수 있습니다.
작은 우선 배치를 고르세요
우선순위 목표 하나로 시작하세요: 관리자가 처음 보는 기본 리스트 쿼리. 그런 다음 자주 쓰이는 2~3개의 쿼리를 고르세요. 보통 이 정도면 가장 큰 지연을 줄이기에 충분합니다.
예: 지원팀이 Tickets 리스트를 status = 'open'로 필터하고 최신순으로 정렬하며 선택적으로 담당자와 날짜 범위를 적용한다면, 그 정확한 조합을 먼저 최적화하세요. 빠르게 되면 다음 화면으로 넘어가 사용량 기반으로 작업하세요.
상태 필터 인덱싱(과잉을 피하는 법)
상태는 사람들이 가장 먼저 추가하는 필터 중 하나고, 잘못하면 효과가 없게 인덱싱하기 쉽습니다.
대부분의 상태 필드는 낮은 카디널리티를 가집니다: 값이 몇 개뿐입니다(open, pending, closed). 인덱스는 결과를 작은 조각으로 좁힐 때 가장 도움이 됩니다. 만약 80%~95%의 행이 동일한 상태라면 status 단독 인덱스는 큰 변화를 만들지 못합니다. 데이터베이스가 여전히 많은 행을 읽어야 하고, 인덱스는 오버헤드를 추가합니다.
다음 상황에서 효과를 느끼는 경우가 많습니다:
- 어떤 상태가 희귀한 경우(예: escalated)
- 상태가 다른 조건과 결합되어 결과 집합을 작게 만드는 경우
- 상태와 정렬이 일반적인 리스트 뷰와 일치하는 경우
흔한 패턴은 "오픈 항목을 보여줘, 최신순"입니다. 이 경우 필터와 정렬을 함께 인덱싱하는 것이 status 단독 인덱스보다 낫습니다.
초기에 효과를 내는 조합 예:
status + updated_at(상태로 필터하고 최근 변경순 정렬)status + assignee_id(워크 큐 뷰)status + updated_at + assignee_id(정말 그 뷰가 많이 쓰일 때만)
부분 인덱스(partial index)는 한 상태가 지배적인 경우 좋은 타협책입니다. 예를 들어 "open" 뷰가 주로 사용된다면 open 행만 인덱싱하면 인덱스가 작아지고 쓰기 비용도 낮아집니다.
-- PostgreSQL example: index only open rows, optimized for newest-first lists
CREATE INDEX CONCURRENTLY tickets_open_updated_idx
ON tickets (updated_at DESC)
WHERE status = 'open';
실용적인 테스트: 느린 관리자 쿼리를 상태 필터를 넣고 뺀 상태로 실행해 보세요. 둘 다 느리면 상태만의 인덱스는 소용이 없습니다. 정렬과 실제로 목록을 줄이는 두 번째 필터에 집중하세요.
담당자 필터링: 동등 비교 인덱스와 흔한 조합
대부분 관리자 패널에서 담당자는 레코드에 저장된 사용자 ID입니다: assignee_id 같은 외래 키. 이는 전형적인 동등 비교 필터이고, 단순 인덱스로 빠른 성과를 볼 수 있습니다.
담당자는 다른 필터와 함께 자주 등장합니다. 예: 관리자가 "Alex에게 할당된 항목"으로 필터한 뒤 "오픈"으로 좁힙니다. 이 뷰가 느리면 단일 컬럼 인덱스 이상이 필요할 때가 많습니다.
시작점으로 좋은 복합 인덱스:
assignee_id, status— "내가 할당된 오픈 항목"assignee_id, status, updated_at— 리스트가 최근 활동 기준으로 정렬될 때
복합 인덱스에서 순서는 중요합니다. 동등 비교 필터(보통 assignee_id, 그다음 status)를 앞에 두고, 정렬이나 범위 컬럼(updated_at)은 뒤에 둡니다. 이렇게 하면 데이터베이스가 효율적으로 활용합니다.
미할당(unassigned)은 흔한 골칫거리입니다. 많은 시스템이 미할당을 assignee_id = NULL로 표현하고 관리자는 이를 자주 필터링합니다. 데이터베이스와 쿼리 형태에 따라 NULL 값은 실행 계획을 바꿔 인덱스가 담당된 항목에서는 잘 작동해도 미할당에는 쓸모없게 느껴질 수 있습니다.
미할당 큐가 중요하다면 하나의 명확한 접근을 정하고 테스트하세요:
assignee_id를 nullable로 유지하되WHERE assignee_id IS NULL쿼리를 테스트하고 필요한 경우 인덱스 전략을 마련합니다.- 데이터 모델에 맞는다면 전용 값(예: 특별한 "Unassigned" 사용자)을 사용하는 방법을 고려합니다.
- 데이터베이스가 지원하면 미할당 행에 대한 부분 인덱스를 추가합니다.
AppMaster로 관리자 패널을 만든다면 팀이 가장 많이 쓰는 필터와 정렬을 로깅하고, 모든 필드를 인덱싱하기보다는 그런 패턴을 반영한 소수의 잘 선택된 인덱스를 만들면 도움이 됩니다.
날짜 범위: 사람들이 필터하는 방식에 맞춘 인덱스
날짜 필터는 보통 "지난 7일", "지난 30일"과 같은 프리셋과 시작/끝을 고르는 커스텀 선택기가 함께 제공됩니다. 단순해 보이지만 큰 테이블에서는 매우 다른 데이터베이스 작업을 촉발할 수 있습니다.
먼저 사람들이 실제로 어떤 타임스탬프 열을 의미하는지 분명히 하세요. 다음을 사용합니다:
- 새 항목 뷰는
created_at - 최근 변경 뷰는
updated_at
해당 컬럼에 일반적인 btree 인덱스를 두세요. 그렇지 않으면 "지난 30일" 클릭이 전체 테이블 스캔으로 바뀔 수 있습니다.
프리셋 범위는 보통 created_at >= now() - interval '30 days' 같은 형태입니다. 이는 범위 조건이고 created_at 인덱스를 효율적으로 사용할 수 있습니다. UI가 최신순 정렬도 함께 한다면 정렬 방향(예: PostgreSQL의 created_at DESC)과 일치시키는 것이 많이 쓰이는 리스트에서 도움이 됩니다.
날짜가 상태나 담당자 같은 다른 필터와 결합될 때는 신중하세요. 복합 인덱스는 그 조합이 흔할 때 훌륭합니다. 그렇지 않으면 쓰기 비용만 증가시킬 뿐입니다.
실용 규칙:
- 대부분의 뷰가 상태로 먼저 필터한 뒤 날짜로 좁힌다면
(status, created_at)이 도움이 됩니다. - 상태는 선택적이지만 날짜가 항상 존재하면 단순한
created_at인덱스를 유지하고 복합 인덱스를 많이 만들지 마세요. - 모든 조합을 만들지 마세요. 각 인덱스는 저장 공간을 늘리고 쓰기를 느리게 합니다.
타임존과 경계는 "레코드 누락" 버그의 주요 원인입니다. 사용자가 날짜(시간 없이)를 선택하면 종료일을 어떻게 해석할지 결정하세요. 안전한 패턴은 시작 포함, 종료 제외: created_at >= start 그리고 created_at < end_next_day. 타임스탬프는 UTC로 저장하고 사용자 입력을 UTC로 변환한 뒤 쿼리하세요.
예: 운영 관리자가 1월 10일~1월 12일을 선택하고 1월 12일의 모든 항목을 기대하는데 쿼리가 <= '2026-01-12 00:00'라면 1월 12일의 거의 모든 항목을 누락합니다. 인덱스 자체는 문제가 아니고 경계 로직이 잘못된 것입니다.
텍스트 필드: 정확 검색 대 포함 검색
텍스트 검색은 많은 관리자 패널이 느려지는 지점입니다. 사람들은 한 박스에서 모든 것을 찾길 기대하기 때문입니다. 첫 번째 개선은 두 가지 필요를 분리하는 것입니다: 정확 일치(빠르고 예측 가능)와 포함 검색(유연하지만 부담이 큼).
정확 일치 필드에는 주문 ID, 티켓 번호, 이메일, 전화번호, 외부 참조 등이 포함됩니다. 이런 필드는 일반 데이터베이스 인덱스에 적합합니다. 관리자가 ID나 이메일을 붙여넣어 찾는 경우 단순 인덱스 + 동등 비교로 즉시 느껴지는 응답을 얻을 수 있습니다.
포함 검색은 누군가가 "refund"나 "john" 같은 조각을 입력해 이름, 노트, 설명에서 일치하는 항목을 찾는 경우입니다. 보통 LIKE %term%으로 구현되는데 선행 와일드카드 때문에 일반 B-tree 인덱스를 사용할 수 없어 많은 행을 스캔합니다.
데이터베이스에 과부하를 주지 않고 검색을 구축하는 실용적 방법:
- 정확 일치 검색(ID, 이메일, 사용자명)을 우선 기능으로 만들고 명확히 라벨링하세요.
- 접두사 검색(
term%)은 표준 인덱스로 어느 정도 도움이 되며 이름 검색에 충분할 때가 많습니다. - 포함 검색은 로그나 불만이 실제로 필요함을 보여줄 때만 추가하세요.
- 포함 검색을 추가할 때는 PostgreSQL full-text search나 trigram 인덱스 같은 적절한 도구를 사용하세요. 일반 인덱스로
LIKE %term%을 고치려 하지 마세요.
입력 규칙은 대부분의 팀이 예상하는 것보다 더 큰 효과가 있습니다. 로드를 줄이고 결과를 일관되게 만듭니다:
- 포함 검색의 최소 길이를 정하세요(예: 3자 이상).
- 대소문자 정규화를 하거나 대소문자 무시 비교를 일관되게 사용하세요.
- 앞뒤 공백을 제거하고 반복 공백을 축소하세요.
- 이메일과 ID는 일반 검색 박스에 입력되더라도 기본적으로 정확 일치로 처리하세요.
- 검색어가 너무 광범위하면 큰 쿼리를 실행하지 말고 사용자에게 구체화를 요청하세요.
작은 예: 지원 매니저가 "ann"을 검색해 고객을 찾으려 합니다. 시스템이 노트, 이름, 주소 전체에 LIKE %ann%을 실행하면 수천 행을 스캔할 수 있습니다. 먼저 이메일이나 고객 ID 같은 정확 필드를 확인하고, 필요할 때만 더 정교한 텍스트 인덱스로 폴백하면 검색이 빠르게 유지됩니다.
인덱스를 안전하게 추가하는 단계별 워크플로
인덱스는 추가하기는 쉽지만 후회하기도 쉽습니다. 안전한 워크플로는 관리자가 의존하는 필터에 집중하게 하고, 나중에 후회할 "혹시 도움이 될까" 인덱스를 피하게 합니다.
실제 사용부터 시작하세요. 상위 쿼리를 두 가지 방식으로 추출하세요:
- 가장 빈번한 쿼리
- 가장 느린 쿼리
관리자 패널에서는 이들이 보통 필터와 정렬이 있는 리스트 페이지입니다.
다음으로 데이터베이스가 보는 그대로 쿼리 형태를 캡처하세요. 정확한 WHERE와 ORDER BY를 적어두세요(정렬 방향과 흔한 조합 포함). 작은 차이도 어떤 인덱스가 도움이 되는지를 바꿀 수 있습니다.
간단한 루프를 사용하세요:
- 한 개의 느린 쿼리와 한 가지 인덱스 변경을 고르세요.
- 하나의 인덱스를 추가하거나 조정하세요.
- 같은 필터와 같은 정렬로 재측정하세요.
- 삽입 및 갱신이 눈에 띄게 느려지지 않았는지 확인하세요.
- 대상 쿼리가 명확히 개선되면 변경을 유지하세요.
페이지네이션도 별도로 확인하세요. 오프셋 기반 페이지네이션(OFFSET 20000)은 인덱스가 있어도 더 깊은 페이지로 갈수록 느려집니다. 사용자가 매우 깊은 페이지로 자주 이동한다면 커서 스타일 페이지네이션("이 타임스탬프/ID 이전 항목 표시")을 고려해 인덱스가 큰 테이블에서도 일관된 작업을 하게 하세요.
마지막으로 인덱스 목록을 몇 달 후에도 이해할 수 있게 간단한 기록을 남기세요: 인덱스 이름, 테이블, 칼럼(순서 포함), 지원하는 쿼리.
관리자 패널에서 흔한 인덱싱 실수
사람들이 실제로 어떻게 필터링하고 정렬하며 페이지를 넘기는지 확인하지 않고 인덱스를 추가하면 관리자 패널이 느려집니다. 인덱스는 저장 공간과 모든 삽입/갱신에 대한 추가 작업을 요구합니다.
자주 발생하는 실수
다음 패턴이 대부분의 문제를 일으킵니다:
- "혹시 몰라서" 모든 컬럼에 인덱스 추가
- 잘못된 컬럼 순서로 복합 인덱스 생성
- 정렬과 페이지네이션을 무시
LIKE '%term%'같은 포함 검색을 일반 인덱스로 해결하려 기대- UI 변경 후 오래된 인덱스를 남겨둠
흔한 시나리오: 지원팀이 Status = Open으로 필터하고 updated 시간으로 정렬하며 페이지를 넘깁니다. status에만 인덱스를 추가하면 데이터베이스는 여전히 열린 티켓을 모두 모으고 정렬해야 할 수 있습니다. 필터와 정렬을 함께 맞춘 인덱스가 1페이지 반환을 빠르게 만듭니다.
이런 문제를 잡는 빠른 방법
관리자 UI 변경 전후에 짧은 리뷰를 하세요:
- 상위 필터와 기본 정렬을 나열하고,
WHERE + ORDER BY패턴을 만족하는 인덱스가 있는지 확인하세요. - 선행 와일드카드(
LIKE '%term%')를 찾아 포함 검색이 정말 필요한지 결정하세요. - 중복되거나 겹치는 인덱스를 찾아보세요.
- 사용되지 않는 인덱스를 일정 기간 추적한 뒤 필요 없다고 확신되면 제거하세요.
AppMaster로 PostgreSQL 기반 관리자 패널을 만든다면, 이 리뷰를 새 화면을 배포할 때 루틴으로 삼으세요. 올바른 인덱스는 UI가 실제로 사용하는 필터와 정렬에서 직접 도출되는 경우가 많습니다.
빠른 점검 및 다음 단계
더 많은 인덱스를 추가하기 전에 이미 있는 인덱스가 매일 쓰이는 정확한 필터에 도움이 되는지 확인하세요. 좋은 관리자 패널은 흔한 경로에서 즉시 반응해야 하고, 드문 검색에서만 빠르게 동작할 필요는 없습니다.
몇 가지 점검만으로 대부분의 문제를 잡을 수 있습니다:
- 가장 흔한 필터 조합(상태, 담당자, 날짜 범위 + 기본 정렬)을 열어 테이블이 커져도 빠르게 유지되는지 확인하세요.
- 느린 뷰마다 쿼리가
WHERE와ORDER BY모두에 맞는 인덱스를 사용하고 있는지 확인하세요. 한쪽만 맞으면 부족할 수 있습니다. - 인덱스 목록을 작게 유지해 각 인덱스의 목적을 한 문장으로 설명할 수 있도록 하세요.
- 인덱스 추가 후 작성/갱신 작업(create, update, status change)이 느려졌는지 관찰하세요. 느려졌다면 인덱스가 너무 많거나 겹쳐져 있을 수 있습니다.
- UI에서 "검색"이 무엇을 의미하는지 결정하세요: 정확 일치, 접두사, 포함 중 어떤 것인지. 인덱싱 계획은 이 선택과 일치해야 합니다.
실용적인 다음 단계는 다음과 같은 골든 패스를 문장으로 적는 것입니다: "지원 에이전트는 오픈 티켓을 필터하고, 내게 할당된 항목, 지난 7일, 최신순으로 정렬한다." 그런 문장들을 사용해 그 경로들을 명확히 지원하는 소수의 인덱스를 설계하세요.
아직 개발 초기라면 너무 많은 화면을 만들기 전에 데이터와 기본 필터를 모델링하는 것이 도움이 됩니다. AppMaster (appmaster.io)를 사용하면 관리자 뷰를 빠르게 반복하고, 실제 사용이 뜨거운 경로를 드러낸 뒤 소수의 인덱스를 추가해 최적화할 수 있습니다.
자주 묻는 질문
먼저 관리자들이 처음 보는 기본 리스트 뷰와 하루 종일 클릭하는 2–3개의 필터를 최우선으로 하세요. 사용 빈도와 체감 지연(가장 느린 쿼리)을 측정한 뒤, 정확한 쿼리 형태에 대해 기다림을 줄여주는 것만 인덱싱하세요.
필터마다 처리해야 하는 양이 다르기 때문입니다. 어떤 필터는 작은 행 집합으로 좁혀 인덱스를 잘 활용하지만, 다른 필터는 넓은 범위를 읽거나 큰 결과를 정렬해야 해서 스캔과 정렬이 많이 발생합니다. 그래서 같은 테이블이라도 한 쿼리는 빠르고 다른 쿼리는 느낄 수 있습니다.
status 컬럼에 항상 인덱스를 걸어야 하는 것은 아닙니다. 대부분의 행이 동일한 상태를 가지면 status만의 인덱스는 큰 도움이 되지 않습니다. 상태가 희귀하거나 상태와 정렬·다른 필터를 함께 인덱싱해 실제 뷰를 재현할 때 효과를 봅니다.
사람들이 실제로 하는 동작을 반영하는 복합 인덱스를 사용하세요. 예: 상태로 필터하고 최근 활동 순으로 정렬하는 뷰라면 상태와 정렬 컬럼을 조합한 인덱스가 효과적입니다. PostgreSQL에서는 특정 상태만 인덱싱하는 부분 인덱스(partial index)가 유용할 수 있습니다.
일반적으로 assignee_id에 단일 인덱스를 만드는 것이 빠른 승수입니다(동등 비교). “내가 할당된 오픈 항목” 같은 핵심 워크플로가 중요하면 (assignee_id, status) 또는 정렬을 포함해 (assignee_id, status, updated_at) 같은 복합 인덱스가 단일 컬럼 인덱스보다 나을 수 있습니다. 복합 인덱스는 칼럼 순서가 중요합니다.
종종 unassigned는 assignee_id가 NULL인 것으로 표현됩니다. WHERE assignee_id IS NULL 쿼리는 = 123과 다른 실행 계획을 탈 수 있어 인덱스가 기대만큼 효과적이지 않을 수 있습니다. unassigned 큐가 중요하다면 특정 전략(예: 부분 인덱스)을 테스트해 보세요.
사람들이 실제로 어떤 타임스탬프를 의미하는지 명확히 하세요: created_at은 새 항목, updated_at은 최근 변경. 해당 컬럼에 btree 인덱스를 두면 최근 30일 같은 범위 쿼리가 전체 테이블 스캔으로 가지 않습니다. 자주 쓰이는 조합이면 (status, created_at) 같은 복합 인덱스도 고려하세요.
대부분의 문제는 범위 경계와 타임존 처리에서 옵니다. 안전한 패턴은 시작은 포함, 끝은 제외로 처리하는 것입니다: created_at >= start 그리고 created_at < end_next_day. 타임스탬프는 UTC로 저장하고 사용자 입력을 UTC로 변환해 쿼리하세요.
LIKE %term% 같은 contains 검색은 일반 B-tree 인덱스를 활용하지 못해 많은 행을 스캔합니다. ID, 이메일, 주문번호 같은 정확 일치 필드를 우선 지원하고, 접두사 검색(term%)은 일반 인덱스로 어느 정도 커버됩니다. 진정한 contains 검색이 필요하면 PostgreSQL full-text search나 trigram 인덱스 같은 적절한 도구를 사용하세요.
인덱스를 무작정 많이 추가하면 저장 공간이 늘고 삽입/갱신 비용이 올라갑니다. 인덱스가 WHERE + ORDER BY 패턴과 맞지 않으면 병목을 해결하지 못합니다. 한 번에 하나의 인덱스만 바꿔서 측정하고, 실제로 느린 쿼리가 개선될 때만 적용하세요.
AppMaster로 관리자 화면을 만든다면, 팀이 주로 사용하는 필터와 정렬을 기록하고 그 실제 뷰에 맞는 소수의 인덱스만 추가하세요.


