비동기 검색 API
Google Security Operations의 검색 플랫폼을 사용하면 최대 100만 개의 결과가 포함된 대규모 결과 세트를 반환하는 장기 실행 쿼리에 비동기 API를 사용할 수 있습니다. 이러한 API를 사용하면 애플리케이션을 차단하지 않고 통합 데이터 모델 (UDM) 이벤트, 감지, 데이터 테이블, 엔티티 컨텍스트 그래프 (ECG)를 비롯한 데이터 소스 전반에서 검색을 시작할 수 있습니다. 장기 실행 작업 (LRO) API를 사용하여 검색어를 실행하면 작업 ID가 수신됩니다. 이 ID를 사용하여 작업 상태를 모니터링하고 결과를 페이지별로 가져올 수 있습니다.
기본 요건
장기 실행 작업 API를 사용하려면 호출 주체에 특정 Identity and Access Management (IAM) 권한이 필요합니다.
다음 작업을 수행하려면 해당 IAM 권한이 있어야 합니다.
- 검색 시작:
chronicle.searchSessions.search - 결과 나열:
SearchSession리소스에 대한chronicle.searchedResults.list
호출 주 구성원에게 이러한 권한을 부여하는 역할(예: Chronicle API 뷰어, Chronicle API 편집자 또는 Chronicle API 관리자 역할)이 있는지 확인합니다.
LRO API를 사용하여 검색 실행
LRO API를 사용하여 검색을 실행하려면 다음 단계를 따르세요.
검색 시작
Google SecOps 인스턴스의 search 커스텀 메서드에 POST 요청을 전송합니다.
- 엔드포인트:
POST /{$api_version}/projects/{project}/locations/{location}/instances/{instance}:search - 메소드:
Search - 요청 본문:
SearchRequest
다음 예시에서는 SearchRequest 객체를 보여줍니다.
{
"parent": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID",
"query": "metadata.event_type = \"USER_LOGIN\"",
"time_range": {},
"start_time": "2026-03-16T14:40:13Z",
"endTime": "2026-03-16T15:40:13Z",
"dialect": "YL2"
}
요청에는 다음 주요 매개변수가 필요합니다.
query: 쿼리 문자열입니다.time_range: 검색 시간 간격입니다.dialect: 언어 방언을YL2로 지정합니다.result_limit: 선택사항. 구체화할 최대 행 수입니다. 기본값은10000이고 최댓값은1000000입니다.
이 호출은 google.longrunning.Operation 객체를 반환합니다.
다음 예시는 성공적인 작업 응답을 보여줍니다.
{
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID",
"metadata": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata)",
"state": "RUNNING",
"start_time": "2026-03-13T10:00:00Z"
}
}
state: RUNNING 필드는 검색이 진행 중임을 나타냅니다.
작업 모니터링
google.longrunning.Operations 서비스의 표준 GetOperation 메서드를 사용하여 LRO 상태를 폴링합니다. 이전 응답의 name 값을 사용합니다.
- 엔드포인트:
GET /{$api_version}/projects/{project}/locations/{location}/instances/{instance}/operations/{operationID}
GetOperation 응답의 done 필드가 true를 반환할 때까지 계속 폴링합니다.
- 작업이 성공하면
metadata.state필드에서SUCCEEDED이 반환되고 응답 필드에 생성된SearchSession리소스가 포함됩니다. - 작업이 실패하면
done필드에서true을 반환하고 오류 필드에 관련 실패 세부정보가 포함됩니다.
다음 예는 성공적인 GetOperation 응답을 보여줍니다.
{
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID",
"metadata": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation) Metadata",
"state": "SUCCEEDED",
"startTime": "2026-03-16T15:42:11.037506921Z"
},
"endTime": "2026-03-16T15:42:17.504730842Z",
"expireTime": "2026-03-17T15:42:17.504731874Z",
"progress": 100,
"done": true,
"response": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)",
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID",
"query": "metadata.event_type = \"USER_LOGIN\"",
"timeRange": {},
"startTime": "2026-03-16T14:40:13Z",
"endTime": "2026-03-16T15:40:13Z",
"dialect": "YL2",
"metadata": {
"operationId": "OPERATION_ID",
"startTime": "2026-03-16T15:42:11.037506921Z",
"endTime": "2026-03-16T15:42:17.504730842Z",
"expireTime": "2026-03-17T15:42:17.504731874Z",
"resultRowCount": 10000,
"moreDataAvailable": true
}
}
}
SearchSession 리소스 이름 형식은 projects/{project}/locations/{location}/instances/{instance}/searchSessions/{search_session}입니다.
성공적인 응답에는 다음 주요 필드가 포함됩니다.
done:true로 설정되면 작업이 완료됩니다.state:SUCCEEDED로 설정된 경우 검색이 성공적으로 완료되었습니다.response.name:SearchSession의 리소스 이름 다음 단계에서 이 값을 상위 속성으로 사용합니다.response.metadata.resultRowCount: 발견된 총 행 수를 나타냅니다.response.metadata.moreDataAvailable: 사용 가능한 결과 수가 정의된 반환 한도를 초과함을 나타냅니다.
LRO 작업 나열
LRO 작업을 나열하려면 google.longrunning.Operations 서비스의 ListOperations 메서드를 사용합니다. 이전 응답의 name 값을 사용합니다.
- 엔드포인트:
GET /{$api_version}/projects/{project}/locations/{location}/instances/{instance}
지난 24시간 동안의 모든 LRO 작업을 나열하려면 name: "operations/s-lro" 필터를 추가합니다.
다음 예는 성공적인 ListOperations 요청을 보여줍니다.
google.longrunning.ListOperationsRequest {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID"
filter: "name:\"operations/lro\""
page_size: 100
}
다음 예는 성공적인 ListOperations 응답을 보여줍니다.
{
operations {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID_1"
metadata {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation) Metadata"
value: "\b\002\022\f\b\367\304\363\316\006\020\317\352\232\240\003\032\f\b\237\307\363\316\006\020\326\334\351\311\001\"\f\b\237\352\370\316\006\020\352\342\351\311\001(d"
}
done: true
response {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)"
value: "\n\213\001projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/OPERATION_ID_11022\bip != \"\"\032\020\n\006\b\251\255\334\316\006\022\006\b\351\345\336\316\006\0012\\\n*OPERATION_ID_1\022\f\b\367\304\363\316\006\020\317\352\232\240\003\032\f\b\237\307\363\316\006\020\326\334\351\311\001\"\f\b\237\352\370\316\006\020\352\342\351\311\001(\300\204=0\001"
}
}
operations {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID_2"
metadata {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata)"
value: "\b\002\022\f\b\200\305\363\316\006\020\324\262\367\225\001\032\v\b\241\307\363\316\006\020\321\261\333?\"\v\b\241\352\370\316\006\020\317\264\333?(d"
}
done: true
response {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)"
value: "\n\213\001projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/OPERATION_ID_2\022\bip != \"\"\0321020\n\006\b\251\255\334\316\006\022\006\b\351\345\336\316\006\0012Z\n*OPERATION_ID_2\022\f\b\200\305\363\316\006\0201324\262\367\225\001\032\v\b\241\307\363\316\006\020\321\261\333?\"\v\b\241\352\370\316\006\020\317\264\333?(\300\204=0\001"
}
}
}
결과 가져오기
작업 상태가 SUCCEEDED를 반환한 후 ListSearchedResults() 메서드를 사용하여 검색 결과를 가져옵니다.
- 엔드포인트:
GET /{$api_version}/{parent=projects/*/locations/*/instances/*/searchSessions/*}/searchedResults - 메소드:
ListSearchedResults - 요청 매개변수:
ListSearchedResultsRequest
다음 예에서는 결과를 3개 가져오고 처음 5개를 건너뛰는 ListSearchedResultsRequest를 보여줍니다.
// GET
/v1alpha/projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID/searchedResults?page_size=3&skip=5
요청은 다음 쿼리 매개변수를 지원합니다.
page_size: 페이지당 반환할 최대 결과 수입니다. 기본값은 100이고 최댓값은 10, 000입니다.page_token: 다음 페이지를 검색하는 데 사용된 이전ListSearchedResultsResponse의 토큰입니다.order_by: 선택사항. 이 필드는 결과를 정렬하는 데 사용됩니다.UDM events (eventRecord):udm필드 내의 경로를 사용합니다(예:udm.metadata.timestamp desc또는udm.principal.hostname asc).hostname,user,process name,event type를 포함한 열 이름도 지원됩니다.기본값은
udm.metadata.event_timestamp입니다.Entities / ECG (entityContextRecord):entity필드 내의 경로를 사용합니다(예:graph.entity.ip asc).data tables (dataTableRecord):%<table_alias>.<column_name>형식을 사용합니다. 예를 들면%dt.user desc입니다.기본값은 첫 번째 데이터 표 열입니다.
Detections (DetectionRecord):detection키워드 뒤에 경로를 사용합니다(예:detection.id).조인: 이벤트 및 항목의 경우 이를 정의하는 자리표시자 변수를 사용합니다. 다른 모든 소스의 경우 형식은 동일하게 유지됩니다.
예를 들면 다음과 같습니다.
- 심전도 (UDM-ECG 조인): 항목:
$e1.graph.entity.hostname - UDM (UDM과의 모든 조인):
$e1.principal.ip - 데이터 테이블 (UDM-데이터 테이블 조인):
%<table_alias>.<column_name>
hostname,user,process name,event type와 같은 사전 정의된 별칭도 지원됩니다. 이러한 경우$e1.hostname.at형식을 사용합니다.- 심전도 (UDM-ECG 조인): 항목:
skip: 선택사항. 건너뛸 결과 수입니다.page_token을 사용하는 경우 사용하지 마세요.
다음 예는 성공적인 ListSearchedResultsResponse 응답을 보여줍니다.
{
"searchedResults": [
{
"name":
"projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID/searchedResults/
RESULT_ID",
"resultRow": {
"eventRecord": {
"event": {
"name":
"projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/events/EVENT_ID",
"udm": {
"metadata": {
"eventTimestamp": "2026-03-16T14:45:18Z",
"eventType": "USER_LOGIN",
"vendorName": "Microsoft",
"productName": "Azure AD"
}
},
//... other UDM fields
}
//... other UDM fields
"eventLogToken":
"EVENT_LOG_TOKEN"
}
},
{ }
},
{
"name": "projects/PROJECT_NUMBER/locations/
LOCATION/instances/INSTANCE_ID
/searchSessions/SEARCH_SESSION_ID/searchedResults/RESULT_ID", "resultRow": {
"eventRecord": {
//... Similar UDM event structure...
}
}
},
{
""name": "projects/PROJECT_NUMBER/locations/
LOCATION/instances/INSTANCE_ID
/searchSessions/SEARCH_SESSION_ID/searchedResults/RESULT_ID", "resultRow": {
"eventRecord": {
//... Similar UDM event structure...
}
}
}
],
"totalSize": 10000,
"columnNames": [],
"columnSchema": {},
"nextPageToken": "CAKYASAB"
}
결과의 다음 페이지를 가져오려면 다음 ListSearchedResults 요청의 page_token 쿼리 매개변수에서 반환된 nextPageToken 값을 사용합니다.
resultRow 필드에는 실제 데이터가 포함됩니다.
각 응답의 next_page_token 값을 사용하여 ListSearchedResults를 계속 호출합니다. next_page_token이 비어 있으면 모든 결과가 검색된 것입니다.
다음 단계
메서드, 요청 및 응답 필드, 유형에 대한 자세한 내용은 다음 API 참고 문서를 참고하세요.
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가에게 문의하여 답변을 받으세요.