SQL 파라미터 바인딩, 값 대입 규칙 보기

?, :id, @name 같은 플레이스홀더가 들어간 SQL에 실제 값이 어떻게 대입되어 완성 쿼리가 되는지는, 규칙 네 가지만 알면 눈으로 따라갈 수 있습니다. 숫자와 null·true·false는 따옴표 없이 그대로, 문자열은 작은따옴표로 감싸고, 따옴표 안 문자와 주석은 건드리지 않는다 — 이게 핵심입니다.
이 글은 그 규칙을 손으로 적용하는 법과, 결과를 즉시 눈으로 확인하는 법을 정리합니다. 로그에 남은 ? 쿼리를 재현하려는 개발자, 드라이버가 값을 어떻게 채우는지 궁금한 사람에게 필요한 내용입니다.
요약 ① 순서형(
?,$1)은 값을 순서대로, 변수명형(:name@name$name)은 이름이 같은 것끼리, JSON은 배열이면 순서·객체면 이름으로 채웁니다. ② 숫자와null·true·false는 따옴표 없이, 나머지 문자열은 작은따옴표로 감싸며 PostgreSQL은'를''로 MySQL·SQLite는\'로 이스케이프합니다. ③ 문자열 상수'...'나 주석(--,/* */) 안의?·:name은 진짜 플레이스홀더가 아니므로 그대로 둡니다.
왜 값이 예상과 다르게 채워질까
애플리케이션 로그나 ORM 디버그 출력에는 값이 채워지기 전 쿼리가 그대로 찍히는 경우가 많습니다. WHERE id = ? 또는 WHERE id = :id 상태로요.
문제를 재현하려면 그 자리에 실제 값이 들어간 완성 쿼리가 필요한데, 값을 손으로 끼워 넣다 보면 실수가 생깁니다. 문자열에 따옴표를 빠뜨리거나, 이름이 안 맞는데 채워진 줄로 착각하는 식입니다.
특히 두 가지 상황에서 결과가 예상과 어긋납니다. 변수명 방식으로 짰는데 쿼리에 ?가 섞여 있으면 그 ?는 채워지지 않고, 순서형인데 값 개수가 플레이스홀더 수와 다르면 남거나 모자란 자리가 생깁니다. 왜 그런지는 규칙을 보면 분명해집니다.
도구 없이 직접 값을 대입하는 규칙
바인딩은 두 단계입니다. 어디가 진짜 플레이스홀더인지 찾고, 그 자리에 값을 형식에 맞게 써넣는 것입니다. 순서대로 규칙을 적용하면 손으로도 완성 쿼리를 만들 수 있습니다.
1단계 — 플레이스홀더 방식을 정합니다. 세 방식은 값을 서로 다르게 대응시킵니다.
- 순서형:
?와$1,$2… 를 값의 순서대로 채웁니다.$1은 첫 번째 값,$2는 두 번째 값입니다. - 변수명형:
:name,@name,$name을 이름이 같은 값과 짝지웁니다. 순서는 상관없습니다. - JSON: 배열
[...]을 주면 순서형처럼, 객체{...}를 주면 변수명형처럼 동작합니다.
2단계 — 값을 형식에 맞게 씁니다. 값의 "생김새"에 따라 따옴표 여부가 갈립니다.
- 순수 숫자(
25,-3,25.5)는 따옴표 없이 그대로 씁니다. null,true,false(대소문자 무관)는 각각NULL,TRUE,FALSE키워드로 씁니다.- 그 밖의 모든 값은 문자열로 보고 작은따옴표로 감쌉니다.
3단계 — 문자열 안의 따옴표를 이스케이프합니다. 값 안에 작은따옴표가 들어 있으면 DB마다 처리가 다릅니다.
- PostgreSQL:
'를''(작은따옴표 두 개)로 바꿉니다.O'Brien→'O''Brien' - MySQL·MariaDB·SQLite:
'를\'로, 역슬래시\는\\로 바꿉니다.O'Brien→'O\'Brien'
4단계 — 문자열 상수와 주석은 건드리지 않습니다. 손으로 할 때 가장 실수하기 쉬운 부분입니다.
쿼리 안에 이미 '?'처럼 따옴표로 감싼 물음표가 있거나, -- 메모 ?처럼 주석 안에 물음표가 있으면 그건 플레이스홀더가 아닙니다. '...' 안, -- 뒤 한 줄, /* */ 사이는 전부 원문 그대로 둡니다.
예를 들어 SELECT '?' AS mark FROM t WHERE id = ?에 순서형 값 5를 주면, 앞의 '?'는 그대로 두고 뒤의 ?만 5로 채웁니다. 결과는 SELECT '?' AS mark FROM t WHERE id = 5가 됩니다.
저희 도구로 확인하기.
설치·로그인 없이 브라우저에서 바로 실행됩니다. MySQL·PostgreSQL·SQLite 이스케이프 규칙과 순서형·변수명·JSON 입력을 지원합니다.

