본문 바로가기
SingleStoreDB/Support Bulletin

SingleStore vs PostgreSQL: 이스케이프 문자 처리 방식 차이 - [Support Bulletin 16]

by 에이플랫폼 [Team SingleStore Korea] 2025. 10. 27.

안녕하세요! 에이플랫폼 Support Bulletin의 열여섯 번째 이야기입니다. 😊

이번 시간에는 데이터 적재 시 '' (역슬래시) 문자가 일으키는 예상치 못한 오류에 대해 이야기해 보려 합니다.

이 문제는 '데이터 자체'의 결함보다는, 데이터베이스마다 SQL 표준을 구현하고 문자열을 해석하는 방식이 미묘하게 다르기 때문에 발생합니다.

특히 이스케이프 문자(Escape Character)인 의 처리 방식이 대표적인 예시이죠.

그래서 이번 글에서는 SingleStore와 PostgreSQL이 ∖ 문자를 파싱(Parsing)하는 메커니즘이 어떻게 다른지, 그리고 그 차이가 실제 데이터 적재 과정에서 어떤 문제를 일으켰는지 실제 사례와 함께 해결 과정을 공유해 드리고자 합니다.

 

문제는 PostgresSQL (이하 PSQL)에서 COPY 명령어로 추출한 데이터를 SingleStore로 적재하는 과정에서 시작되었습니다.

데이터 파이프라인은 겉보기에 정상적으로 작동했지만, 적재된 데이터를 확인해 보니 일부 데이터가 원본과 다르게 저장되는 현상을 발견했습니다.

바로 ∖12 라는 문자열이 포함된 데이터였습니다.

이해를 돕기 위해, 실제 데이터를 두 데이터베이스에 적재하며 문제 상황을 재현해 보겠습니다.

다음과 같이 ∖12 가 포함된 텍스트 파일(test_data.txt)이 준비되어 있다고 가정합니다.

# test_data.txt
First\12Second

결과를 조회하자, ∖12가 아닌 12만 남는 현상이 발생했습니다.  문자가 사라진 것입니다.

 

  • SingleStore 결과
# 예시 테이블 생성
CREATE TABLE escape_test (content TEXT);

# 데이터 적재
LOAD DATA INFILE '/S2DB/nodes/db_files/test_data.txt'
INTO TABLE escape_test
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';

# 결과
singlestore> select * from escape_test;
+---------------+
| content       |
+---------------+
| First12Second |
+---------------+
1 row in set (0.08 sec)

반면 PSQL의 결과는 완전히 달랐습니다. ∖12 가 줄 바꿈 문자로 해석되어 데이터가 두 줄로 나뉘어 저장되었습니다.

 

  • PSQL 결과
# 예시 테이블 생성
CREATE TABLE escape_test (content TEXT);

# 데이터 적재
COPY escape_test FROM '/S2DB/nodes/db_files/test_data.txt';

# 결과
test_db=# select * from escape_test;
 content
---------
 First  +
 Second
(1 row)

이러한 차이는 두 DB가 를 해석하는 방식이 근본적으로 다르기 때문입니다.


PSQL의 '옥탈 이스케이프(Octal Escape)' 지원

PSQL은 문자열을 처리할 때 C언어 스타일의 이스케이프 시퀀스(Escape Sequence)를 폭넓게 지원합니다.

여기에는 ∖n(줄 바꿈) 뿐만 아니라, 뒤에 8진수 숫자를 사용해 특정 문자를 표현하는 옥탈(Octal) 이스케이프 기능이 포함됩니다.

  • ∖digits (옥탈, 8진수): 12는 8진수 12를 의미하며, 이는 10진수 10에 해당합니다. 아스키(ASCII) 코드에서 10번은 줄 바꿈(Line Feed, ∖n) 문자입니다.

따라서 PSQL은 12를 '줄 바꿈'이라는 특수 문자로 올바르게 해석한 것입니다.

 

SingleStore의 '리터럴 이스케이프(Literal Escape)'

반면 SingleStore(와 MySQL)는 n, r, b 등 미리 정의된 몇몇 이스케이프 시퀀스만 지원합니다.

8진수(digits) 이스케이프 기능은 기본적으로 해석하지 않습니다.

