Oracle ORA-29275 오류 원인과 해결 방법 완벽 가이드

ORA-29275
2026년 10월 05일 | DBMS Error 가이드

이 글에서 다루는 내용

ORA-29275 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

ORA-29275 partial multibyte character 는?

ORA-29275 에러는 멀티바이트 문자(예: UTF-8, AL32UTF8 등)를 처리하는 과정에서 문자열이 도중에 잘려 불완전한 바이트 시퀀스가 발생했을 때 나타나는 오류입니다. 주로 CLOB, BLOB, VARCHAR2 데이터를 특정 바이트 길이로 자르거나 변환하는 과정에서 문자 경계를 무시하고 바이트 단위로 잘라낼 때 발생합니다. 특히 한국어, 중국어, 일본어처럼 한 문자가 2~4바이트를 차지하는 다국어 환경에서 SUBSTR, UTL_FILE, UTL_RAW 등의 함수를 사용할 때 자주 목격됩니다.


주요 발생 원인

1. SUBSTRB 또는 바이트 기반 문자열 처리 시 문자 경계 무시

SUBSTRB 함수는 문자(character) 단위가 아닌 바이트(byte) 단위로 문자열을 잘라냅니다. 한글이나 한자처럼 한 문자가 3바이트를 사용하는 경우, 중간 바이트에서 문자열을 자르면 불완전한 멀티바이트 문자가 남아 ORA-29275가 발생합니다.

예를 들어, '안녕하세요'를 SUBSTRB로 4바이트만 잘라내면 두 번째 문자(‘녕’)의 첫 번째 바이트만 남아 불완전한 시퀀스가 만들어집니다. 이 값을 이후 문자열 변환이나 출력 함수에 전달하면 에러가 발생합니다.

2. UTL_RAW 또는 UTL_FILE을 이용한 RAW 데이터 변환 시 문자 경계 오류

UTL_RAW.CAST_TO_VARCHAR2 또는 UTL_RAW.SUBSTR 함수를 사용하여 RAW 타입 데이터를 VARCHAR2로 변환하거나 일부분만 추출할 때, 멀티바이트 문자의 중간 바이트에서 잘리면 해당 에러가 발생합니다. RAW 데이터를 다루는 배치 프로그램이나 파일 I/O 처리 로직에서 자주 나타납니다.

특히 네트워크 또는 외부 시스템으로부터 수신한 RAW 데이터를 고정 크기 버퍼로 나누어 처리할 때 버퍼 크기가 문자 경계와 맞지 않으면 이 에러가 반복적으로 발생할 수 있습니다.

3. DBMS_LOB.SUBSTR을 사용한 CLOB 처리 시 바이트/문자 단위 혼용

DBMS_LOB.SUBSTR 함수는 기본적으로 문자 단위로 동작하지만, 내부적으로 데이터베이스 캐릭터 셋과 NLS 설정에 따라 바이트 변환 과정에서 문제가 생길 수 있습니다. CLOB 데이터를 VARCHAR2로 변환하는 과정에서 최대 길이(32767바이트)를 초과하거나 중간에 잘릴 때 멀티바이트 문자 경계 문제가 발생합니다.

또한, 대용량 CLOB 데이터를 페이징(Paging) 처리하거나 청크(Chunk) 단위로 나눌 때 청크 크기가 멀티바이트 문자의 경계와 정확히 맞지 않으면 이 에러가 발생합니다.


해결 방법

원인 1 해결: SUBSTRB 대신 SUBSTR 사용 또는 문자 경계 확인

문자 단위로 처리하는 SUBSTR을 사용하면 멀티바이트 문자 경계 문제를 피할 수 있습니다.

-- 잘못된 방법: SUBSTRB로 바이트 단위 자르기 (ORA-29275 유발 가능)
SELECT SUBSTRB('안녕하세요', 1, 4) FROM DUAL;

-- 올바른 방법: SUBSTR로 문자 단위 자르기
SELECT SUBSTR('안녕하세요', 1, 2) FROM DUAL;  -- '안녕' 반환

-- 바이트 기준으로 자르되 문자 경계를 보장하는 방법
-- LENGTHB로 바이트 길이를 확인하고 안전하게 처리
SELECT SUBSTR('안녕하세요', 1, 
    LENGTH(SUBSTRB('안녕하세요', 1, 6))
) FROM DUAL;

바이트 기반으로 반드시 잘라야 하는 경우에는 아래와 같이 안전한 함수를 만들어 사용합니다.

