본문으로 건너뛰기

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 파일src/Caffeine.IPC/Protos/public_api.proto
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. 여러 번 호출하여 처리