본문으로 건너뛰기

Public API 통합 레퍼런스

개요​

CaffeineApi gRPC 서비스는 외부 클라이언트가 Caffeine Engine과 통신하기 위한 표준 인터페이스입니다. REST API (JSON Transcoding)와 gRPC 모두 지원하며, 15개의 주요 엔드포인트를 제공합니다.

아키텍처​

외부 클라이언트 (C#, JavaScript, Python, curl)
↓ gRPC / REST (JSON Transcoding)
CaffeineApi (Engine)
↓
태그 관리, 알람 관리, 드라이버 관리, 히스토리 조회

기본 정보​

항목값
서비스명CaffeineApi
프로토콜gRPC + REST (JSON Transcoding)
엔드포인트Engine gRPC 포트 (기본: 5001)
인증JWT 토큰 (Bearer)
Proto 정의Caffeine.IPC.Grpc 네임스페이스
C# 네임스페이스Caffeine.IPC.Grpc

전체 엔드포인트 목록​

Health & System (1)​

메서드경로설명인증
GetHealth/api/v1/health시스템 상태 확인불필요

Tag Management (5)​

메서드경로설명인증
ReadTag/api/v1/tags/{tag_name}단일 태그 조회필요
ReadTags/api/v1/tags:batchRead복수 태그 조회 (최대 500개)필요
WriteTag/api/v1/tags/{driver_id}/{address}/write단일 태그 쓰기필요
WriteTags/api/v1/tags:batchWrite복수 태그 쓰기 (최대 500개)필요
GetTagList/api/v1/tags태그 목록 조회 (페이지네이션)필요
SubscribeTagChangesgRPC Stream태그 변경 실시간 구독필요

Alarm Management (4)​

메서드경로설명인증
GetAlarms/api/v1/alarms현재 알람 목록 조회필요
GetAlarmHistory/api/v1/alarms/history알람 이력 조회필요
AcknowledgeAlarm/api/v1/alarms/{alarm_id}/acknowledge알람 확인(ACK)필요
SubscribeAlarmsgRPC Stream알람 실시간 구독필요

Driver Management (3)​

메서드경로설명권한
GetDrivers/api/v1/drivers드라이버 목록 조회User
GetDriverStatus/api/v1/drivers/{driver_name}/status드라이버 상태 조회User
RestartDriver/api/v1/drivers/{driver_name}/restart드라이버 재시작Admin

History & Analytics (2)​

메서드경로설명인증
GetTagHistory/api/v1/tags/{tag_name}/history태그 히스토리 조회필요
GetAggregatedData/api/v1/tags/{tag_name}/aggregated집계 데이터 조회필요

빠른 시작​

CaffeineClient SDK 사용 (권장)​

using Caffeine.Client;

var client = new CaffeineClient();
await client.ConnectAsync("https://localhost:5001");

// Health 체크
var health = await client.GetHealthAsync();

// 태그 읽기
var tag = await client.ReadTagAsync("PLC01.D100");

// 알람 조회
var alarms = await client.GetAlarmsAsync(severityFilter: "Critical");

await client.DisposeAsync();

직접 gRPC 호출​

using Grpc.Net.Client;
using Caffeine.IPC.Grpc;

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CaffeineApi.CaffeineApiClient(channel);

var health = await client.GetHealthAsync(new Empty());
Console.WriteLine($"Status: {health.Status}, Version: {health.Version}");

REST API (curl)​

curl -X GET "https://localhost:5001/api/v1/health"

curl -X GET "https://localhost:5001/api/v1/tags/PLC01.D100" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

인증 및 권한​

JWT 토큰 발급​

Caffeine Identity 서비스를 통해 JWT 토큰을 발급받습니다.

curl -X POST "https://localhost:5001/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"username": "admin@example.com",
"password": "your_password"
}'

응답

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": "2026-02-18T10:00:00Z"
}

gRPC 메타데이터 헤더​

var headers = new Metadata
{
{ "Authorization", $"Bearer {jwtToken}" }
};

