본문으로 건너뛰기

1. 시작하기

V5 Java API 클라이언트를 사용해 첫 호출까지 보내는 단계를 안내합니다.

빠른 시작 (60초)​

필요한 것 3가지

항목값어디서
서버 주소http://<서버IP>:80 (TLS 는 https://<서버IP>:443)REST API 엔드포인트. 웹 콘솔과 같은 주소입니다
계정 ID예: apiAPI 호출용 사용자 계정
토큰UUID 형식콘솔 보안 관리 → API 인증 토큰(/token/index) 에서 그 계정으로 발급 → 보안 설정 · 보안 관리

세 값만 있으면 아래 한 블록으로 첫 호출이 됩니다.

import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;

try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", "<발급받은-토큰>", false)) {
client.connect(); // 인증
System.out.println(client.system().ping().isPing()); // → true
for (SiteResponseV5 s : client.site().list()) // 사이트 목록
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}

이 블록이 true 와 사이트 목록을 출력하면 준비 완료입니다. 아래 1.1~1.7 에서 각 단계를 자세히 설명합니다.

1.1 의존성 추가​

JAR 파일을 클래스패스에 추가하시면 됩니다.

# 빌드 (Gradle + Java 21)
cd plantpulse-api
./gradlew build

산출물은 target/ 에 생성됩니다.

파일용도
target/plantpulse-api.jar기존 프로젝트에 의존성과 함께 통합할 때

1.2 클라이언트 생성​

APIClient_V5 생성자에 옵션을 직접 전달합니다.

import plantpulse.api.v5.APIClient_V5;

APIClient_V5 client = new APIClient_V5(
"HTTPS", // 프로토콜: "HTTP" 또는 "HTTPS"
"100.68.69.41", // host
443, // port (REST API: 80=HTTP, 443=HTTPS)
"api", // user_id
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", // token (api_key 발급용)
false); // debug

client.connect(); // 인증 + HTTP 클라이언트 초기화

// ... API 호출 ...

client.close(); // 종료

생성자 파라미터​

파라미터타입설명
protocolString"HTTP" 또는 "HTTPS"
hostString서버 호스트 또는 IP
portint서버 포트
user_idString인증 계정 ID
tokenString발급된 토큰 (UUID 형식)
debugbooleanHTTP 요청·응답 디버그 로그

인증 흐름: connect() 호출 시 내부에서 user_id + token 으로 api_key를 발급받고, 이후 모든 호출에서 그 키를 자동으로 헤더에 포함시킵니다. 사용자가 직접 다룰 필요는 없습니다.

1.3 첫 API 호출 — ping & 사이트 목록​

import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
import plantpulse.api.v5.dto.response.SystemPingResponseV5;

public class FirstCall {
public static void main(String[] args) throws Exception {

try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api",
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", false)) {

client.connect();

// 1) 서버 헬스체크
SystemPingResponseV5 ping = client.system().ping();
System.out.println("Server alive: " + ping.isPing()
+ " (v" + ping.getVersion() + ")");

// 2) 사이트 목록 (typed DTO 리스트)
List<SiteResponseV5> sites = client.site().list();
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
}
}
}

실행 결과:

Server alive: true (v5.0)
SITE_000001 — 대전 1공장
SITE_000002 — 천안 2공장

1.4 try-with-resources 패턴​

APIClient_V5는 AutoCloseable을 구현합니다. try-with-resources 사용을 권장합니다.

try (APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, false)) {
client.connect();
// ... API 호출 ...
}
// close() 자동 호출 — HTTP 커넥션 정리

1.5 서비스 13개 한눈에 보기​

connect() 직후 아래 서비스 메서드로 모든 도메인에 접근할 수 있습니다 (lazy 초기화).

client.system() // 헬스체크
client.site() // 사이트(공장)
client.customer() // 고객
client.employee() // 직원
client.product() // 제품
client.asset() // 자산 계층 (Area → Line → Equipment)
client.opc() // OPC 데이터 채널
client.tag() // 태그(데이터 포인트)
client.alarm() // 알람
client.order() // 작업지시
client.calendar() // 일정
client.path() // 자산 경로
client.flow() // Flow / Flow Node

1.6 V5 메서드 시그니처 표준 패턴​

모든 도메인 서비스는 일관된 메서드 패턴을 따릅니다. 도메인 이름만 다를 뿐 사용법은 동일합니다.

패턴반환 타입실패 시 반환값설명
create(req)*ResponseV5null등록
update(id, req)*ResponseV5null전체 수정
delete(id)booleanfalse삭제
get(id)*ResponseV5null단건 조회
list()List<*ResponseV5>빈 리스트전체 목록
exists(id)booleanfalse존재 여부
count()long0카운트

