본문으로 건너뛰기

3. 응답 형식 및 에러 처리

V5 API의 모든 호출은 타입 DTO 또는 boolean/long을 반환합니다. 실패 시에는 다음과 같은 안전 기본값을 반환합니다.

3.1 V5 응답 패턴

메서드 패턴성공 반환실패 반환
create(req) / update(id, req) / get(id)*ResponseV5 인스턴스null
list() 계열List<*ResponseV5>빈 리스트 (절대 null 아님)
delete(id)truefalse
exists(id)true (있음)false (없거나 오류)
count() 계열long (≥0)0
Order 라이프사이클 (start/end/pause/resume/abort)true (성공)false (전이 불가 등)

예외가 throw되지 않습니다. HTTP 오류, 서버 ERROR, JSON 파싱 실패는 모두 위 표의 안전 기본값으로 흡수됩니다. 에러 원인은 디버그 로그나 envelope의 _code/_message에서 확인합니다.

3.2 내부 wire format (참고)

V5 서버는 다음과 같은 envelope 형태로 응답합니다. 클라이언트 라이브러리가 자동으로 파싱하므로, 일반 사용 시에는 신경 쓸 필요가 없습니다.

// 단건 성공
{ "_status": "OK", "data": { ... } }

// 목록 성공
{ "_status": "OK", "data": [ { ... }, { ... } ] }

// 목록 — items 키 안에 배열
{ "_status": "OK", "data": { "items": [ ... ] } }

// 에러
{
"_status": "ERROR",
"_code": "E1002",
"_message": "validation failed: ...",
"_http_status": 400
}

3.3 V5 ErrorCode 매핑

서버가 ERROR 응답을 보낼 때 _code 필드에 다음 코드가 들어옵니다.

코드의미일반적 원인
E1001INVALID_INPUT필수 필드 누락, 타입 불일치
E1002VALIDATION_FAILEDID 접두사 위반 (SITE_ 등)
E1100UNAUTHORIZEDapi_key 누락 또는 무효
E1101FORBIDDEN권한 부족
E1200CONFLICT중복 ID, FK 충돌
E1300NOT_FOUND해당 ID 없음
E1400DOMAIN_RULE_VIOLATIONOrder 상태 전이 불가 등
E1500DEPENDENCY_FAILED외부 시스템 응답 실패
E1900INTERNAL_ERROR서버 내부 오류

3.4 에러 정보 추출

V5 서비스 메서드는 envelope을 직접 노출하지 않으므로, 에러 상세는 다음 두 가지 방법으로 확인합니다.

방법 1 — 디버그 모드

APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, true);
client.connect();

SiteResponseV5 result = client.site().create(req);
if (result == null) {
// 콘솔 로그에서 envelope 확인 가능
System.err.println("등록 실패 — 디버그 로그 확인");
}

콘솔 출력:

[V5] POST /api/v5/site body={...}
[V5] response: 400 {"_status":"ERROR","_code":"E1002","_message":"site_id must start with SITE_"}

방법 2 — BaseServiceV5 static 메서드 사용 (커스텀 확장)

BaseServiceV5의 헬퍼는 JSONObject 응답에서 에러 정보를 추출합니다. 라이브러리를 확장해 envelope를 캡처하는 커스텀 서비스를 만들 때 유용합니다.

import plantpulse.api.v5.service.BaseServiceV5;
import plantpulse.json.JSONObject;

// 환경 (envelope) JSONObject 가 있다면
String code = BaseServiceV5.getErrorCode(env); // 예: "E1002"
String message = BaseServiceV5.getErrorMessage(env); // 예: "validation failed: ..."
int httpStatus = BaseServiceV5.getHttpStatus(env); // 예: 400

3.5 안전한 호출 패턴

CRUD 호출

import plantpulse.api.v5.dto.request.SiteRequestV5;
import plantpulse.api.v5.dto.response.SiteResponseV5;

SiteRequestV5 req = new SiteRequestV5();
req.setSite_id("SITE_DJ");
req.setSite_name("대전공장");

SiteResponseV5 created = client.site().create(req);
if (created == null) {
// 실패 처리 — 예: 알람, 재시도, 다른 경로 시도
log.warn("사이트 생성 실패");
return;
}
System.out.println("등록 완료: " + created.getSite_id());

목록 호출

List<SiteResponseV5> sites = client.site().list();
// 실패해도 null 이 아님 — 그대로 순회 가능
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id());
}
if (sites.isEmpty()) {
log.info("등록된 사이트 없음");
}

존재 확인 → 조회

if (client.site().exists("SITE_DJ")) {
SiteResponseV5 site = client.site().get("SITE_DJ");
// ...
}

Order 라이프사이클 (boolean 반환)

String orderId = "ORD_20260514_001";

if (!client.order().start(orderId)) {
// 전이 불가 (예: 이미 START 상태)
log.warn("작업 시작 실패 — 현재 상태 확인");
return;
}

// 작업 진행 ...

if (!client.order().end(orderId)) {
log.warn("작업 종료 실패");
}

3.6 재시도 패턴

V5는 자동 재시도를 하지 않습니다. 일시적 네트워크 오류를 대비한 재시도가 필요하면 직접 구현하시면 됩니다.

import java.util.function.Supplier;

public static <T> T callWithRetry(Supplier<T> apiCall, int maxRetries, long baseDelayMs) {
int attempt = 0;
while (true) {
T result = apiCall.get();
if (result != null) return result; // 성공
if (++attempt >= maxRetries) return null; // 포기
try {
Thread.sleep(baseDelayMs * attempt); // 지수 백오프
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return null;
}
}
}

// 사용
SiteResponseV5 site = callWithRetry(
() -> client.site().get("SITE_DJ"),
3,
1_000L);

3.7 빈 응답과 null 응답 구별

V5에서 null 또는 false는 다음 두 가지 의미를 모두 포함합니다:

  1. 서버에 데이터가 없음 (NOT_FOUND, E1300)
  2. 호출이 실패함 (네트워크 오류, 검증 실패 등)

이를 구별하려면 exists()를 함께 사용하시면 됩니다.

if (!client.site().exists("SITE_DJ")) {
log.info("사이트가 등록되어 있지 않습니다");
// 신규 등록 로직
} else {
SiteResponseV5 site = client.site().get("SITE_DJ");
if (site == null) {
log.error("사이트는 존재하지만 조회 실패 — 일시적 오류 가능");
}
}

다음 단계