흔한 오해와 결과 읽는 법
값의 생김새에 따라 결과가 어떻게 표기되는지부터 정리하면 이렇습니다.
| 입력한 값 | 결과 표기 | 이유 |
|---|---|---|
| 25 | 25 | 순수 숫자는 따옴표 없이 |
| null / TRUE / false | NULL / TRUE / FALSE | 키워드로 인식(대소문자 무관) |
| administrator | 'administrator' | 문자열은 작은따옴표로 감쌈 |
| O'Brien (PostgreSQL) | 'O''Brien' | '를 ''로 이스케이프 |
| O'Brien (MySQL·SQLite) | 'O'Brien' | '를 \'로 이스케이프 |
변수명 방식인데 ?가 그대로 남는 경우. 변수명 방식은 이름으로만 값을 찾습니다. ?는 이름이 없으므로 채울 대상이 아니고, 그대로 ?로 남습니다. 위 화면의 기본 예시가 바로 이 상황입니다 — :min_age와 @user_role에는 값이 들어갔지만 ?는 그대로입니다. 순서형 자리와 변수명 자리를 한 쿼리에 섞었다면 방식을 하나로 통일해야 합니다.
이름이 서로 안 맞는 경우. 변수명 방식에서 쿼리의 :user_id와 값의 userId처럼 철자가 다르면 짝을 못 찾아 :user_id가 그대로 남습니다. 채워지지 않은 이름이 결과에 보이면 오타를 의심하면 됩니다.
순서형인데 값 개수가 안 맞는 경우. 플레이스홀더보다 값이 적으면 모자란 자리는 NULL로 채워지고, 값이 많으면 남는 값은 무시됩니다. 예를 들어 VALUES (?, ?, ?)에 값을 두 개(1, hello)만 주면 VALUES (1, 'hello', NULL)이 됩니다.
문자열 'null'을 넣고 싶은 경우. 값의 생김새로 판단하기 때문에, null이라고 쓰면 문자열이 아니라 NULL 키워드가 됩니다. 007처럼 앞자리 0이 있어도 순수 숫자 형태면 따옴표 없이 007로 나옵니다. 의도와 다르게 나올 수 있으므로 결과를 눈으로 확인하는 것이 안전합니다.
마지막으로 오해하지 말아야 할 것이 있습니다. 이 도구가 따옴표를 이스케이프한다고 해서 SQL 인젝션이 막히는 것은 아닙니다. 이것은 완성 쿼리를 눈으로 보기 위한 조합 시뮬레이터이고, 실제 서버 코드에서는 값을 문자열로 이어붙이지 말고 PreparedStatement나 드라이버의 네이티브 바인딩을 써야 합니다.
정리
- 세 방식은 값을 채우는 기준이 다릅니다. 순서형은 순서, 변수명형은 이름, JSON은 배열이면 순서·객체면 이름입니다.
- 값의 생김새가 따옴표를 결정합니다. 순수 숫자와
null·true·false는 그대로, 나머지는 작은따옴표로 감쌉니다. - 따옴표 이스케이프는 DB마다 다릅니다. PostgreSQL은
'', MySQL·SQLite는\'입니다. - 문자열 상수와 주석 안의
?·:name은 플레이스홀더가 아니므로 그대로 남습니다. - 이 도구는 결과를 확인하는 조합 시뮬레이터입니다. 실제 서버 코드에서는 PreparedStatement나 네이티브 파라미터 바인딩을 쓰는 것이 표준입니다.
설치·로그인 없이 브라우저에서 바로 실행됩니다. MySQL·PostgreSQL·SQLite 이스케이프 규칙과 순서형·변수명·JSON 입력을 지원합니다.
자주 묻는 질문
문자열이나 주석 안에 있는 ?도 값으로 바뀌나요?
아닙니다. 작은따옴표로 감싼 문자열 상수 '...', 한 줄 주석 --, 블록 주석 /* */ 안에 있는 ?나 :name은 진짜 플레이스홀더가 아니므로 그대로 둡니다. 도구가 쿼리를 글자 단위로 훑으면서 지금 위치가 문자열·주석 안인지 판단하기 때문에 쿼리가 망가지지 않습니다.
변수명 모드인데 ?가 안 바뀌고 그대로 있어요.
정상입니다. 변수명 모드는 이름(:name, @name, $name)이 같은 값만 채웁니다. ?는 이름이 없어 채울 대상이 아니라 그대로 남습니다. ?를 채우려면 순서형 모드로 바꾸거나, 쿼리의 플레이스홀더를 한 방식으로 통일하세요.
순서형인데 값 개수가 플레이스홀더보다 적으면 어떻게 되나요?
모자란 자리는 NULL로 채워지고, 값이 더 많으면 남는 값은 무시됩니다. 결과 쿼리에 예상치 못한 NULL이 보이면 값의 줄 수와 ?·$n의 개수가 맞는지 확인하면 됩니다.
이 도구로 만든 쿼리를 그대로 실행해도 되나요?
학습·재현·디버깅 용도의 조합 시뮬레이터로 보는 것이 맞습니다. 실제 애플리케이션에서는 값을 직접 이어붙이는 대신 PreparedStatement나 드라이버의 네이티브 바인딩을 사용하는 것이 표준 방식입니다.
