본문으로 건너뛰기

알람 관리 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 StreamSubscribeAlarms알람 실시간 구독

상세 엔드포인트

GetAlarms

현재 활성 알람 목록을 조회합니다.

gRPC 메서드

rpc GetAlarms (GetAlarmsRequest) returns (GetAlarmsResponse)

요청

파라미터타입필수설명
severity_filterstringN심각도 필터 ("Critical", "Warning", "Info")
only_unacknowledgedboolN미확인 알람만 조회 (기본: false)
paginationPaginationRequestN페이지 정보

PaginationRequest 구조

필드타입기본값설명
pageint321페이지 번호 (1-based)
page_sizeint3250페이지 크기 (최대 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_rangeTimeRangeY조회 시간 범위
severity_filterstringN심각도 필터
paginationPaginationRequestN페이지 정보

TimeRange 구조

필드타입설명
startTimestamp시작 시각
endTimestamp종료 시각

응답

{
"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_idstringY알람 ID
commentstringN확인 코멘트

응답

{
"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_filterstringN심각도 필터 (예: "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 구조

필드타입설명
idstring알람 고유 ID
sourcestring알람 소스 (태그, 설비 등)
messagestring알람 메시지
severitystring심각도 ("Critical", "Warning", "Info")
triggered_atTimestamp발생 시각
acknowledged_atTimestamp?확인 시각 (nullable)
acknowledged_bystring?확인자 (nullable)
is_activebool활성 상태

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 오타
  • 알람이 이미 삭제됨
  • 권한이 없는 알람

해결:

  1. GetAlarms로 현재 알람 목록 확인
  2. 알람 ID 정확성 확인
  3. JWT 토큰 권한 확인

실시간 알람 수신 안 됨

원인:

  • 구독 필터가 너무 제한적
  • 네트워크 연결 끊김
  • 알람이 실제로 발생하지 않음

해결:

  1. severity_filter 제거하여 모든 알람 구독
  2. gRPC 연결 상태 확인
  3. 테스트 알람 발생시켜 확인

이미 확인된 알람 (ALREADY_ACKNOWLEDGED)

원인:

  • 중복 ACK 요청
  • 다른 사용자가 이미 확인함

해결:

  1. GetAlarms로 최신 상태 확인
  2. acknowledgedAt 필드 확인

관련 문서


REST API

Caffeine Engine의 REST 엔드포인트(AlarmController)를 통해 HTTP로 알람을 관리할 수 있습니다.

기본 정보

항목
기본 경로/api/v1.0/alarms
인증필수 (JWT Bearer 토큰, v2.2.1+)
인증 요구사항 (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

알람 이력을 조회합니다.

파라미터타입필수설명
limitintN조회할 최대 개수 (기본값: 100)

POST /api/v1.0/alarms/{code}/acknowledge

특정 알람을 확인(Acknowledge)하고 활성 알람 목록에서 제거합니다.

응답 코드: 200 성공, 401 인증 실패, 404 알람 없음

REST DTO

ActiveAlarm

속성타입설명
codeint알람 코드 (고유 식별자)
messagestring알람 메시지
severitystring심각도 (Info, Warning, Critical)
timestampDateTime발생 시간 (UTC)
sourceIdstring알람 소스 ID
acknowledgedbool확인 여부
clearedAtDateTime?해제 시간 (이력 조회 시에만 존재)

아키텍처 변경사항 (v2.2.1)

AlarmController는 IAlarmSource 인터페이스만 의존하도록 리팩토링되었습니다 (DIP 준수). IAlarmSourceGetHistoryAsync() 메서드가 추가되었습니다.