SingleStore의 LOAD DATA 파서는 First12Second라는 데이터를 만났을 때, 다음과 같이 동작합니다.

  1. 기본 이스케이프 문자인 를 만납니다.
  2. 뒤따르는 문자 1을 확인합니다.
  3. 1은 미리 정의된 이스케이프 시퀀스( n, t 등)가 아니므로, ∖ 문자만 제거하고 뒤따르는 문자 1을 그대로 남깁니다.
  4. 다음 문자 2는 일반 문자로 인식합니다.
  5. 그 결과 First + 1 + 2 + Second가 조합되어 First12Second가 테이블에 저장됩니다.

결국, 원본 PSQL에서는 '줄 바꿈'으로 의도되었던 12가, 대상인 SingleStore에서는 이스케이프 문자가 소실된 '12'라는 텍스트로 저장되었습니다.

이는 단순히 문자열이 그대로 저장되는 것(예: First12Second)을 넘어, 원본 데이터의 ∖가 소실되는 심각한 데이터 정합성 문제를 야기한 것입니다.


이처럼 데이터베이스의 미묘한 메커니즘 차이로 인한 데이터 유실은 매우 치명적입니다. 그렇다면 이 문제는 어떻게 해결해야 할까요? 두 가지 관점에서 해결 방안을 모색했습니다.

 

해결 방안 1: LOAD DATA 옵션 변경으로 이스케이프 비활성화 (즉각적인 해결책)

SingleStore의 LOAD DATA 구문은 기본적으로 를 이스케이프 문자로 사용합니다. 이번 문제(First12Second -> First12Second)는 이 기본값 때문에 발생했습니다.

만약 원본 데이터의 문자를 특수 기능 없이 그대로(Literal) 적재하고 싶다면, LOAD DATA 명령어에 ESCAPED BY '' 옵션을 추가하여 이스케이프 기능을 비활성화할 수 있습니다.

# 데이터 적재
LOAD DATA INFILE '/S2DB/nodes/db_files/test_data.txt'
INTO TABLE escape_test
FIELDS TERMINATED BY ','
ESCAPED BY '' 
LINES TERMINATED BY '\n';

# 결과
singlestore> select * from escape_test;
+----------------+
| content        |
+----------------+
| First12Second  |  # 이전 데이터
| First\12Second |
+----------------+
2 rows in set (0.00 sec)
 

이 방법은 PSQL의 '줄 바꿈' 해석과는 여전히 다르지만, 적어도 원본 데이터의 가 소실되는 최악의 상황은 막을 수 있습니다.

 

해결 방안 2: 데이터 파이프라인 전처리 (근본적인 해결책)

더 근본적인 해결책은 데이터 파이프라인 중간에 변환(Transform) 단계를 두는 것입니다.

두 DB의 해석 방식이 다르다면, 데이터를 적재하기 전에 한쪽의 표준으로 통일(Normalize)하는 것입니다.

  • Case A: '줄 바꿈'이 의도였다면? PSQL의 12가 '줄 바꿈'을 의도한 것이라면, SingleStore에 적재하기 전에 12를 SingleStore가 이해하는 n으로 모두 치환해야 합니다.
  • Case B: '문자열 그대로'가 의도였다면?First12Second라는 문자열 자체가 의도된 값이라면, PSQL에서 COPY로 추출할 때부터 First∖∖12Second와 같이 가 이스케이프된 상태로 추출되도록 처리해야 합니다.

이처럼 ETL/ELT 파이프라인 중간에 Python, Spark 등을 이용한 전처리 스크립트를 추가하여 데이터를 정리하면, 양쪽 DB의 메커니즘 차이에 관계없이 항상 일관된 데이터를 보장할 수 있습니다.


이번 시간에는 SingleStore와 PostgreSQL이 (역슬래시) 문자를 해석하는 방식의 차이로 인해 발생할 수 있는 심각한 데이터 정합성 문제와 그 해결 방안을 살펴보았습니다.

사소해 보이는 이스케이프 문자 하나가 데이터베이스 환경에 따라 전혀 다르게 해석되어, 원본 데이터의 유실까지 이어질 수 있다는 점을 실제 사례로 확인할 수 있었습니다.

앞으로도 데이터 마이그레이션이나 이기종 DB 간의 파이프라인을 구축할 때는, 눈에 보이는 데이터 값뿐만 아니라 '보이지 않는' 이스케이프 문자의 처리 정책까지 면밀히 검토하는 것이 안정적인 시스템 구축의 핵심이라는 점을 강조하며 이번 호를 마칩니다.

다음 Support Bulletin에서도 유용한 기술 정보로 찾아뵙겠습니다. 감사합니다. 😊