| name | db-query-review |
| description | N+1 문제, 풀테이블 스캔, 집계 부하 등 쿼리 성능 이슈를 리뷰한다 |
| argument-hint | 쿼리 파일 경로 또는 애플리케이션 코드 경로 |
| user-invocable | true |
| disable-model-invocation | true |
| allowed-tools | Read, Grep, Glob |
당신은 신중한 시니어 엔지니어다. $ARGUMENTS 를 대상으로 아래 작업을 수행하라.
목적
애플리케이션 코드 및 SQL 쿼리를 분석하여 N+1 문제, 풀테이블 스캔, 비효율적인 집계 처리, 잘못된 JOIN 패턴 등 성능 문제를 탐지한다.
각 문제에 대해 구체적인 개선 방안을 제시한다.
입력
- 애플리케이션 코드 (Repository / DAO / Model / Controller 계층)
- 순수 SQL 쿼리 파일
- ORM 쿼리 빌더 코드
- 슬로우 쿼리 로그 (선택)
- EXPLAIN 실행 결과 (선택)
절차
1. 쿼리 패턴 수집
1-1. 지정 경로의 코드에서 SQL 쿼리 및 ORM 쿼리를 추출한다
1-2. 반복문 내부에서 실행되는 쿼리 패턴을 탐지한다
1-3. ORM의 연관 로딩 방식(Eager / Lazy)을 식별한다
1-4. 동적으로 생성되는 쿼리(문자열 결합, 조건 분기)를 파악한다
1-5. 트랜잭션 경계와 쿼리 실행의 관계를 점검한다
2. N+1 문제 탐지
2-1. 루프 내부에서 반복 실행되는 쿼리 패턴 식별
2-2. ORM Lazy Loading으로 인한 N+1 탐지 (belongs_to, has_many 등)
2-3. 중첩된 연관 접근으로 인한 다단계 N+1 탐지
2-4. 각 N+1 패턴의 예상 쿼리 실행 횟수 계산
2-5. Eager Loading / JOIN / 서브쿼리로의 개선안 작성
3. 풀테이블 스캔 탐지
3-1. WHERE 절이 없는 SELECT 문 탐지
3-2. 인덱스를 사용하지 못하는 조건식 탐지 (함수 적용, 형 변환, LIKE '%prefix' 등)
3-3. OR 조건으로 인한 인덱스 무효화 패턴 탐지
3-4. 암묵적 형 변환으로 인한 인덱스 무효화 탐지
3-5. SELECT * 사용 위치 식별 및 필요한 컬럼만 조회하도록 권장
4. 집계 및 정렬 처리 평가
4-1. 대량 데이터 대상 COUNT / SUM / AVG 등 집계 탐지
4-2. GROUP BY 절에서 인덱스 활용 가능성 평가
4-3. ORDER BY로 인한 파일 정렬(filesort) 발생 가능성 탐지
4-4. 불필요한 DISTINCT 사용 탐지
4-5. HAVING 절을 WHERE 절로 이동 가능한 경우 식별
4-6. 윈도우 함수 사용의 적절성 평가
5. JOIN 패턴 평가
5-1. 실제로 사용되지 않는 불필요한 JOIN 탐지
5-2. JOIN 조건 컬럼의 인덱스 존재 여부 확인
5-3. 대용량 테이블 간 CROSS JOIN(직적 곱) 탐지
5-4. 서브쿼리를 JOIN으로 치환 가능한지 평가
5-5. 상관 서브쿼리의 성능 영향 평가
6. 종합 평가 및 개선안
6-1. 각 탐지 항목의 영향도 점수화 (CRITICAL / HIGH / MEDIUM / LOW)
6-2. 개선 우선순위 결정 (영향도 × 실행 빈도)
6-3. 각 문제에 대한 구체적 개선 SQL / 코드 제시
6-4. 개선 효과에 대한 정성적 추정 제시
출력 포맷
# 쿼리 리뷰: [대상 파일 / 모듈명]
## 리뷰 요약
| 항목 | 건수 |
|------|------|
| 분석 쿼리 수 | N 건 |
| N+1 문제 | N 건 |
| 풀테이블 스캔 | N 건 |
| 집계 부하 | N 건 |
| JOIN 문제 | N 건 |
| 종합 리스크 | CRITICAL / HIGH / MEDIUM / LOW |
## 탐지 상세
### [CRITICAL] N+1 문제: [요약]
- **위치**: `file_path:line`
- **문제 코드**:
```python
# 예: 반복문 내부에서 쿼리 실행
for user in users:
orders = db.query(Order).filter(Order.user_id == user.id).all() # N번 실행
- 예상 실행 횟수: 부모 레코드 N건 × 자식 쿼리 1회 = N+1회
- 개선안:
users = db.query(User).options(joinedload(User.orders)).all()
[HIGH] 풀테이블 스캔: [요약]
- 위치:
file_path:line
- 문제 쿼리:
SELECT * FROM orders WHERE YEAR(created_at) = 2024;
SELECT order_id, user_id, total FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
- 근거: 함수 제거 후 범위 조건으로 변경하여 인덱스 레인지 스캔 가능
[MEDIUM] 불필요한 집계 처리: [요약]
- 위치:
file_path:line
- 문제점: [구체 설명]
- 개선안: [개선 코드]
개선 우선순위 매트릭스
| 우선순위 | 문제 | 영향도 | 실행 빈도 | 개선 비용 |
|---|
| 1 | [N+1 문제 A] | CRITICAL | 높음 | 낮음 |
| 2 | [풀스캔 B] | HIGH | 중간 | 중간 |
| 3 | [집계 부하 C] | MEDIUM | 낮음 | 높음 |
안티패턴 목록
| 패턴 | 탐지 수 | 파일 |
|---|
| SELECT * | N 곳 | file1.py, file2.py |
| 루프 내 쿼리 | N 곳 | file3.py |
| 암묵적 형 변환 | N 곳 | file4.py |
권장 액션
## 안전 수칙
- **실제 SQL을 실행하지 말 것** — 본 작업은 정적 분석만 수행한다
- **운영 데이터베이스에 접속하지 말 것**
- **애플리케이션 코드를 직접 수정하지 말 것** — 리뷰 결과 보고만 수행
- **ORM 고유 동작을 정확히 이해하고 분석할 것** (ActiveRecord, SQLAlchemy, Eloquent, Prisma 등)
- **성능 영향 평가는 정성적으로만 제시할 것** — 정확한 수치는 실측 필요
- **사용자 입력이 직접 쿼리에 삽입되는 경우 SQL 인젝션 위험도 함께 경고할 것**
- **추정 기반 판단은 반드시 ‘추정’이라고 명시할 것**
---
## 종료 조건
- 대상 범위의 모든 쿼리 패턴이 수집 및 분석되었을 것
- N+1 문제가 빠짐없이 탐지되었을 것
- 풀테이블 스캔의 원인이 식별되었을 것
- 각 문제에 대해 구체적인 개선 코드가 제시되었을 것
- 개선 우선순위가 매트릭스로 정리되었을 것
- 권장 액션 리스트가 작성되었을 것
---
## 이 스킬이 적합하지 않은 경우
- **실행 계획(EXPLAIN)의 대체로 사용하는 경우**: 본 분석은 코드 기반 정적 분석이다. 실제 데이터 분포 및 인덱스 사용 여부는 `EXPLAIN ANALYZE`로 확인해야 한다.
- **스토어드 프로시저나 뷰 내부의 복잡한 쿼리 분석**: 파일 내 SQL 패턴이 주 대상이다. DB 내부에 정의된 로직 분석에는 적합하지 않다.