2026년 09월 09일 | DBMS Error 가이드
이 글에서 다루는 내용
42602 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42602 invalid name 는?
PostgreSQL 에러 코드 42602 (invalid_name) 은 데이터베이스 객체(테이블, 컬럼, 인덱스, 함수 등)의 이름이 PostgreSQL의 명명 규칙을 위반했을 때 발생하는 에러입니다. 식별자(identifier)는 문자, 숫자, 언더스코어로만 구성되어야 하며, 특수문자나 예약어를 무분별하게 사용하면 이 에러가 트리거됩니다. 주로 동적 SQL을 생성하거나, 외부 시스템에서 가져온 이름을 그대로 사용할 때, 혹은 마이그레이션 작업 중 비표준 식별자를 처리할 때 자주 마주치는 에러입니다.
주요 발생 원인
1. 식별자에 허용되지 않는 특수문자 사용
PostgreSQL 식별자는 기본적으로 영문자(a-z), 숫자(0-9), 언더스코어(_)만 허용하며, 반드시 문자 또는 언더스코어로 시작해야 합니다. 하이픈(-), 점(.), 공백, 달러 기호($) 등의 특수문자가 포함된 이름을 따옴표 없이 사용하면 파서가 이를 올바른 식별자로 인식하지 못해 42602 에러를 발생시킵니다. 특히 외부 API나 레거시 시스템에서 넘어온 컬럼명을 그대로 DDL에 삽입하는 경우 이 상황이 빈번하게 발생합니다.
2. 동적 SQL 생성 시 식별자 처리 미흡
PL/pgSQL 또는 애플리케이션 레이어에서 문자열 연결(concatenation)을 통해 SQL을 동적으로 생성할 때, 사용자 입력값이나 변수를 적절한 인용 처리 없이 식별자 위치에 삽입하면 에러가 발생합니다. quote_ident() 함수나 format() 함수의 %I 포맷 지시자를 사용하지 않으면 공백이나 특수문자가 포함된 이름이 파서를 혼란스럽게 만들 수 있습니다. 이는 보안 취약점(SQL Injection)으로도 이어질 수 있는 매우 중요한 문제입니다.
3. 숫자로 시작하는 식별자 사용
SQL 표준 및 PostgreSQL 명명 규칙에 따르면, 비따옴표(unquoted) 식별자는 반드시 문자 또는 언더스코어로 시작해야 합니다. 1st_column, 2024_report 처럼 숫자로 시작하는 이름을 큰따옴표 없이 사용하면 PostgreSQL 파서가 이를 숫자 리터럴의 일부로 해석하려다 실패하고, 결국 42602 에러를 반환합니다. 이 경우 큰따옴표로 감싸면 파싱은 성공하지만, 이후 매번 따옴표를 붙여야 하는 유지보수 부담이 생기므로 처음부터 올바른 명명 규칙을 따르는 것이 좋습니다.
해결 방법
원인 1: 특수문자 포함 식별자 처리
특수문자가 포함된 이름은 큰따옴표(")로 감싸서 사용할 수 있습니다. 단, 이 방법은 대소문자를 구분하게 되며, 이후 모든 참조에서 동일하게 따옴표를 사용해야 합니다.
-- 에러 발생: 하이픈이 포함된 테이블명
CREATE TABLE user-data (
id SERIAL PRIMARY KEY
);
-- ERROR: 42602: invalid name
-- 해결 방법 1: 큰따옴표로 감싸기
CREATE TABLE "user-data" (
id SERIAL PRIMARY KEY,
name TEXT
);
-- 이후 참조 시에도 따옴표 필수
SELECT * FROM "user-data";
-- 해결 방법 2 (권장): 명명 규칙을 준수한 이름으로 변경
CREATE TABLE user_data (
id SERIAL PRIMARY KEY,
name TEXT
);
-- 기존에 특수문자가 포함된 컬럼을 가진 테이블 마이그레이션
ALTER TABLE legacy_table RENAME COLUMN "first-name" TO first_name;
ALTER TABLE legacy_table RENAME COLUMN "last.name" TO last_name;
원인 2: 동적 SQL에서 안전한 식별자 처리
PL/pgSQL에서 동적 SQL을 생성할 때는 반드시 quote_ident() 또는 format()의 %I를 사용해야 합니다.
-- 잘못된 방법: 문자열 연결로 식별자 삽입 (에러 및 보안 취약)
DO $$
DECLARE
tbl_name TEXT := 'my-table'; -- 하이픈 포함
sql_stmt TEXT;
BEGIN
sql_stmt := 'SELECT * FROM ' || tbl_name; -- 위험!
EXECUTE sql_stmt;
END;
$$;
-- 올바른 방법 1: quote_ident() 사용
DO $$
DECLARE
tbl_name TEXT := 'my-table';
sql_stmt TEXT;
BEGIN
sql_stmt := 'SELECT * FROM ' || quote_ident(tbl_name);
-- 결과: SELECT * FROM "my-table"
EXECUTE sql_stmt;
END;
$$;
-- 올바른 방법 2 (권장): format() + %I 사용
DO $$
DECLARE
tbl_name TEXT := 'user data'; -- 공백 포함
col_name TEXT := 'first-name'; -- 하이픈 포함
sql_stmt TEXT;
BEGIN
sql_stmt := format('SELECT %I FROM %I WHERE id = $1', col_name, tbl_name);
-- 결과: SELECT "first-name" FROM "user data" WHERE id = $1
EXECUTE sql_stmt USING 1;
END;
$$;
-- 실전 예제: 동적 테이블 생성 함수
CREATE OR REPLACE FUNCTION create_partition_table(schema_name TEXT, table_name TEXT)
RETURNS VOID AS $$
DECLARE
full_table_name TEXT;
BEGIN
-- format + %I로 안전하게 식별자 구성
full_table_name := format('%I.%I', schema_name, table_name);
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %s (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW()
)', full_table_name
);
RAISE NOTICE 'Table % created successfully.', full_table_name;
END;
$$ LANGUAGE plpgsql;
-- 사용 예
SELECT create_partition_table('public', 'orders_2024');
원인 3: 숫자로 시작하는 식별자 수정
-- 에러 발생: 숫자로 시작하는 컬럼명
CREATE TABLE report (
2024_revenue NUMERIC(15,2)
);
-- ERROR: 42602: invalid name
-- 해결 방법 1: 큰따옴표 사용 (임시방편)
CREATE TABLE report (
"2024_revenue" NUMERIC(15,2)
);
-- 해결 방법 2 (강력 권장): 접두어 추가로 규칙 준수
CREATE TABLE report (
revenue_2024 NUMERIC(15,2), -- 연도를 뒤로
yr2024_revenue NUMERIC(15,2) -- 접두어 추가
);
-- 이미 생성된 컬럼 이름 변경
ALTER TABLE report RENAME COLUMN "2024_revenue" TO revenue_2024;
-- 이름 유효성 검사 쿼리 (사전 점검용)
SELECT
table_name,
column_name,
CASE
WHEN column_name ~ '^[0-9]' THEN '숫자로 시작'
WHEN column_name ~ '[^a-zA-Z0-9_]' THEN '특수문자 포함'
ELSE '정상'
END AS name_issue
FROM information_schema.columns
WHERE table_schema = 'public'
AND (
column_name ~ '^[0-9]'
OR column_name ~ '[^a-zA-Z0-9_]'
);
예방 방법
1. 식별자 명명 규칙 표준화 및 린팅 도입
팀 내 DDL 작성 가이드라인을 수립하고, 모든 식별자는 소문자 영문자, 숫자, 언더스코어만 사용하며 반드시 문자로 시작하도록 규칙을 강제하세요. CI/CD 파이프라인에 sqlfluff, pgTAP 등의 SQL 린팅 도구를 통합하면 비표준 식별자가 포함된 마이그레이션 스크립트가 프로덕션에 배포되기 전에 자동으로 탐지할 수 있습니다. 아래 쿼리를 주기적으로 실행하여 데이터베이스 내 비표준 식별자를 모니터링하는 것도 좋은 습관입니다.
-- 비표준 식별자 전체 점검 쿼리
SELECT
'table' AS object_type,
table_schema AS schema_name,
table_name AS object_name
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name ~ '[^a-z0-9_]' -- 소문자, 숫자, 언더스코어 외 문자 포함
UNION ALL
SELECT
'column' AS object_type,
table_schema AS schema_name,
column_name AS object_name
FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND column_name ~ '[^a-z0-9_]'
ORDER BY object_type, schema_name, object_name;
2. 동적 SQL 코드 리뷰 체크리스트 적용
동적 SQL이 포함된 모든 PL/pgSQL 함수나 애플리케이션 코드에 대한 코드 리뷰 시, 식별자 위치에 quote_ident() 또는 format()의 %I가 반드시 사용되었는지 확인하는 항목을 체크리스트에 포함시키세요. 값(value) 위치에는 $1, $2 파라미터 바인딩 또는 quote_literal()을 사용하는 것이 원칙이며, 이를 혼동하지 않도록 팀 내 교육을 주기적으로 진행하는 것이 중요합니다.
관련 에러
- 42601 (syntax_error): 식별자 문제가 구문 오류로 이어질 때 함께 발생하는 경우가 많습니다. 42602와 유사하게 파서 단계에서 감지됩니다.
- 42P01 (undefined_table): 따옴표 처리 불일치로 인해 대소문자가 맞지 않는 테이블명을 참조하면 발생합니다. 예를 들어
"MyTable"로 생성 후mytable로 접근하면 이 에러가 납니다. - 42703 (undefined_column): 동일한 이유(따옴표 처리 불일치)로 컬럼을 찾지 못할 때 발생하며, 42602 해결 후 연달아 마주칠 수 있는 에러입니다.
- 42501 (insufficient_privilege): 동적 SQL로 객체를 생성하거나 접근할 때 권한 문제가 겹쳐 발생하는 경우가 있으므로, 동적 SQL 디버깅 시 함께 확인하세요.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.