-- 멀티바이트 안전 SUBSTRB 대체 함수
CREATE OR REPLACE FUNCTION SAFE_SUBSTRB(
    p_str   IN VARCHAR2,
    p_start IN NUMBER,
    p_len   IN NUMBER
) RETURN VARCHAR2 IS
    v_result VARCHAR2(32767);
    v_byte_str VARCHAR2(32767);
BEGIN
    v_byte_str := SUBSTRB(p_str, p_start, p_len);
    -- 불완전한 멀티바이트 문자를 제거하기 위해 CONVERT 활용
    v_result := CONVERT(v_byte_str, 'AL32UTF8', 'AL32UTF8');
    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        -- 마지막 바이트를 하나씩 줄여가며 완전한 문자 경계 찾기
        RETURN SUBSTRB(p_str, p_start, p_len - 1);
END;
/

원인 2 해결: UTL_RAW 처리 시 문자 경계 보정

-- RAW 데이터를 VARCHAR2로 변환할 때 안전하게 처리
DECLARE
    v_raw   RAW(100);
    v_char  VARCHAR2(200);
BEGIN
    -- 테스트용 RAW 데이터 생성 (한글 '안녕' UTF-8 바이트)
    v_raw := UTL_RAW.CAST_TO_RAW('안녕하세요');
    
    -- 잘못된 방법: 중간 바이트에서 자르기
    -- v_char := UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.SUBSTR(v_raw, 1, 4));
    
    -- 올바른 방법: 전체 변환 후 문자 단위로 자르기
    v_char := UTL_RAW.CAST_TO_VARCHAR2(v_raw);
    v_char := SUBSTR(v_char, 1, 2);  -- 문자 단위 처리
    
    DBMS_OUTPUT.PUT_LINE('결과: ' || v_char);
END;
/

-- RAW 버퍼를 안전하게 나누는 방법
DECLARE
    v_raw_data  RAW(32767);
    v_chunk     RAW(1000);
    v_str       VARCHAR2(4000);
    v_offset    NUMBER := 1;
    v_chunk_size NUMBER := 990;  -- 멀티바이트 여유분 고려
BEGIN
    -- 실제 데이터 로드 로직 (예시)
    v_raw_data := UTL_RAW.CAST_TO_RAW('한글데이터샘플입니다테스트용도입니다');
    
    WHILE v_offset <= UTL_RAW.LENGTH(v_raw_data) LOOP
        v_chunk := UTL_RAW.SUBSTR(v_raw_data, v_offset, v_chunk_size);
        
        -- EXCEPTION 처리로 ORA-29275 핸들링
        BEGIN
            v_str := UTL_RAW.CAST_TO_VARCHAR2(v_chunk);
        EXCEPTION
            WHEN OTHERS THEN
                -- 청크 크기를 줄여 재시도
                v_chunk := UTL_RAW.SUBSTR(v_raw_data, v_offset, v_chunk_size - 3);
                v_str := UTL_RAW.CAST_TO_VARCHAR2(v_chunk);
        END;
        
        DBMS_OUTPUT.PUT_LINE(v_str);
        v_offset := v_offset + UTL_RAW.LENGTH(v_chunk);
    END LOOP;
END;
/

원인 3 해결: DBMS_LOB.SUBSTR 안전 처리

-- CLOB 데이터를 안전하게 VARCHAR2로 변환
DECLARE
    v_clob   CLOB;
    v_result VARCHAR2(4000);
    v_len    NUMBER;
    v_offset NUMBER := 1;
    v_amount NUMBER := 1000;  -- 문자 단위 청크 크기
BEGIN
    -- 테스트용 CLOB 생성
    v_clob := TO_CLOB('안녕하세요. 이것은 ORA-29275 테스트용 한글 데이터입니다. 멀티바이트 문자 처리를 안전하게 합니다.');
    v_len := DBMS_LOB.GETLENGTH(v_clob);
    
    WHILE v_offset <= v_len LOOP
        -- 문자(character) 단위로 읽기 (바이트 단위 아님)
        v_result := DBMS_LOB.SUBSTR(
            lob_loc => v_clob,
            amount  => LEAST(v_amount, v_len - v_offset + 1),  -- 문자 수
            offset  => v_offset  -- 문자 위치
        );
        
        DBMS_OUTPUT.PUT_LINE('청크: ' || v_result);
        v_offset := v_offset + v_amount;
    END LOOP;
END;
/