var response = await client.ReadTagAsync(new ReadTagRequest { TagName = "PLC01.D100" }, headers);

REST API Authorization 헤더​

curl -X GET "https://localhost:5001/api/v1/tags/PLC01.D100" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

권한 레벨​

역할권한
User읽기 권한 (GetHealth, ReadTag, GetAlarms 등)
Admin전체 권한 (쓰기, 삭제, 드라이버 재시작 포함)

공통 메시지 타입​

Empty​

파라미터가 없는 요청에 사용됩니다.

message Empty {}

PaginationRequest​

페이지네이션 파라미터입니다.

message PaginationRequest {
int32 page = 1; // 1-based (기본: 1)
int32 page_size = 2; // 기본: 50, 최대: 500
}

TimeRange​

시간 범위를 지정합니다.

message TimeRange {
google.protobuf.Timestamp start = 1;
google.protobuf.Timestamp end = 2;
}

TagValue (다형성)​

태그 값은 다음 타입 중 하나를 가질 수 있습니다.

message TagValue {
oneof value {
string string_value = 1;
double double_value = 2;
int64 int64_value = 3;
bool bool_value = 4;
bytes bytes_value = 5;
}
}

사용 예시

{
"doubleValue": 123.45
}

{
"stringValue": "OK"
}

{
"boolValue": true
}

ErrorDetail​

에러 상세 정보입니다.

message ErrorDetail {
string code = 1; // "TAG_NOT_FOUND", "INVALID_DRIVER"
string message = 2;
map<string, string> metadata = 3;
}

예시

{
"code": "TAG_NOT_FOUND",
"message": "Tag 'PLC01.D999' not found",
"metadata": {
"tagName": "PLC01.D999",
"driverId": "PLC01"
}
}

에러 처리​

gRPC 상태 코드​

코드설명HTTP 상태
OK성공200
CANCELLED요청 취소됨499
INVALID_ARGUMENT잘못된 파라미터400
DEADLINE_EXCEEDED타임아웃504
NOT_FOUND리소스를 찾을 수 없음404
ALREADY_EXISTS리소스가 이미 존재함409
PERMISSION_DENIED권한 없음403
UNAUTHENTICATED인증 필요401
UNAVAILABLE서비스 연결 불가503

에러 응답 예시​

gRPC

try
{
var response = await client.ReadTagAsync(new ReadTagRequest { TagName = "InvalidTag" });
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {ex.StatusCode} - {ex.Status.Detail}");
}

REST API

{
"error": {
"code": "TAG_NOT_FOUND",
"message": "Tag 'InvalidTag' not found",
"metadata": {
"tagName": "InvalidTag"
}
}
}

스트리밍 RPC​

Server Streaming​

서버 → 클라이언트 단방향 스트리밍입니다.

  • SubscribeTagChanges: 태그 변경 알림
  • SubscribeAlarms: 알람 발생 알림

사용 예시

using var subscription = client.SubscribeTagChanges(new SubscribeTagChangesRequest
{
TagNames = { "PLC01.D100", "PLC01.D200" }
});

await foreach (var notification in subscription.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"{notification.TagName} = {notification.NewValue}");
}

배치 연산​

배치 읽기​

최대 500개의 태그를 한 번에 조회합니다.

var response = await client.ReadTagsAsync(new ReadTagsRequest
{
TagNames = { "PLC01.D100", "PLC01.D200", "PLC01.D300" }
});

// 성공한 태그
foreach (var tag in response.Tags)
{
Console.WriteLine($"{tag.Name} = {tag.CurrentValue}");
}

// 실패한 태그
foreach (var error in response.Errors)
{
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}

배치 쓰기​

최대 500개의 태그에 값을 씁니다.

var response = await client.WriteTagsAsync(new WriteTagsRequest
{
Writes =
{
new TagWriteItem { TagName = "PLC01.D100", Value = new TagValue { DoubleValue = 100.5 } },
new TagWriteItem { TagName = "PLC01.D200", Value = new TagValue { Int64Value = 500 } }
}
});

