Elasticsearch REST API를 사용한 문서 색인 및 업데이트
REST API를 사용한 Elasticsearch의 핵심 CRUD(Create, Read, Update, Delete) 작업을 마스터하세요. 이 가이드에서는 새 문서 색인(지정된 ID 포함 또는 미포함)과 기존 레코드의 세분화된 부분 업데이트에 필요한 정확한 HTTP 요청, 엔드포인트 및 JSON 페이로드를 자세히 설명합니다. 원자적 업데이트, 스크립트 기반 수정 및 효율적인 대량 데이터 수집을 위한 실용적인 `curl` 예제를 알아보세요.
Elasticsearch REST API를 사용한 문서 색인 및 업데이트
Elasticsearch REST API를 사용한 문서 색인 및 업데이트는 처음에는 쉬워 보입니다. 엔드포인트에 JSON을 보내고 JSON을 다시 받으면 됩니다. 프로덕션에서 중요한 부분은 더 구체적입니다. 새 문서를 생성하는 것인가요, 아니면 기존 문서를 교체하는 것인가요? Elasticsearch가 ID를 생성하도록 하시겠습니까, 아니면 소스 시스템의 ID가 필요하신가요? 부분 업데이트를 수행하는 것인가요, 아니면 유지하려는 필드를 실수로 덮어쓰는 것인가요?
아래 예제는 localhost:9200, products라는 인덱스, 그리고 일반 curl을 사용합니다. 실제 클러스터에서는 HTTPS, 인증 및 인증서 옵션이 필요할 수도 있습니다.
curl -u elastic:password --cacert http_ca.crt https://es.example.com:9200/
자동 ID로 문서 생성
Elasticsearch가 문서 ID를 할당할 수 있을 때 POST /{index}/_doc를 사용하세요.
curl -X POST "localhost:9200/products/_doc" \
-H "Content-Type: application/json" \
-d '{
"sku": "mouse-x1",
"name": "Wireless Mouse X1",
"price": 25.99,
"in_stock": true,
"updated_at": "2026-05-24T09:30:00Z"
}'
성공적인 응답에는 _id와 result가 created로 포함됩니다.
{
"_index": "products",
"_id": "wI4aQZkB8xExample",
"_version": 1,
"result": "created"
}
자동 ID는 이벤트 데이터, 로그, 클릭 기록 및 추가 전용 콘텐츠에 적합합니다. 동일한 실제 항목이 나중에 다시 도착할 수 있는 경우에는 이상적이지 않습니다. 동일한 제품 업데이트가 자동 ID로 두 번 색인되면 두 개의 문서가 생성됩니다.
자체 ID로 색인
소스 시스템에 이미 안정적인 식별자가 있는 경우 PUT /{index}/_doc/{id}를 사용하세요.
curl -X PUT "localhost:9200/products/_doc/sku-mouse-x1" \
-H "Content-Type: application/json" \
-d '{
"sku": "mouse-x1",
"name": "Wireless Mouse X1",
"price": 25.99,
"in_stock": true,
"updated_at": "2026-05-24T09:30:00Z"
}'
ID가 존재하지 않으면 Elasticsearch가 생성합니다. 이미 존재하면 Elasticsearch가 전체 문서를 교체합니다. 이 교체 동작은 간단한 동기화 작업에 유용하지만, 보내는 것을 잊은 필드를 삭제할 수도 있습니다.
예를 들어, 기존 문서에 category, brand, description이 있고 다음 PUT에서 price만 보내면 저장된 _source는 해당 새 요청의 필드만 됩니다. 완전한 원하는 문서를 보낼 때 PUT을 사용하세요.
누락된 경우에만 생성
중복 생성이 버그가 될 경우 create 작업을 사용하세요:
curl -X PUT "localhost:9200/products/_create/sku-mouse-x1" \
-H "Content-Type: application/json" \
-d '{
"sku": "mouse-x1",
"name": "Wireless Mouse X1",
"price": 25.99
}'
ID가 이미 존재하면 Elasticsearch는 문서를 덮어쓰는 대신 버전 충돌을 반환합니다. 이는 메시지 재시도가 기존 레코드를 변경하지 않아야 하는 수집 작업에 유용합니다.
필드 부분 업데이트
일부 필드만 변경하려면 doc 객체와 함께 POST /{index}/_update/{id}를 사용하세요.
curl -X POST "localhost:9200/products/_update/sku-mouse-x1" \
-H "Content-Type: application/json" \
-d '{
"doc": {
"price": 22.49,
"in_stock": false,
"updated_at": "2026-05-24T10:15:00Z"
}
}'
Elasticsearch는 내부적으로 기존 문서를 가져오고, 제공된 필드를 병합한 다음, 변경된 소스를 다시 색인합니다. API 사용자 관점에서는 부분 업데이트이지, 디스크에서의 제자리 변경은 아닙니다.
문서가 아직 존재하지 않을 수 있는 경우 doc_as_upsert를 사용하세요:
curl -X POST "localhost:9200/products/_update/sku-keyboard-k90" \
-H "Content-Type: application/json" \
-d '{
"doc": {
"sku": "keyboard-k90",
"name": "Mechanical Keyboard K90",
"price": 129.99,
"in_stock": true
},
"doc_as_upsert": true
}'
이렇게 하면 문서가 없을 경우 doc 내용으로 문서를 생성합니다. 업스트림 피드가 "현재 상태" 레코드를 보내고 Elasticsearch가 ID를 처음 보는지 여부에 관계없이 사용하세요.
스크립트 기반 업데이트
새 값이 이전 값에 따라 달라질 때 스크립트가 유용합니다. 일반적인 예는 카운터 증가입니다:
curl -X POST "localhost:9200/products/_update/sku-mouse-x1" \
-H "Content-Type: application/json" \
-d '{
"script": {
"source": "ctx._source.views = (ctx._source.views ?: 0) + params.count",
"params": {
"count": 1
}
}
}'
스크립트를 작고 예측 가능하게 유지하세요. 강력하지만 일반 doc 업데이트보다 오용하기 쉽습니다. 대량의 카운터의 경우 쓰기 속도, 새로 고침 필요성 및 Elasticsearch가 기록 시스템이어야 하는지 신중히 고려하세요.
대량 색인 및 업데이트
소수의 문서보다 많은 경우 _bulk를 사용하세요. 요청 본문은 개행으로 구분된 JSON이며, 마지막 개행이 중요합니다.
cat bulk-products.ndjson
{"index":{"_index":"products","_id":"sku-mouse-x1"}}
{"sku":"mouse-x1","name":"Wireless Mouse X1","price":25.99,"in_stock":true}
{"update":{"_index":"products","_id":"sku-keyboard-k90"}}
{"doc":{"price":119.99,"in_stock":true},"doc_as_upsert":true}
{"delete":{"_index":"products","_id":"sku-old-cable"}}
다음과 같이 보내세요:
curl -X POST "localhost:9200/_bulk" \
-H "Content-Type: application/x-ndjson" \
--data-binary @bulk-products.ndjson
일반 -d가 아닌 --data-binary를 사용하여 curl이 개행을 유지하도록 하세요. 그런 다음 응답을 검사하세요. 대량 요청은 개별 항목이 실패했더라도 HTTP 200을 반환할 수 있습니다.
curl -s -X POST "localhost:9200/_bulk" \
-H "Content-Type: application/x-ndjson" \
--data-binary @bulk-products.ndjson | jq '.errors, .items[] | select(.index.error or .update.error or .delete.error)'
새로 고침 및 가시성
색인된 문서가 항상 즉시 검색 가능한 것은 아닙니다. Elasticsearch는 기본적으로 주기적으로 인덱스를 새로 고칩니다. ID로 문서를 읽어야 하는 경우 GET /products/_doc/sku-mouse-x1이 즉시 찾을 수 있습니다. 테스트를 계속하기 전에 검색에 나타나야 하는 경우 refresh=wait_for를 사용하세요:
curl -X PUT "localhost:9200/products/_doc/sku-mouse-x1?refresh=wait_for" \
-H "Content-Type: application/json" \
-d '{"sku":"mouse-x1","name":"Wireless Mouse X1","price":25.99}'
비용을 측정하지 않고 모든 프로덕션 쓰기에 강제 새로 고침을 추가하지 마세요. 색인 처리량을 줄일 수 있습니다.
실용적인 경험 법칙
중복 이벤트가 허용되거나 다른 곳에서 처리되는 추가 전용 데이터에는 POST /_doc를 사용하세요. 요청에 완전한 현재 문서가 포함된 경우 PUT /_doc/{id}를 사용하세요. 특정 필드만 변경하려면 _update를 사용하세요. 덮어쓰기가 데이터 문제를 숨길 경우 _create를 사용하세요. 작업이 소수의 문서보다 많을 경우 _bulk를 사용하세요.
대부분의 Elasticsearch 색인 버그는 잘못된 쓰기 형태를 선택하는 데서 발생합니다. 엔드포인트는 소스 데이터, 재시도 동작 및 Elasticsearch가 완전한 레코드를 저장하는지 아니면 다른 시스템 레코드의 검색 가능한 복사본을 저장하는지와 일치해야 합니다.
쓰기 요청을 탓하기 전에 매핑 확인
때로는 쓰기가 성공하고 검색 결과가 여전히 잘못 보일 수 있습니다. 이는 종종 색인 엔드포인트 문제가 아닌 매핑 문제입니다.
매핑 확인:
curl -s "localhost:9200/products/_mapping?pretty"
초기 테스트 문서가 "25.99"를 문자열로 보냈기 때문에 price가 처음에 텍스트로 색인된 경우 범위 쿼리가 제대로 작동하지 않을 수 있습니다. sku에 정확한 일치 및 집계가 필요한 경우 일반적으로 keyword 필드이거나 키워드 하위 필드가 있어야 합니다. 타임스탬프가 다른 형식으로 도착하면 일부 문서는 색인 중에 거부되고 다른 문서는 성공할 수 있습니다.
예측 가능한 시스템의 경우 첫 번째 쓰기 전에 인덱스 매핑을 생성하세요:
curl -X PUT "localhost:9200/products" \
-H "Content-Type: application/json" \
-d '{
"mappings": {
"properties": {
"sku": { "type": "keyword" },
"name": { "type": "text" },
"price": { "type": "double" },
"in_stock": { "type": "boolean" },
"updated_at": { "type": "date" }
}
}
}'
동적 매핑은 탐색에 편리합니다. 프로덕션 수집의 경우 명시적 매핑은 잘못된 첫 번째 문서가 인덱스를 형성하는 것을 방지합니다.
충돌 및 재시도 의도적으로 처리
동시 업데이트는 충돌할 수 있습니다. 두 작업자가 거의 동시에 동일한 문서를 업데이트하면 하나가 이전 버전을 기반으로 할 수 있습니다. 간단한 업데이트 재시도의 경우 Elasticsearch는 retry_on_conflict를 지원합니다:
curl -X POST "localhost:9200/products/_update/sku-mouse-x1?retry_on_conflict=3" \
-H "Content-Type: application/json" \
-d '{
"doc": {
"price": 21.99
}
}'
이는 위험이 낮은 업데이트에 유용하지만 명확한 소유권을 대체하지는 않습니다. 한 시스템이 제품 이름을 소유하고 다른 시스템이 재고를 소유하는 경우 잘 정의된 파이프라인을 통해 다른 필드를 업데이트하는 것을 고려하세요. 이벤트 순서가 중요한 경우 소스 타임스탬프를 포함하고 수집 계층이나 신중한 스크립트를 통해 이전 업데이트를 거부하세요.
디버깅 중 쓰기 후 읽기
색인 흐름을 테스트할 때 ID로 문서를 즉시 가져오세요:
curl -s "localhost:9200/products/_doc/sku-mouse-x1?pretty"
그런 다음 검색하세요:
curl -s "localhost:9200/products/_search?pretty" \
-H "Content-Type: application/json" \
-d '{
"query": {
"term": {
"sku": "mouse-x1"
}
}
}'
ID로 GET은 작동하지만 검색이 작동하지 않으면 새로 고침, 매핑, 분석기 또는 쿼리 유형을 생각하세요. 둘 다 실패하면 색인 오류, 잘못된 인덱스, 잘못된 ID, 인증 또는 라우팅을 생각하세요.
애플리케이션의 경우 대상 인덱스, 문서 ID, 응답 상태 및 항목 수준 대량 오류를 기록하세요. 모든 전체 문서를 영원히 기록할 필요는 없지만 롤아웃 중에는 이러한 필드가 시간을 절약해 줍니다.