이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 실무 SQL 리뷰에서는 무엇을 확인해야 하는가?
- 안전하고 운영 가능한 쿼리는 어떤 특징을 가지는가?
- 주니어와 시니어는 SQL 리뷰에서 각각 무엇을 봐야 하는가?
개요
SQL 리뷰는 문법 검사만이 아니다. API 요구사항, 인덱스, 실행 계획, 동시성, 데이터량, 운영 영향까지 확인하는 과정이다. 좋은 SQL은 읽기 쉽고, 결과가 맞고, 비용이 설명 가능하며, 실패 시 영향 범위가 제한되어 있다.
원리
SQL 리뷰의 원리는 “이 SQL이 맞는가”를 넘어 “이 SQL이 운영 트래픽, 데이터량, 인덱스, 트랜잭션, 장애 대응 안에서 감당 가능한가”를 확인하는 것이다. 같은 SQL도 관리자 기능에서 하루 한 번 실행되면 허용될 수 있고, 메인 피드 API에서 초당 수백 번 실행되면 장애 원인이 될 수 있다.
리뷰의 좋은 산출물은 감이 아니라 증거다. 실제 SQL, bind 값 패턴, 예상 호출 빈도, 실행 계획, 후보 인덱스, 영향 row 수, fallback 계획이 PR이나 작업 기록에 남아야 나중에 장애 회고와 인덱스 정리에도 활용할 수 있다.
SQL 리뷰 체크리스트
읽기 쿼리:
- 필요한 컬럼만 조회하는가?
- WHERE 조건이 인덱스를 사용할 수 있는가?
- ORDER BY와 LIMIT이 적절한가?
- JOIN 중간 결과가 커지지 않는가?
- N+1을 만들지 않는가?
- count 쿼리가 필요한가?
쓰기 쿼리:
- WHERE 조건이 정확한가?
- 영향 row 수를 확인하는가?
- 현재 상태 조건이 필요한가?
- lock 범위가 너무 넓지 않은가?
- 대량 변경은 batch인가?
- rollback 또는 forward fix가 있는가?
코드 리뷰 예시
나쁜 목록 조회:
SELECT *
FROM posts
WHERE title LIKE '%spring%'
ORDER BY created_at DESC
LIMIT 100;리뷰 포인트:
- 필요한 컬럼만 가져오는가?
%spring%검색 요구가 정말 필요한가?- 검색 인덱스가 있는가?
- 최신순 정렬 인덱스가 있는가?
- 최대 size가 100이어야 하는가?
개선 방향은 요구사항에 따라 달라진다.
SELECT id, title, created_at
FROM posts
WHERE board_id = :boardId
AND title LIKE :prefix
ORDER BY created_at DESC, id DESC
LIMIT 20;또는 full-text search로 분리할 수 있다.
PR에는 다음처럼 짧게라도 근거를 남긴다.
SQL: posts by board_id, title prefix, created_at desc
예상 호출: 게시판 목록 검색, peak 80 rpm
데이터량: posts 약 320만 건, board_id별 최대 18만 건
후보 인덱스: (board_id, created_at desc, id desc)
남은 리스크: title 부분 검색은 prefix만 지원, contains 검색은 검색 엔진 분리 검토
검증: 운영 유사 데이터에서 EXPLAIN 확인, limit 20 기준 sort 없음이 정도 기록은 과해 보이지만, 나중에 “왜 이 인덱스가 있지?”라는 질문에 답해준다.
주니어 팁
처음에는 세 가지만 봐도 좋다.
- 실제 SQL은 무엇인가?
- 몇 건을 읽을 것 같은가?
- 어떤 인덱스가 필요한가?
이 세 질문에 답하려고 하면 실행 계획과 데이터량을 자연스럽게 보게 된다.
시니어 팁
시니어는 쿼리 하나가 시스템에 미치는 영향을 본다.
- 호출 빈도
- p95/p99 영향
- pool 점유 시간
- lock과 deadlock 가능성
- 배포와 migration 위험
- 장애 시 완화 가능성
인덱스 하나 추가도 쓰기 부하와 DDL 영향을 함께 리뷰한다.
Guru급 팁
좋은 SQL 리뷰는 제품 요구사항을 다시 묻는다. “정확한 전체 count가 필요한가?”, “검색이 정말 부분 일치여야 하는가?”, “최신순 20개면 충분한가?”, “관리자 기능이라 느려도 되는가?” 같은 질문이 DB 비용을 크게 줄인다.
실전 팁
- SQL과 인덱스는 같은 변경으로 리뷰한다.
- 중요한 쿼리는 실행 계획을 PR에 첨부한다.
- 동적 검색은 주요 조건 조합만 최적화한다.
- 운영 쿼리는 dry-run과 대상 건수 확인을 한다.
- 리뷰에서 “왜”를 남겨 나중에 인덱스 정리에 활용한다.
- p95/p99, 호출 빈도, pool 점유 시간을 함께 보며 쿼리 우선순위를 정한다.
count가 꼭 필요한지, 최신순 20개로 충분한지, 정확한 실시간 검색이 필요한지 제품 요구를 다시 묻는다.- 인덱스 추가 PR에는 기존 유사 인덱스, 쓰기 부하, DDL 방식, 제거 후보를 같이 적는다.
위험 신호!
- SQL 리뷰가 문법과 결과만 본다.
- 실행 계획 없이 성능을 추측한다.
- count 쿼리를 자동으로 받아들인다.
- 모든 검색 요구를
%keyword%로 처리한다. - 쓰기 쿼리의 영향 row 수를 확인하지 않는다.
- PR에 Repository 메서드 이름만 있고 실제 SQL과 bind 값 예시가 없다.
- 실행 계획을 첨부했지만 rows estimate와 actual rows 차이를 읽지 않는다.
- 인덱스 추가 사유가 “혹시 빨라질 것 같아서”에 머문다.
확인 질문
- SQL 리뷰에서 결과 외에 확인해야 하는 것은 무엇인가?
- 인덱스, 실행 계획, 읽는 행 수, 정렬/조인 비용, lock, 호출 빈도, 운영 영향이다.
- 주니어가 SQL 리뷰에서 처음 집중할 세 가지는 무엇인가?
- 실제 SQL, 예상 읽기 건수, 필요한 인덱스다.
- 좋은 SQL 리뷰가 제품 요구사항을 묻는 이유는 무엇인가?
- 요구사항을 조금 바꾸면 DB 비용과 장애 위험을 크게 줄일 수 있기 때문이다.
- SQL 리뷰 기록에 남기면 좋은 운영 근거는 무엇인가?
- 실제 SQL, bind 값 패턴, 데이터량, 호출 빈도, 실행 계획, 후보 인덱스, 영향 row 수, 남은 리스크와 fallback이다.