자동화 slecs

상 목록 조회가 화면에 안 띄우던 alias 예약어 버그

목차

지난주에 상 조회 기능 데이터가 DB에서는 정상 반환되는데 화면에 나타나지 않는 버그를 잡았다. 원인은 SQL 쿼리의 alias가 DB의 예약어였기 때문. 백틱으로 처리해서 해결했는데, 이런 류 버그가 왜 자꾸 튀어나오는지, 어떻게 더 일찍 catch할 수 있을지 생각해보게 됐다.

증상과 원인 추적

상 정보를 조회하는 findAwardsByGroup 쿼리가 이상했다. 개발자 도구로 네트워크 응답을 보니 데이터가 들어있고, DB 로그도 정상. 그런데 화면에선 그 데이터가 렌더되지 않는 거다. "미렌더(unrendered)"라고 부르는 이 현상은 보통 데이터 구조가 프론트엔드의 기대와 안 맞을 때 발생한다.

디버깅하다 보니 src/lib/db.ts의 쿼리 alias가 문제였다. 상 정보를 조회할 때 AS show라고 alias를 붙였는데, show는 SQL의 예약어다. 데이터베이스가 이 예약어를 제대로 인식하지 못하면서, 쿼리 결과의 컬럼 이름이 꼬여버린 거다. 프론트엔드는 예상한 컬럼명(예: show.id, show.name)을 못 찾으니까 당연히 렌더할 데이터가 없었던 것.

SQL 예약어와 백틱 처리

이 버그의 핵심은 SQL 예약어의 동작 방식을 모르면 헷갈리기 쉽다는 점이다. 대부분의 SQL 데이터베이스(MySQL, PostgreSQL, MSSQL 등)는 예약어를 식별자(테이블명, 컬럼명, alias)로 쓰지 못하도록 막는다. 이건 SQL 문법 충돌을 피하고 파싱을 정확하게 하기 위한 설계다.

-- 문제가 되는 쿼리
SELECT awards.id, awards.name AS show FROM awards;
-- MySQL 에서는 "show" 예약어 때문에 alias 컬럼명이 제대로 매핑 안 됨

-- 백틱으로 처리
SELECT awards.id, awards.name AS `show` FROM awards;
-- alias 컬럼명이 정상적으로 "show"로 반영됨

백틱(backtick )은 MySQL 표준이고, 다른 DB는 큰따옴표("`)를 쓰기도 한다. 우리 시스템이 MySQL 기반이어서 백틱으로 처리했는데, DB 추상화 라이브러리(ORM 같은 걸 안 쓰면)를 쓰지 않을 땐 이런 디테일을 매번 신경 써야 한다.

왜 이런 버그가 계속 나올까

이 버그가 발생한 근본 원인은 몇 가지다:

1. 예약어 리스트가 방대하다. SQL은 수십 개의 예약어를 갖고 있고(SELECT, WHERE, ORDER, GROUP, SHOW, CREATE 등), 실제 쿼리를 짤 때 "이게 예약어인지" 매번 확인하기 어렵다. 특히 show 같은 흔한 단어가 예약어인 경우 실수하기 쉽다.

2. 로컬 개발/테스트에서 안 튀어난다. 팀이 쓰는 dev 환경과 실 환경의 DB 설정이나 버전이 조금 다르면, 로컬에선 되는데 스테이징/프로덕션에선 안 되는 일이 생긴다. 예약어 처리도 DB 버전과 설정에 따라 완화 정도가 다를 수 있다.

3. 쿼리 빌더 없이 raw SQL을 쓸 때 노출된다. ORM이나 쿼리 빌더(예: Knex, Sequelize)를 쓰면 대부분 예약어를 자동으로 이스케이프한다. 하지만 우리처럼 raw SQL을 조립하는 방식이면 손으로 관리해야 한다.

코드리뷰와 자동화

이번 버그를 계기로 팀 내에서 논의한 게 몇 가지다.

코드리뷰 레벨: DB 쿼리를 PR에서 볼 때, alias나 컬럼명이 SQL 예약어는 아닌지 한번 더 체크하는 습관을 들여야 한다. 리뷰어가 매번 전체 예약어 리스트를 암기하고 있긴 어렵지만, "이 이름이 일반적인 SQL 키워드는 아닌지"라는 질문만 던져도 많이 걸러진다.

자동화: 쿼리를 정적 분석(linting)하는 도구를 도입할 수도 있다. 예를 들어 쿼리 문자열에서 alias나 컬럼명을 추출해서 예약어 리스트와 대조하는 간단한 스크립트. 또는 더 근본적으로는 ORM/쿼리 빌더로 마이그레이션하는 것도 고려할 만하다.

테스트: 단위 테스트에서 쿼리 결과의 컬럼명을 검증하는 단계를 넣으면 "alias가 제대로 매핑됐는가"를 자동으로 확인할 수 있다.

일반론: SQL 예약어 처리

비슷한 버그를 방지하려면:

  • alias는 명시적으로. 생략하지 말고 항상 AS 이름 형태로 명시. 함수 결과도 COUNT(*) AS total 같이.
  • 예약어 의심스러우면 이스케이프. 확실하지 않으면 백틱/따옴표 처리. 오버헤드도 거의 없다.
  • 여러 DB 지원하면 더 신경 쓰기. PostgreSQL은 "이름", SQLite는 백틱 같이 DB마다 문법이 다르다. 쿼리 빌더가 유용한 이유.

번외: 왜 raw SQL을 쓸까

좀 더 근본적인 얘기를 하면, 우리가 raw SQL을 쓰는 이유는 쿼리가 복잡하고 성능이 중요하기 때문. 쿼리 빌더 오버헤드를 피하거나, DB-specific 기능(윈도우 함수, JSON 쿼리 등)을 직접 써야 할 때다. 하지만 이렇게 하면 이런 류 버그의 책임이 전부 우리한테 온다. 그래서 raw SQL을 쓸 땐 쿼리 리뷰, 테스트, 문서화를 더 철저히 해야 한다는 교훈.

다음 스텝

이번 수정 후로 팀 리뷰 체크리스트에 "SQL 예약어 확인" 항목을 추가하기로 했다. 또한 비슷한 케이스가 있을 수 있으니 src/lib/db.ts 전체를 한 번 더 스캔해볼 계획. 장기적으로는 정적 분석 도구 도입도 고려 중이다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.