Console.WriteLine($"Success: {response.SuccessCount}, Errors: {response.Errors.Count}");

성능 최적화​

1. 배치 연산 사용​

단일 호출 대신 배치 연산을 사용하여 네트워크 라운드트립을 줄입니다.

// ❌ 비효율적: 10번 호출
for (int i = 0; i < 10; i++)
{
await client.ReadTagAsync(new ReadTagRequest { TagName = $"PLC01.D{i}" });
}

// ✅ 효율적: 1번 호출
var response = await client.ReadTagsAsync(new ReadTagsRequest
{
TagNames = Enumerable.Range(0, 10).Select(i => $"PLC01.D{i}").ToList()
});

2. 스트리밍 재사용​

스트리밍 연결을 재사용하여 오버헤드를 줄입니다.

// ✅ 하나의 스트리밍 연결로 여러 태그 구독
using var subscription = client.SubscribeTagChanges(new SubscribeTagChangesRequest
{
TagNames = { "PLC01.D100", "PLC01.D200", "PLC01.D300" }
});

3. 집계 데이터 활용​

장기간 히스토리는 집계 데이터를 사용합니다.

// ❌ 비효율적: 1년치 raw 데이터
var history = await client.GetTagHistoryAsync(new GetTagHistoryRequest
{
TagName = "PLC01.D100",
TimeRange = new TimeRange
{
Start = Timestamp.FromDateTime(DateTime.UtcNow.AddYears(-1)),
End = Timestamp.FromDateTime(DateTime.UtcNow)
},
MaxPoints = 10000
});

// ✅ 효율적: 일별 집계
var aggregated = await client.GetAggregatedDataAsync(new GetAggregatedDataRequest
{
TagName = "PLC01.D100",
TimeRange = new TimeRange
{
Start = Timestamp.FromDateTime(DateTime.UtcNow.AddYears(-1)),
End = Timestamp.FromDateTime(DateTime.UtcNow)
},
Aggregation = AggregationType.Avg,
Interval = Duration.FromTimeSpan(TimeSpan.FromDays(1))
});

상세 문서​

각 카테고리별 상세 문서는 다음을 참조하세요:

클라이언트 SDK​

Caffeine.Client (C#)​

공식 .NET 클라이언트 라이브러리입니다.

dotnet add package NEXCODE.Caffeine.Client

기능:

  • gRPC, SignalR, GraphQL 통합
  • 자동 재연결
  • 이벤트 기반 알림
  • 스레드 안전

JavaScript/TypeScript​

npm install @grpc/grpc-js @grpc/proto-loader
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const packageDefinition = protoLoader.loadSync('public_api.proto');
const proto = grpc.loadPackageDefinition(packageDefinition).caffeine.api;

const client = new proto.CaffeineApi('localhost:5001', grpc.credentials.createInsecure());

client.GetHealth({}, (error, response) => {
console.log('Status:', response.status);
});

Python​

pip install grpcio grpcio-tools
import grpc
from caffeine_pb2 import Empty
from caffeine_pb2_grpc import CaffeineApiStub

channel = grpc.insecure_channel('localhost:5001')
client = CaffeineApiStub(channel)

response = client.GetHealth(Empty())
print(f"Status: {response.status}, Version: {response.version}")

문제 해결​

gRPC 연결 실패​

증상: Grpc.Core.RpcException: Status(StatusCode="Unavailable")

해결:

  1. Engine 서비스 실행 확인
  2. 방화벽 포트 개방 확인
  3. TLS 인증서 유효성 확인

인증 실패 (UNAUTHENTICATED)​

증상: Status(StatusCode="Unauthenticated")

해결:

  1. JWT 토큰 유효성 확인
  2. Authorization 헤더 형식 확인 (Bearer {token})
  3. 토큰 만료 시간 확인

배치 크기 초과 (BATCH_SIZE_EXCEEDED)​

증상: Batch size exceeds maximum of 500

해결:

  1. 배치를 500개 이하로 분할
  2. 여러 번 호출하여 처리