-- CLOB을 VARCHAR2로 변환하는 안전한 함수
CREATE OR REPLACE FUNCTION CLOB_TO_VARCHAR2(
    p_clob IN CLOB,
    p_max_chars IN NUMBER DEFAULT 4000
) RETURN VARCHAR2 IS
    v_result VARCHAR2(4000);
    v_len    NUMBER;
BEGIN
    v_len := DBMS_LOB.GETLENGTH(p_clob);
    IF v_len IS NULL OR v_len = 0 THEN
        RETURN NULL;
    END IF;
    
    -- 문자 단위로 안전하게 추출
    v_result := DBMS_LOB.SUBSTR(
        lob_loc => p_clob,
        amount  => LEAST(p_max_chars, v_len),
        offset  => 1
    );
    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('CLOB 변환 오류: ' || SQLERRM);
        RETURN NULL;
END;
/

-- 사용 예
SELECT CLOB_TO_VARCHAR2(clob_column, 2000) 
FROM your_table 
WHERE id = 1;

예방 방법

1. 문자 처리 함수는 반드시 문자(Character) 단위 함수 사용 원칙 수립

개발 가이드라인에 SUBSTRB, INSTRB, LENGTHB 등 바이트 기반 함수 사용을 금지하거나 반드시 문자 경계 검증 로직을 포함하도록 코딩 컨벤션을 정합니다. 대신 SUBSTR, INSTR, LENGTH 함수를 기본으로 사용하고, 바이트 단위 처리가 꼭 필요한 경우에는 전용 Wrapper 함수를 만들어 팀 내 공유합니다. 또한 NLS_CHARACTERSET을 AL32UTF8로 통일하여 예측 가능한 바이트 길이 환경을 조성합니다.

-- 현재 DB 캐릭터셋 확인
SELECT VALUE FROM NLS_DATABASE_PARAMETERS 
WHERE PARAMETER = 'NLS_CHARACTERSET';

-- 세션 레벨 NLS 설정 확인
SELECT * FROM NLS_SESSION_PARAMETERS 
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_LENGTH_SEMANTICS');

-- 길이 단위를 CHAR로 설정하여 VARCHAR2 선언 시 문자 기준 적용
ALTER SESSION SET NLS_LENGTH_SEMANTICS = CHAR;

2. 데이터 입력 및 변환 시 유효성 검증 로직 구현

RAW, CLOB 등 이진 또는 대용량 데이터를 문자열로 변환하기 전에 반드시 유효성 검증 함수를 통과하도록 아키텍처를 설계합니다. 아래와 같이 멀티바이트 유효성을 사전에 체크하는 함수를 공통 유틸리티로 등록하여 프로젝트 전반에 적용합니다.

-- 멀티바이트 문자열 유효성 검증 함수
CREATE OR REPLACE FUNCTION IS_VALID_MULTIBYTE(
    p_str IN VARCHAR2
) RETURN VARCHAR2 IS
    v_dummy VARCHAR2(32767);
BEGIN
    -- CONVERT를 통해 유효한 멀티바이트 문자열인지 확인
    v_dummy := CONVERT(p_str, 'AL32UTF8', 'AL32UTF8');
    RETURN 'VALID';
EXCEPTION
    WHEN OTHERS THEN
        RETURN 'INVALID: ' || SQLERRM;
END;
/

-- 데이터 입력 전 검증 예
SELECT column_name, IS_VALID_MULTIBYTE(column_name) AS validation_result
FROM your_table
WHERE IS_VALID_MULTIBYTE(column_name) != 'VALID';

관련 에러

  • ORA-06502: PL/SQL: numeric or value error: character string buffer too small — 문자열 버퍼 크기 부족으로 발생하며, ORA-29275와 함께 발생하는 경우가 많습니다. VARCHAR2 변수의 크기를 문자 단위로 충분히 선언해야 합니다.
  • ORA-12704: character set mismatch — 서로 다른 캐릭터셋 간 변환 시 발생하며, 멀티바이트 처리 중 자주 동반됩니다.
  • ORA-01461: can bind a LONG value only for insert into a LONG column — VARCHAR2 크기 초과 시 발생하며, CLOB 처리 중 ORA-29275와 연계될 수 있습니다.
  • ORA-00910: specified length too long for its datatype — 캐릭터셋 변경 후 컬럼 길이가 부족할 때 발생하며, 멀티바이트 환경 전환 시 함께 검토해야 합니다.
DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기