8. 에러 응답과 에러 코드#
8.1 오류 응답 형식#
오류가 발생하면 DataAPI는 다음 형식의 JSON 응답을 반환한다. details 필드는 선택 사항이며, 오류 종류에 따라 포함되지 않을 수 있다.
| 필드 | 설명 |
|---|---|
| code | DataAPI 오류 코드 |
| message | 오류 메시지 |
| path | 요청 URL |
| timestamp | 오류 발생 시간 |
| details | 추가 오류 정보(선택) |
예시:
{
"code": "DATA_003",
"message": "SQL 문 실행에 실패했습니다.",
"path": "/dataapi/query/records",
"timestamp": "2026-03-03T06:10:31.123Z",
"details": [
"SQLSTATE:23505",
"VENDOR_CODE:-69720",
"SQLSTATE_TEXT:DB 제약조건 위반이 발생했습니다."
]
}
8.2 details 필드#
details 필드는 오류에 대한 추가 정보를 제공하기 위해 사용된다.
| 상황 | 대표 코드 | details 성격 |
|---|---|---|
| 요청값 검증 실패 | COMMON_001 | 필드별 검증 메시지를 field: message 형태로 포함할 수 있다. |
| 요청 본문 형식 오류 | COMMON_002 | 보통 생략한다. |
| DB 사용자 인증 실패 | AUTH_008 | 원인이 SQLException 계열이면 안전한 SQL 메타데이터만 포함할 수 있다. |
| DB 커넥션 대여 실패 | DATA_002 | 원인이 SQLException 계열이면 안전한 SQL 메타데이터만 포함할 수 있다. |
| SQL 실행 실패 | DATA_003 | 원인이 SQLException 계열이면 안전한 SQL 메타데이터만 포함할 수 있다. |
| 그 외 에러 | 기타 코드 | 기본적으로 생략한다. |
SQL 오류와 관련된 details 항목은 데이터베이스의 상세 내부 정보가 그대로 노출되는 것을 방지하기 위해 원본 JDBC 에러 메시지를 직접 출력하지 않는다. 대신, 외부 노출에 안전한 다음과 같은 정형 메타데이터 포맷으로만 제공된다.
SQLSTATE:<code>
VENDOR_CODE:<code>
SQLSTATE_TEXT:<safe hint>
8.3 오류 분류표#
| 오류 분류 | 대표 코드 | HTTP 상태 | 분류 기준 | details |
|---|---|---|---|---|
| Authorization 헤더 없음 | AUTH_001 | 401 | 인증 헤더가 필요한 요청에서 헤더가 없거나 logout 요청의 Bearer 형식이 맞지 않음 | 보통 없음 |
| Refresh 토큰 차단 | AUTH_002 | 401 | 로그아웃 또는 rotation 이후 블록리스트에 등록된 Refresh 토큰 사용 | 보통 없음 |
| 토큰 유효성 실패 | AUTH_003 | 401 | JWT 문자열이 유효하지 않거나 만료되었거나 session reset cutoff 이전에 발급됨 | 보통 없음 |
| Refresh 토큰 타입 오류 | AUTH_004 | 401 | Refresh 전용 토큰이 필요한 경로에 다른 토큰 사용 | 보통 없음 |
| Access 토큰 타입 오류 | AUTH_005 | 401 | Access 전용 토큰이 필요한 경로에 다른 토큰 사용 | 보통 없음 |
| Access/Refresh 사용자 또는 세션 불일치 | AUTH_006 | 401 | logout 등에서 Access 토큰과 Refresh 토큰의 사용자 또는 세션 정보가 다름 | 보통 없음 |
| 세션 풀 만료 또는 부재 | AUTH_007 | 401 | 서버 인스턴스에 사용자 세션 풀 또는 현재 JWT 세션/refresh rotation 상태가 없음 | 보통 없음 |
| DB 사용자 인증 실패 | AUTH_008 | 401 | DB 사용자명/비밀번호 검증 실패 | SQL 메타데이터 가능 |
| 인증되지 않은 요청 | AUTH_009 | 401 | 인증 객체가 없거나 Spring Security 인증 실패가 일반 unauthorized로 매핑됨 | 보통 없음 |
| Refresh 토큰 재사용 | AUTH_010 | 401 | 이미 사용되었거나 교체된 Refresh 토큰 사용 | 보통 없음 |
| 지원하지 않는 인증 스킴 | AUTH_011 | 401 | Bearer, Basic 외 Authorization 스킴 사용 | 보통 없음 |
| Basic 자격증명 형식 오류 | AUTH_012 | 401 | Basic 헤더 디코딩 또는 username:password 형식 오류 | 보통 없음 |
| 접근 권한 없음 | AUTH_013 | 403 | 필요한 role이 없는 사용자가 보호 엔드포인트 접근 | 보통 없음 |
| 활성 JWT 세션 충돌 | AUTH_014 | 409 | 사용자별 활성 JWT 세션 풀이 이미 있어 새 로그인 거부 | 보통 없음 |
| SQL API 미구현 | DATA_001 | 501 | 구현되지 않은 SQL 실행 API 호출 | 보통 없음 |
| DB 커넥션 대여 실패 | DATA_002 | 500 | 사용자 풀에서 SQL 실행용 DB 커넥션을 대여하지 못함 | SQL 메타데이터 가능 |
| SQL 실행 실패 | DATA_003 | 500 | SQL 실행 중 DB 오류 또는 실행 오류 발생 | SQL 메타데이터 가능 |
| 조회 결과 집합 없음 | DATA_004 | 400 | 조회 엔드포인트에 결과 집합을 반환하지 않는 SQL 요청 | 보통 없음 |
| 실행 엔드포인트 결과 집합 반환 | DATA_005 | 400 | /execute에서 결과 집합을 반환하는 SQL 요청 | 보통 없음 |
| 스트리밍 지속 시간 초과 | DATA_006 | 500 | 스트리밍 응답이 설정된 최대 지속 시간을 초과 | stream 시작 전이면 JSON, 시작 후에는 partial NDJSON 가능 |
| buffered LOB inline 제한 초과 | DATA_007 | 400 | buffered 조회 응답에서 BLOB/CLOB 값을 inline 제한 안에 담을 수 없음 | 메시지 placeholder에 타입, 실제 크기, 제한값 포함 |
| execute batch DML 제한 | DATA_008 | 400 | /execute batch에 DML이 아닌 SQL 요청 | 보통 없음 |
| records 중복 컬럼명 | DATA_009 | 400 | /query/records 또는 /stream/records 결과에 같은 컬럼 label이 반복됨 | 메시지 placeholder에 중복 label 포함 |
| 요청값 검증 실패 | COMMON_001 | 400 | Bean Validation 또는 필드 검증 실패 | 필드별 검증 메시지 가능 |
| 요청 본문 형식 오류 | COMMON_002 | 400 | JSON 파싱 또는 요청 본문 변환 실패 | 보통 없음 |
| 서버 내부 오류 | COMMON_003 | 500 | 별도 코드로 분류되지 않은 내부 런타임 오류 | 보통 없음 |
8.4 스트리밍 오류 처리#
스트리밍 엔드포인트는 실시간으로 데이터를 전송하는 특성상, 오류가 발생한 시점에 따라 응답 방식이 달라진다. 클라이언트는 다음 세 가지 경우를 고려하여 구현해야 한다.
응답 전송 시작 전 실패한 경우#
스트림이 전송되기 전에 오류가 발생하면, 표준 JSON 구조의 ErrorResponse를 반환한다.
예시
{"code":"DATA_003","message":"SQL 문 실행에 실패했습니다.","path":"/dataapi/stream/records","timestamp":"..."}
스트림 전송 중 오류 (에러 코드 반환 가능)#
스트림 전송이 시작된 이후, 직전 NDJSON 객체의 전송이 완료된 후 다음 객체를 전송하기 전에 오류가 발생하면 NDJSON형식의 오류 객체를 추가하여 반환할 수 있다. 이 경우 complete 필드가 포함된 객체는 전송되지 않는다.
예시
{"record":{"ID":1,"NAME":"Alice"}}
{"error":true,"code":"DATA_003","message":"SQL 문 실행에 실패했습니다."}
스트림 전송 중 오류(에러 코드 반환 불가)#
스트림 전송이 시작되고 NDJSON 객체를 전송하는 도중 오류가 발생하면, 이미 일부 JSON 데이터가 전송되었을 수 있으므로 오류 객체를 추가하여 반환할 수 없다. HTTP 연결 특성상 중간에 HTTP 상태 코드를 변경하거나 일반 JSON 포맷으로 에러 응답을 보낼 수 없기 때문이다.
이 경우 연결은 불완전한 NDJSON 상태에서 종료되며, complete 필드가 포함된 객체도 전송되지 않는다.
예시:
{"record":{"ID":1,"NAME":"Alice"}}
{"record":{"ID":2,"NAME":
클라이언트 구현 시 주의사항#
스트리밍 응답은 항상 마지막 라인에 complete 필드가 포함된 객체가 전송되어야 정상적으로 종료된 것으로 간주한다. complete 필드가 없으면 '정상 종료'가 아닌 '비정상적인 전송 실패'로 간주하고 예외 처리를 수행해야 한다.