⚠️ 예외가 throw 되지 않습니다. 통신 오류나 서버 ERROR 응답은 위 표의 안전 기본값으로 반환됩니다. 에러 원인은 로그 또는 BaseServiceV5.getErrorCode(env) 로 확인하세요. 상세 내용은 응답 형식 및 에러 처리 참고.

1.7 디버그 모드​

생성자 마지막 인자를 true 로 주면 HTTP 요청·응답이 콘솔에 출력됩니다.

APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", TOKEN, true); // debug=true

출력 예:

[V5] GET https://server/api/v5/site headers={api_key=ab****cd}
[V5] response: 200 {"_status":"OK","data":[{"site_id":"SITE_000001",...}]}

1.8 인증이 실패했을 때 — 503 은 「키가 틀렸다」가 아니다​

connect() 는 POST /api/v5/auth 로 토큰을 api_key 와 교환합니다. 성공 판정은 HTTP 200 이 아닙니다 — meta.status 가 "OK" 이고 data.api_key 가 비어 있지 않아야 성공입니다. 둘째 조건이 따로 있는 이유는, 토큰 검사는 통과했는데 키 발급이 실패하는 경로가 실제로 있기 때문입니다(그때 서버는 500 을 냅니다).

curl -s -X POST "http://<서버>/api/v5/auth" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"token":"<발급받은-토큰>"}'
# 200: {"data":{"api_key":"..."},"meta":{"status":"OK"},"errors":null}
응답코드뜻무엇을 고칩니까
400E1001token 이 없거나 비었습니다요청 본문
422E1002본문이 JSON 이 아닙니다요청 본문
401E1100토큰이 틀립니다자격증명
401E1102토큰 만료재발급하면 복구됩니다
401E1103토큰 회수됨같은 토큰은 복구되지 않습니다 — 새로 받으십시오
429E1101같은 IP 의 실패가 쌓였습니다Retry-After 만큼 기다린 뒤, 값을 고쳐서 재시도
500E5000토큰은 맞는데 api_key 발급이 실패서버측 — 자격증명이 아닙니다
503E5500토큰 저장소에 닿지 못해 «판정 자체가 없습니다»서버측 — 자격증명이 아닙니다
503 을 「인증 실패」로 처리하면 엉뚱한 곳을 여드레 동안 뒤집니다

서버는 토큰 저장소(PostgreSQL)에 닿지 못했을 때 401 을 내지 않습니다. 401 은 「당신의 자격증명이 틀렸다」는 주장이고, 그때 서버는 토큰이 좋은지 나쁜지 모르기 때문입니다. 호출자가 그 둘을 구별할 방법이 없어지는 것이 문제입니다.

실제로 그렇게 새어 나간 적이 있습니다 — 2026-09-07, 야간 e2e 가 여드레 동안 「22/22 실패, E1100 Token authentication failed」를 보고했는데, 플랫폼 로그의 진짜 이유는 Connection to plantpulse-datalake:5432 refused 였습니다. PostgreSQL 장애가 여드레 동안 자격증명 오류의 얼굴을 쓰고 있었습니다. 지금은 저장소 예외(SQLException · 소켓 계열)를 원인 사슬까지 따라가 확인하고 503 E5500 으로 답합니다.

같은 이유로 503 은 실패 횟수로 세지 않습니다. 세었다면 DB 장애가 멀쩡한 호출자를 429 로 잠가 버렸을 것입니다 — 장애 위에 잠금이 얹히는 것입니다.

Java 클라이언트는 이 구분을 예외 메시지로 그대로 돌려줍니다. 메시지를 보고 어디를 고칠지 정하십시오.

메시지뜻
V5 connect: /api/v5/auth call failed — …요청이 서버에 닿지도 못했습니다(주소·포트·방화벽)
V5 connect: auth endpoint unavailable (http=503) — server reached no verdict about the token서버가 판정을 못 냈습니다 — 토큰을 의심하지 마십시오
V5 connect: authentication failed — …401/400 계열 — 자격증명이나 요청을 고칩니다
V5 connect: api_key issuance failed — response=…meta.status 는 OK 인데 키가 비었습니다 — 서버측
인증 시도 잠금은 설정으로 바꿉니다

같은 IP 의 인증 실패가 engine.api.v5.auth.bruteforce.ip_limit(기본 30) 회 쌓이면 engine.api.v5.auth.bruteforce.window.minutes(기본 15 분) 동안 429 로 잠깁니다. engine.api.v5.auth.bruteforce.enabled=false 로 끌 수 있지만, 끄면 토큰 대입 공격에 무방비가 됩니다. 본문이 JSON 이 아닌 요청은 자격증명 시도가 아니므로 세지 않습니다.

다음 단계​