SQL 유효성 검사기는 사실 파서가 아니다 — 괄호·따옴표·키워드 3가지만 본다
"SQL 유효성 검사"라는 이름을 보면 마치 데이터베이스 엔진이 SQL을 해석하는 것과 같은 수준의 검증을 기대하게 됩니다. 하지만 브라우저에서 돌아가는 경량 SQL 검사기 대부분은 실제로 SQL 문법을 이해하지 않습니다. 이 가이드는 실제 코드를 열어 무엇을 검사하고 무엇을 검사하지 않는지 확인하고, 그 한계가 왜 생기는지, 그리고 그런데도 왜 여전히 쓸모가 있는지를 정리합니다.
1. 진짜 SQL 파서와 이 도구의 결정적 차이
진짜 SQL 파서(데이터베이스 엔진 내부, 또는 sqlparse 같은 라이브러리)는 토큰을 순서대로 읽어 추상 구문 트리(AST)를 만듭니다. 이 과정에서 SELECT 절 다음에 FROM 절이 와야 하는지, WHERE가 GROUP BY보다 앞에 있어야 하는지 같은 문법 규칙과 절의 순서까지 함께 검증됩니다. 순서가 틀리면 트리를 만들 수 없으니 그 자리에서 오류가 납니다.
반면 브라우저에서 즉시 실행되는 경량 검사기는 트리를 만들지 않습니다. 서버로 아무것도 전송하지 않고 정규식과 문자 단위 순회만으로 결과를 내야 하다 보니, 절 순서를 추적하는 상태 기계를 만드는 대신 훨씬 값싼 검사 세 가지로 대체하는 경우가 많습니다.
2. 코드를 열어 확인한 실제 검사 로직
SQL 유효성 검사기의 validate() 함수를 직접 읽어보면 하는 일이 정확히 세 가지로 나뉩니다.
- 괄호 짝 맞춤: 문자열을 한 글자씩 순회하며 여는 괄호에서 카운터를 올리고 닫는 괄호에서 내립니다. 끝났을 때 카운터가 0이 아니면 오류.
- 따옴표 짝 맞춤: 작은따옴표·큰따옴표로 시작한 문자열이 이스케이프 없이 같은 문자로 닫혔는지 상태로 추적합니다.
- 키워드 존재 여부: 문자열 리터럴 내용을 정규식으로 제거한 뒤, SELECT·INSERT·UPDATE·DELETE·CREATE·DROP·ALTER·WITH·CALL·EXEC 중 하나라도 단어 경계로 존재하는지만 확인합니다.
이 세 조건을 모두 만족하면 다른 어떤 것도 확인하지 않고 "유효"로 표시합니다. 절의 순서, 컬럼 존재 여부, 테이블 존재 여부, JOIN 조건의 타당성 같은 것은 애초에 검사 대상이 아닙니다.
3. 실험: 절 순서를 뒤섞으면 정말 통과할까
키워드 순서가 문법적으로 말이 안 되는 문장을 직접 넣어 검사기가 어떻게 반응하는지 확인해봤습니다. 결과는 코드를 읽어서 예상한 그대로였습니다.
| 입력한 SQL | 괄호 | 따옴표 | 키워드 존재 | 검사 결과 |
|---|---|---|---|---|
FROM users SELECT * WHERE id=1 | 없음(문제 없음) | 없음(문제 없음) | SELECT 있음 | ✓ 유효 |
WHERE id=1 CREATE users FROM | 없음(문제 없음) | 없음(문제 없음) | CREATE 있음 | ✓ 유효 |
SELECT * FROM users ( | 1개 미해소 | 없음 | SELECT 있음 | ✗ 오류(괄호) |
첫 번째, 두 번째 줄은 어떤 데이터베이스에도 넣을 수 없는 문법 오류지만, 괄호·따옴표가 맞고 인식 가능한 키워드가 하나라도 있으니 검사기는 "유효"라고 판정합니다. 세 번째 줄처럼 괄호 하나만 어긋나도 즉시 오류로 잡히는 것과 대조적입니다. 즉 이 도구가 실제로 방어하는 것은 "복사·붙여넣기 과정에서 괄호나 따옴표가 잘렸는지" 같은 물리적 손상이지, 문법적으로 실행 가능한 쿼리인지가 아닙니다.
4. 포맷터도 같은 방식이라 생기는 부작용
'포맷' 기능 역시 SQL 구조를 이해해서 다시 그리는 게 아니라, 미리 정한 키워드 목록을 정규식으로 찾아 대문자화하고 줄바꿈을 넣는 텍스트 치환입니다. 그래서 컬럼명이나 별칭이 key, order, set처럼 SQL 키워드와 우연히 같으면 문맥과 무관하게 대문자로 바뀝니다. 더 미묘한 문제는 문자열 리터럴 안입니다. 검증 함수는 정규식으로 작은따옴표 문자열 내용을 미리 제거한 뒤 검사하지만, 포맷 함수는 그런 전처리 없이 원본 텍스트에 그대로 정규식을 적용합니다. 그래서 WHERE status = 'select'처럼 값 자체가 키워드와 같은 단어면 문자열 값까지 대문자로 바뀌어버릴 수 있습니다.
5. 그렇다면 언제 쓸모가 있나
문법 구조까지 검증하지 못한다고 이 도구가 무의미한 것은 아닙니다. 실무에서 자주 발생하는 오류는 사실 복잡한 문법 오류보다 훨씬 단순한 것들입니다 — 슬랙이나 이메일에서 SQL을 복사할 때 괄호 하나가 잘려나갔거나, 여러 줄 문자열을 붙여넣는 과정에서 따옴표가 한쪽만 남는 경우입니다. 이런 물리적 손상은 이 세 가지 검사만으로도 충분히 잡아냅니다. 다만 이 결과를 "실행 가능함을 보장한다"는 의미로 받아들이면 안 됩니다. 실제 실행 가능 여부를 확인하려면 스테이징 DB에 직접 실행해보거나, JSON으로 변환해 구조를 눈으로 확인하는 SQL to JSON 변환기, 혹은 최소한 SQL 압축기로 공백·주석을 정리한 뒤 육안 검토하는 절차를 거치는 편이 안전합니다.
자주 묻는 질문
Q. "유효" 판정이 나오면 그대로 실행해도 안전한가요?
아닙니다. 괄호·따옴표·키워드 존재 3가지만 확인하므로 절 순서가 틀렸거나 존재하지 않는 컬럼·테이블을 참조해도 "유효"로 표시될 수 있습니다. 실제 실행 가능 여부는 별도로 확인해야 합니다.
Q. 왜 브라우저 도구는 진짜 파서를 안 쓰나요?
완전한 SQL 파서는 방언(MySQL/PostgreSQL/Oracle 등)마다 문법이 달라 구현이 무겁고, 서버 없이 순수 자바스크립트로 구현하려면 라이브러리 용량도 커집니다. 가벼운 정규식 기반 검사는 로드 속도와 구현 비용 면에서 실용적인 절충안입니다.
Q. 포맷 결과를 그대로 배포 코드에 써도 되나요?
컬럼명이 키워드와 겹치거나 문자열 리터럴 안에 키워드와 같은 단어가 있으면 의도치 않게 바뀔 수 있으므로, 포맷 후 diff를 눈으로 한 번 확인하고 사용하는 것을 권장합니다.
Q. 이 도구로 SQL 인젝션 취약점도 찾을 수 있나요?
아니요. 이 도구는 문자열 하나가 문법적으로 손상되지 않았는지만 보는 것이라 인젝션 패턴 탐지와는 목적이 다릅니다. 인젝션 방어는 파라미터 바인딩 등 애플리케이션 코드 수준에서 해결해야 합니다.