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 | 태그 목록 조회 (페이지네이션) | 필요 |
| SubscribeTagChanges | gRPC Stream | 태그 변경 실시간 구독 | 필요 |
Alarm Management (4)
| 메서드 | 경로 | 설명 | 인증 |
|---|---|---|---|
| GetAlarms | /api/v1/alarms | 현재 알람 목록 조회 | 필요 |
| GetAlarmHistory | /api/v1/alarms/history | 알람 이력 조회 | 필요 |
| AcknowledgeAlarm | /api/v1/alarms/{alarm_id}/acknowledge | 알람 확인(ACK) | 필요 |
| SubscribeAlarms | gRPC 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")
해결:
- Engine 서비스 실행 확인
- 방화벽 포트 개방 확인
- TLS 인증서 유효성 확인
인증 실패 (UNAUTHENTICATED)
증상: Status(StatusCode="Unauthenticated")
해결:
- JWT 토큰 유효성 확인
- Authorization 헤더 형식 확인 (
Bearer {token}) - 토큰 만료 시간 확인
배치 크기 초과 (BATCH_SIZE_EXCEEDED)
증상: Batch size exceeds maximum of 500
해결:
- 배치를 500개 이하로 분할
- 여러 번 호출하여 처리