SQL 포맷터가 쿼리를 정렬하는 규칙

한 줄로 길게 붙어 있는 SQL을 절 단위로 펴서 보여주는 것이 SQL 포맷터입니다. 이 글은 그 포맷터가 쿼리를 "어떻게" 정렬하는지, 규칙을 그대로 설명합니다. 로그나 ORM 출력에서 복사한 한 줄짜리 쿼리를 매번 눈으로 따라 읽느라 시간을 쓰는 분에게 필요한 내용입니다.
핵심만 먼저 말하면 이렇습니다. 포맷터는 쿼리의 뜻을 바꾸지 않습니다. 공백과 줄바꿈, 그리고 키워드의 대소문자만 손봅니다.
요약 ① 대문자로 바뀌는 건 미리 정해진 SQL 키워드 목록에 든 단어뿐이고, 테이블·컬럼명과 따옴표 문자열은 원본 대소문자를 그대로 둡니다. ② SELECT·FROM·WHERE·JOIN·GROUP BY·ORDER BY 같은 주요 절마다 새 줄이 생기고, AND·OR·ON 조건과 콤마 뒤 항목, 서브쿼리는 한 단계 들여써집니다. ③ 바뀌는 건 공백·줄바꿈·키워드 대소문자뿐이라 쿼리의 실행 의미는 동일합니다.
한 줄로 붙은 쿼리는 왜 읽기 힘든가
애플리케이션 로그, ORM이 찍어준 쿼리, 슬랙에 붙여진 쿼리는 대개 줄바꿈 없이 한 줄로 이어져 있습니다. select a,b,c from t1 join t2 on ... where ... and ... order by ... 처럼요.
이런 형태에서는 어디가 SELECT 항목이고 어디가 조건절인지 경계가 눈에 잡히지 않습니다. WHERE 뒤에 조건이 몇 개 붙어 있는지 세려면 문자열을 왼쪽부터 끝까지 훑어야 합니다.
특히 서브쿼리가 들어가면 괄호 안이 또 하나의 SELECT인지, 단순한 함수 호출인지가 섞여 보입니다. 이 경계를 사람이 매번 머릿속으로 다시 그리는 것이 피로의 원인입니다.
도구 없이 직접 정렬하는 규칙
포맷터가 하는 일은 정해진 기계적 규칙의 반복입니다. 그래서 도구 없이 손으로도 똑같이 할 수 있습니다. 순서는 이렇습니다.
1. 키워드만 대문자로 바꾼다. SELECT·FROM·WHERE·JOIN·AND·ON·GROUP BY·ORDER BY 같은 예약어만 대문자로 올립니다. 테이블 이름, 컬럼 이름, 함수 인자, 따옴표로 감싼 문자열은 손대지 않습니다.
2. 주요 절마다 줄을 바꾼다. SELECT·FROM·WHERE·HAVING·LIMIT·GROUP BY·ORDER BY, 그리고 JOIN 계열(LEFT JOIN, INNER JOIN 등)이 나올 때마다 새 줄에서 시작합니다.
3. 조건과 항목을 한 단계 들여쓴다. AND·OR·ON으로 시작하는 조건, 콤마 뒤에 이어지는 SELECT 항목은 절보다 한 칸 안쪽으로 넣습니다.
4. 서브쿼리 괄호는 더 들여쓰고, 함수 괄호는 붙여둔다. 괄호 안이 또 다른 SELECT면 그 안쪽을 한 단계 더 들여씁니다. 반면 COUNT(*) 처럼 함수 호출 괄호는 이름에 붙인 채로 둡니다.
이 네 규칙을 아래 한 줄 쿼리에 적용하면 됩니다.
select o.id, o.total, u.name from orders o join users u on u.id = o.user_id where o.total > 1000 and u.status = 'active' order by o.total desc;
규칙대로 손보면 이런 모양이 됩니다(들여쓰기 2칸 기준).
SELECT o.id,
o.total,
u.name
FROM orders o
JOIN users u
ON u.id = o.user_id
WHERE o.total > 1000
AND u.status = 'active'
ORDER BY o.total DESC;
여기서 확인할 점이 몇 가지 있습니다. 'active' 문자열은 대문자로 바뀌지 않았고, 별칭 o·u와 컬럼명도 소문자 원본 그대로입니다. 대문자로 올라간 건 SELECT·FROM·JOIN·ON·WHERE·AND·ORDER BY·DESC처럼 예약어뿐입니다.
손으로도 되지만, 조건이 열 개씩 붙거나 서브쿼리가 중첩되면 괄호 깊이를 세는 것부터 번거로워집니다. 저희 도구로 확인하기.
붙여넣기만 하면 예약어 대문자화와 절 단위 줄바꿈으로 정렬됩니다. 설치·회원가입 없이 브라우저에서 바로 실행됩니다.

