알람 관리 API
개요
알람 관리 API는 시스템에서 발생한 알람을 조회하고, 확인(ACK)하며, 실시간으로 새 알람을 구독하는 기능을 제공합니다. 현재 활성 알람과 이력 알람을 모두 지원합니다.
기본 정보
| 항목 | 값 |
|---|---|
| 서비스 | CaffeineApi |
| 엔드포인트 | Engine gRPC 포트 (기본: 5001) |
| 인증 | JWT 토큰 필요 |
| 버전 | v1 |
엔드포인트 요약
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/v1/alarms | 현재 알람 목록 조회 |
| GET | /api/v1/alarms/history | 알람 이력 조회 |
| POST | /api/v1/alarms/{alarm_id}/acknowledge | 알람 확인(ACK) |
| gRPC Stream | SubscribeAlarms | 알람 실시간 구독 |
상세 엔드포인트
GetAlarms
현재 활성 알람 목록을 조회합니다.
gRPC 메서드
rpc GetAlarms (GetAlarmsRequest) returns (GetAlarmsResponse)
요청
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| severity_filter | string | N | 심각도 필터 ("Critical", "Warning", "Info") |
| only_unacknowledged | bool | N | 미확인 알람만 조회 (기본: false) |
| pagination | PaginationRequest | N | 페이지 정보 |
PaginationRequest 구조
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
| page | int32 | 1 | 페이지 번호 (1-based) |
| page_size | int32 | 50 | 페이지 크기 (최대 500) |
응답
{
"alarms": [
{
"id": "alarm-12345",
"source": "PLC01.D100",
"message": "Temperature exceeded threshold (85°C)",
"severity": "Critical",
"triggeredAt": "2026-02-17T10:30:00Z",
"acknowledgedAt": null,
"acknowledgedBy": null,
"isActive": true
}
],
"totalCount": 15
}
응답 코드
| 코드 | 설명 |
|---|---|
| OK | 성공 |
| INVALID_ARGUMENT | 잘못된 파라미터 |
| UNAUTHENTICATED | 인증 필요 |
REST 경로
GET /api/v1/alarms?severityFilter=Critical&onlyUnacknowledged=true&page=1&pageSize=50
GetAlarmHistory
알람 이력을 조회합니다.
gRPC 메서드
rpc GetAlarmHistory (GetAlarmHistoryRequest) returns (GetAlarmHistoryResponse)
요청
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| time_range | TimeRange | Y | 조회 시간 범위 |
| severity_filter | string | N | 심각도 필터 |
| pagination | PaginationRequest | N | 페이지 정보 |
TimeRange 구조
| 필드 | 타입 | 설명 |
|---|---|---|
| start | Timestamp | 시작 시각 |
| end | Timestamp | 종료 시각 |
응답
{
"alarms": [
{
"id": "alarm-12340",
"source": "PLC01.D100",
"message": "Temperature exceeded threshold",
"severity": "Warning",
"triggeredAt": "2026-02-17T09:00:00Z",
"acknowledgedAt": "2026-02-17T09:05:00Z",
"acknowledgedBy": "admin@example.com",
"isActive": false
}
],
"totalCount": 523
}
REST 경로
GET /api/v1/alarms/history?start=2026-02-17T00:00:00Z&end=2026-02-17T23:59:59Z&severityFilter=Warning
AcknowledgeAlarm
알람을 확인(ACK)합니다.
gRPC 메서드
rpc AcknowledgeAlarm (AcknowledgeAlarmRequest) returns (AcknowledgeAlarmResponse)
요청
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| alarm_id | string | Y | 알람 ID |
| comment | string | N | 확인 코멘트 |
응답
{
"success": true
}
응답 코드
| 코드 | 설명 |
|---|---|
| OK | 성공 |
| NOT_FOUND | 알람을 찾을 수 없음 |
| ALREADY_EXISTS | 이미 확인된 알람 |
REST 경로
POST /api/v1/alarms/{alarm_id}/acknowledge
Content-Type: application/json
{
"comment": "Maintenance team notified"
}
SubscribeAlarms
알람을 실시간으로 스트리밍합니다. (gRPC Server Streaming)
gRPC 메서드
rpc SubscribeAlarms (SubscribeAlarmsRequest) returns (stream AlarmNotification)
요청
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| severity_filter | string | N | 심각도 필터 (예: "Critical") |
스트림 응답
{
"alarm": {
"id": "alarm-12346",
"source": "PLC02.D200",
"message": "Pressure drop detected",
"severity": "Warning",
"triggeredAt": "2026-02-17T10:35:00Z",
"acknowledgedAt": null,
"acknowledgedBy": null,
"isActive": true
}
}
특징
- 서버 → 클라이언트 스트리밍
- 새 알람 발생 시 즉시 전송
severity_filter로 특정 심각도만 구독 가능
데이터 타입
AlarmInfo 구조
| 필드 | 타입 | 설명 |
|---|---|---|
| id | string | 알람 고유 ID |
| source | string | 알람 소스 (태그, 설비 등) |
| message | string | 알람 메시지 |
| severity | string | 심각도 ("Critical", "Warning", "Info") |
| triggered_at | Timestamp | 발생 시각 |
| acknowledged_at | Timestamp? | 확인 시각 (nullable) |
| acknowledged_by | string? | 확인자 (nullable) |
| is_active | bool | 활성 상태 |
Severity 값
| 값 | 설명 | 사용 예시 |
|---|---|---|
| Critical | 치명적 | 설비 정지, 안전 임계값 초과 |
| Warning | 경고 | 비정상 동작, 임계값 접근 |
| Info | 정보 | 상태 변경, 일반 알림 |
사용 예제
C# (CaffeineClient)
using Caffeine.Client;
var client = new CaffeineClient();
await client.ConnectAsync("https://localhost:5001");
// 현재 알람 조회
var alarmsResponse = await client.GetAlarmsAsync(
severityFilter: "Critical",
onlyUnacknowledged: true,
page: 1,
pageSize: 50
);
foreach (var alarm in alarmsResponse.Alarms)
{
Console.WriteLine($"[{alarm.Severity}] {alarm.Message} at {alarm.TriggeredAt}");
}
Console.WriteLine($"Total: {alarmsResponse.TotalCount}");
// 알람 이력 조회
var historyResponse = await client.GetAlarmHistoryAsync(
startTime: DateTime.UtcNow.AddDays(-7),
endTime: DateTime.UtcNow,
severityFilter: "Warning"
);
// 알람 확인
bool ackSuccess = await client.AcknowledgeAlarmAsync("alarm-12345", "Resolved by operator");
Console.WriteLine($"Acknowledged: {ackSuccess}");
await client.DisposeAsync();
C# (직접 gRPC 호출)
using Grpc.Net.Client;
using Caffeine.IPC.Grpc;
using Google.Protobuf.WellKnownTypes;
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var grpcClient = new CaffeineApi.CaffeineApiClient(channel);
// 현재 알람 조회
var alarmsResponse = await grpcClient.GetAlarmsAsync(new GetAlarmsRequest
{
SeverityFilter = "Critical",
OnlyUnacknowledged = true,
Pagination = new PaginationRequest
{
Page = 1,
PageSize = 50
}
});
foreach (var alarm in alarmsResponse.Alarms)
{
Console.WriteLine($"{alarm.Id}: {alarm.Message}");
}
// 알람 이력 조회
var historyResponse = await grpcClient.GetAlarmHistoryAsync(new GetAlarmHistoryRequest
{
TimeRange = new TimeRange
{
Start = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-7)),
End = Timestamp.FromDateTime(DateTime.UtcNow)
},
SeverityFilter = "Warning"
});
// 알람 확인
var ackResponse = await grpcClient.AcknowledgeAlarmAsync(new AcknowledgeAlarmRequest
{
AlarmId = "alarm-12345",
Comment = "Maintenance completed"
});
Console.WriteLine($"Acknowledged: {ackResponse.Success}");
// 실시간 구독
using var subscription = grpcClient.SubscribeAlarms(new SubscribeAlarmsRequest
{
SeverityFilter = "Critical"
});
await foreach (var notification in subscription.ResponseStream.ReadAllAsync())
{
var alarm = notification.Alarm;
Console.WriteLine($"[NEW ALARM] {alarm.Severity}: {alarm.Message}");
}
curl (REST API)
현재 알람 조회
curl -X GET "https://localhost:5001/api/v1/alarms?severityFilter=Critical&onlyUnacknowledged=true&page=1&pageSize=50" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
응답
{
"alarms": [
{
"id": "alarm-12345",
"source": "PLC01.D100",
"message": "Temperature exceeded threshold (85°C)",
"severity": "Critical",
"triggeredAt": "2026-02-17T10:30:00.000Z",
"acknowledgedAt": null,
"acknowledgedBy": null,
"isActive": true
}
],
"totalCount": 15
}
알람 이력 조회
curl -X GET "https://localhost:5001/api/v1/alarms/history?start=2026-02-17T00:00:00Z&end=2026-02-17T23:59:59Z&severityFilter=Warning" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
알람 확인
curl -X POST "https://localhost:5001/api/v1/alarms/alarm-12345/acknowledge" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"comment": "Maintenance team notified"
}'
응답
{
"success": true
}
JavaScript (SignalR)
SignalR Hub를 통한 실시간 알람 수신도 가능합니다. 자세한 내용은 실시간 통신 API 문서를 참조하세요.
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("https://localhost:5001/hubs/alarms", {
accessTokenFactory: () => "YOUR_JWT_TOKEN"
})
.withAutomaticReconnect()
.build();
connection.on("ReceiveAlarm", (message, severity, timestamp) => {
console.log(`[${severity}] ${message} at ${timestamp}`);
});
await connection.start();
에러 코드
| 코드 | 설명 | HTTP 상태 |
|---|---|---|
| ALARM_NOT_FOUND | 알람을 찾을 수 없음 | 404 |
| ALREADY_ACKNOWLEDGED | 이미 확인된 알람 | 409 |
| INVALID_TIME_RANGE | 잘못된 시간 범위 | 400 |
| PAGE_SIZE_EXCEEDED | 페이지 크기 초과 (최대 500) | 400 |
성능 최적화
페이지네이션 사용
대량의 알람 조회 시 페이지네이션을 사용하여 성능을 최적화합니다.
// ❌ 비효율적
var allAlarms = await client.GetAlarmsAsync(page: 1, pageSize: 10000);
// ✅ 효율적
var page1 = await client.GetAlarmsAsync(page: 1, pageSize: 100);
var page2 = await client.GetAlarmsAsync(page: 2, pageSize: 100);
심각도 필터 활용
불필요한 알람을 제외하여 네트워크 트래픽을 줄입니다.
// Critical 알람만 구독
var subscription = grpcClient.SubscribeAlarms(new SubscribeAlarmsRequest
{
SeverityFilter = "Critical"
});
시간 범위 제한
알람 이력 조회 시 필요한 기간만 지정합니다.
// ❌ 비효율적: 전체 이력 조회
var history = await client.GetAlarmHistoryAsync(
startTime: DateTime.MinValue,
endTime: DateTime.UtcNow
);
// ✅ 효율적: 최근 7일만 조회
var history = await client.GetAlarmHistoryAsync(
startTime: DateTime.UtcNow.AddDays(-7),
endTime: DateTime.UtcNow
);
문제 해결
알람을 찾을 수 없음 (ALARM_NOT_FOUND)
원인:
- 알람 ID 오타
- 알람이 이미 삭제됨
- 권한이 없는 알람
해결:
GetAlarms로 현재 알람 목록 확인- 알람 ID 정확성 확인
- JWT 토큰 권한 확인
실시간 알람 수신 안 됨
원인:
- 구독 필터가 너무 제한적
- 네트워크 연결 끊김
- 알람이 실제로 발생하지 않음
해결:
severity_filter제거하여 모든 알람 구독- gRPC 연결 상태 확인
- 테스트 알람 발생시켜 확인
이미 확인된 알람 (ALREADY_ACKNOWLEDGED)
원인:
- 중복 ACK 요청
- 다른 사용자가 이미 확인함
해결:
GetAlarms로 최신 상태 확인acknowledgedAt필드 확인
관련 문서
REST API
Caffeine Engine의 REST 엔드포인트(
AlarmController)를 통해 HTTP로 알람을 관리할 수 있습니다.
기본 정보
| 항목 | 값 |
|---|---|
| 기본 경로 | /api/v1.0/alarms |
| 인증 | 필수 (JWT Bearer 토큰, v2.2.1+) |
모든 요청 헤더에 유효한 JWT 토큰을 포함해야 합니다.
Authorization: Bearer <your-jwt-token>
엔드포인트 목록
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/v1.0/alarms/active | 현재 활성 알람 목록 조회 |
| GET | /api/v1.0/alarms/history | 알람 이력 조회 |
| POST | /api/v1.0/alarms/{code}/acknowledge | 알람 확인 및 제거 |
GET /api/v1.0/alarms/active
현재 활성화되어 있는 알람 목록을 조회합니다.
응답
[
{
"code": 1001,
"message": "Motor Overload",
"severity": "Warning",
"timestamp": "2026-02-09T10:30:00Z",
"sourceId": "Equipment-01",
"acknowledged": false
}
]
GET /api/v1.0/alarms/history
알람 이력을 조회합니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| limit | int | N | 조회할 최대 개수 (기본값: 100) |
POST /api/v1.0/alarms/{code}/acknowledge
특정 알람을 확인(Acknowledge)하고 활성 알람 목록에서 제거합니다.
응답 코드: 200 성공, 401 인증 실패, 404 알람 없음
REST DTO
ActiveAlarm
| 속성 | 타입 | 설명 |
|---|---|---|
| code | int | 알람 코드 (고유 식별자) |
| message | string | 알람 메시지 |
| severity | string | 심각도 (Info, Warning, Critical) |
| timestamp | DateTime | 발생 시간 (UTC) |
| sourceId | string | 알람 소스 ID |
| acknowledged | bool | 확인 여부 |
| clearedAt | DateTime? | 해제 시간 (이력 조회 시에만 존재) |
아키텍처 변경사항 (v2.2.1)
AlarmController는 IAlarmSource 인터페이스만 의존하도록 리팩토링되었습니다 (DIP 준수). IAlarmSource에 GetHistoryAsync() 메서드가 추가되었습니다.