정렬된 결과를 어떻게 읽어야 하나
정렬 결과를 볼 때 기억할 것은 "형태만 바뀌었다"는 점입니다. 줄이 여러 개로 늘어났다고 해서 쿼리가 달라진 게 아닙니다.
들여쓰기가 한 단계 들어간 줄은 바로 위 절에 딸린 부분이라는 뜻입니다. WHERE 아래 들여쓴 AND는 그 WHERE의 추가 조건이고, 여는 괄호 뒤에서 더 깊이 들어간 SELECT는 서브쿼리입니다.
서브쿼리 괄호는 깊이를 한 단계 늘리지만 함수 호출 괄호는 그대로 붙어 있으므로, 들여쓰기 깊이만 따라가도 "이 괄호가 하위 질의인지 단순 함수인지"를 눈으로 구분할 수 있습니다.
화면에는 결과와 함께 출력 줄 수와 출력 크기(바이트) 두 값이 표시됩니다. 이 수치는 정렬 후 텍스트의 길이를 나타낼 뿐, 쿼리 성능이나 처리하는 행 수와는 아무 관계가 없습니다.
자주 하는 오해
가장 흔한 오해는 "정렬하면 쿼리 결과가 달라진다"는 것입니다. 포매팅은 공백·줄바꿈·키워드 대소문자만 바꾸므로 데이터베이스가 읽는 의미는 동일합니다.
한 가지 한계도 함께 알아두면 좋습니다. SQL은 MySQL·PostgreSQL·Oracle 등 방언마다 세부 구문이 다르고, 포맷터가 아는 키워드 목록에 없는 특수 구문은 절로 인식되지 못해 정렬이 어색하게 붙을 수 있습니다. 이 도구는 가독성을 높이는 참고용이라는 뜻입니다.
| 흔한 오해 | 실제 동작 |
|---|---|
| 정렬하면 쿼리 결과가 달라진다 | 공백·줄바꿈·키워드 대소문자만 바뀌어 실행 의미는 동일합니다 |
| 대문자로 바뀐 단어는 전부 예약어다 | 미리 정의된 키워드 목록에 든 단어만 대문자화됩니다 |
| 컬럼·테이블명도 대문자로 통일된다 | 식별자와 따옴표 문자열은 원본 대소문자를 유지합니다 |
| 모든 괄호가 들여쓰기를 만든다 | 서브쿼리 괄호만 들여쓰고 함수 호출 괄호는 이름에 붙어 있습니다 |
| 들여쓰기를 바꾸면 쿼리도 바뀐다 | 2칸·4칸·탭은 보이는 여백만 다를 뿐 의미는 같습니다 |
정리
- 대문자화는 정해진 키워드 목록에만 적용되고, 테이블·컬럼명과 따옴표 문자열은 원본을 그대로 유지합니다.
- SELECT·FROM·WHERE·JOIN·GROUP BY·ORDER BY 같은 주요 절마다 새 줄이 생기고, AND·OR·ON 조건과 콤마 뒤 항목은 한 단계 들여써집니다.
- 서브쿼리 괄호는 들여쓰기를 늘리지만 함수 호출 괄호는 이름에 붙어 있어, 깊이만 봐도 둘을 구분할 수 있습니다.
- 주석과 문자열은 그대로 보존되고, 세미콜론으로 나뉜 여러 문장을 각각 정렬합니다.
- 바뀌는 건 형태뿐이라 실행 의미는 동일하며, 들여쓰기 단위(2·4칸·탭)를 바꿔도 뜻은 같습니다.
붙여넣기만 하면 예약어 대문자화와 절 단위 줄바꿈으로 정렬됩니다. 설치·회원가입 없이 브라우저에서 바로 실행됩니다.
자주 묻는 질문
정렬하면 쿼리 실행 결과가 바뀌나요?
바뀌지 않습니다. 포매팅은 공백·줄바꿈·키워드 대소문자만 조정하므로 데이터베이스가 해석하는 쿼리의 의미는 그대로입니다. 다만 방언별 특수 구문은 정렬이 어색해질 수 있어 가독성 향상용 참고 도구로 보시면 됩니다.
컬럼 이름이나 문자열도 대문자로 바뀌나요?
아니요. 대문자화는 SELECT·FROM·WHERE·JOIN 같은 미리 정의된 키워드 목록에 든 단어에만 적용됩니다. 테이블·컬럼명, 함수 인자, 따옴표로 감싼 문자열 리터럴은 원본 대소문자를 유지합니다. 키워드 대문자화 체크박스를 끄면 예약어도 원본 그대로 둡니다.
여러 문장을 한 번에 정렬할 수 있나요?
네. 세미콜론(;)으로 구분된 여러 문장을 각각 정렬합니다. 각 문장은 세미콜론에서 끊겨 독립적으로 절 단위 줄바꿈이 적용됩니다. 주석(-- , /* */)과 문자열 안의 내용은 정렬 과정에서 그대로 보존됩니다.
들여쓰기를 2칸에서 4칸으로 바꾸면 뭐가 달라지나요?
들여쓴 줄 앞의 여백 폭만 달라집니다. 공백 2칸, 공백 4칸, 탭 중에서 고를 수 있으며, 어느 것을 선택해도 줄이 나뉘는 위치나 쿼리의 의미는 동일합니다. 설정을 바꾸면 별도 실행 버튼 없이 결과가 바로 다시 계산됩니다.
