🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Elasticsearch와 Node.js 연동 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 11. 29. · 25 Views

Elasticsearch와 Node.js 연동 완벽 가이드

Node.js 애플리케이션에서 Elasticsearch를 연동하여 강력한 검색 기능을 구현하는 방법을 알아봅니다. 클라이언트 설치부터 자동완성까지, 실무에서 바로 사용할 수 있는 핵심 기술을 다룹니다.


목차

  1. 클라이언트_라이브러리_설치
  2. 연결_설정
  3. 문서_인덱싱_구현
  4. 검색_API_구현
  5. 자동완성_구현
  6. 에러_핸들링

1. 클라이언트 라이브러리 설치

신입 개발자 김개발 씨가 첫 번째 대규모 프로젝트에 투입되었습니다. 프로젝트 리더가 말합니다.

"우리 서비스에 검색 기능을 추가해야 해요. Elasticsearch를 써볼까요?" 김개발 씨는 Elasticsearch라는 이름은 들어봤지만, Node.js에서 어떻게 연동하는지는 전혀 몰랐습니다.

@elastic/elasticsearch는 Elasticsearch 공식 Node.js 클라이언트 라이브러리입니다. 마치 데이터베이스를 사용할 때 드라이버가 필요하듯이, Elasticsearch와 통신하려면 이 클라이언트가 필수입니다.

npm이나 yarn으로 간단히 설치할 수 있으며, TypeScript도 완벽하게 지원합니다.

다음 코드를 살펴봅시다.

// 1. 패키지 설치 (터미널에서 실행)
// npm install @elastic/elasticsearch

// 2. 설치 확인을 위한 간단한 테스트
const { Client } = require('@elastic/elasticsearch');

// 클라이언트 인스턴스 생성
const client = new Client({
  node: 'http://localhost:9200'
});

// 연결 테스트
async function checkConnection() {
  const health = await client.cluster.health();
  console.log('클러스터 상태:', health.status);
}

checkConnection();

김개발 씨는 검색 엔진이라는 말에 약간 긴장했습니다. 뭔가 복잡하고 어려울 것 같았기 때문입니다.

하지만 선배 박시니어 씨는 웃으며 말했습니다. "걱정 마세요.

생각보다 훨씬 쉬워요. 우선 라이브러리부터 설치해봅시다." 그렇다면 Elasticsearch 클라이언트란 정확히 무엇일까요?

쉽게 비유하자면, 클라이언트 라이브러리는 마치 통역사와 같습니다. 여러분이 외국에 가서 현지인과 대화하려면 통역사가 필요하듯이, Node.js 애플리케이션이 Elasticsearch와 대화하려면 클라이언트 라이브러리가 필요합니다.

이 통역사가 여러분의 JavaScript 코드를 Elasticsearch가 이해할 수 있는 언어로 바꿔주는 것입니다. 예전에는 어떻게 했을까요?

클라이언트 라이브러리가 없던 시절에는 개발자들이 HTTP 요청을 직접 만들어야 했습니다. axios나 fetch로 일일이 REST API를 호출하고, 응답을 파싱하고, 에러를 처리해야 했습니다.

코드가 길어지고 실수하기도 쉬웠습니다. @elastic/elasticsearch 라이브러리가 이 모든 것을 해결해줍니다.

이 라이브러리를 사용하면 복잡한 HTTP 통신을 신경 쓸 필요가 없습니다. 마치 JavaScript 함수를 호출하듯이 자연스럽게 Elasticsearch를 다룰 수 있습니다.

자동 재시도, 연결 풀링, 로드 밸런싱까지 모두 내장되어 있습니다. 설치 과정을 살펴보겠습니다.

터미널에서 npm install @elastic/elasticsearch 명령어 하나면 됩니다. yarn을 사용한다면 yarn add @elastic/elasticsearch를 입력하면 됩니다.

설치가 완료되면 package.json의 dependencies에 추가된 것을 확인할 수 있습니다. 설치 후에는 간단한 테스트로 제대로 작동하는지 확인해봅니다.

위 코드에서 require 문으로 Client 클래스를 불러옵니다. 그리고 new Client()로 인스턴스를 생성하는데, 이때 Elasticsearch가 실행 중인 주소를 지정합니다.

기본적으로 localhost:9200에서 실행됩니다. cluster.health() 메서드는 Elasticsearch 클러스터의 상태를 확인합니다.

이 메서드가 성공적으로 실행되면 green, yellow, red 중 하나의 상태를 반환합니다. green은 모든 것이 정상이라는 뜻입니다.

이 테스트가 통과하면 연동 준비가 완료된 것입니다. 김개발 씨가 터미널에 명령어를 입력하자 설치가 순식간에 끝났습니다.

"어? 정말 이게 끝인가요?" 박시니어 씨가 고개를 끄덕였습니다.

"네, 설치는 이게 전부예요. 이제 본격적으로 연결 설정을 해볼까요?"

실전 팁

💡 - 프로덕션 환경에서는 반드시 특정 버전을 지정하여 설치하세요

  • TypeScript 프로젝트라면 타입 정의가 이미 포함되어 있어 별도 설치가 필요 없습니다

2. 연결 설정

라이브러리 설치를 마친 김개발 씨에게 박시니어 씨가 질문했습니다. "로컬 환경에서는 localhost로 접속하면 되지만, 실제 서버에서는 어떻게 해야 할까요?" 김개발 씨는 잠시 생각에 빠졌습니다.

분명 보안 설정이나 인증 같은 것들이 필요할 것 같았습니다.

Elasticsearch 연결 설정은 단순히 주소만 지정하는 것이 아닙니다. 인증 정보, SSL/TLS 설정, 타임아웃, 재시도 정책 등 다양한 옵션을 설정해야 합니다.

특히 프로덕션 환경에서는 보안을 위해 인증이 필수이며, 여러 노드를 지정하여 고가용성을 확보할 수도 있습니다.

다음 코드를 살펴봅시다.

const { Client } = require('@elastic/elasticsearch');

// 프로덕션 환경을 위한 상세 설정
const client = new Client({
  // 여러 노드 지정으로 고가용성 확보
  nodes: [
    'https://es-node1.example.com:9200',
    'https://es-node2.example.com:9200'
  ],
  // 인증 정보 설정
  auth: {
    username: process.env.ES_USERNAME,
    password: process.env.ES_PASSWORD
  },
  // 요청 타임아웃 설정 (밀리초)
  requestTimeout: 30000,
  // 최대 재시도 횟수
  maxRetries: 3,
  // SSL 인증서 검증 (프로덕션에서는 true 권장)
  tls: {
    rejectUnauthorized: true
  }
});

module.exports = client;

박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. "Elasticsearch 연결은 마치 은행 금고에 접근하는 것과 같아요." 왜 은행 금고일까요?

은행 금고에 들어가려면 신분증 확인, 비밀번호 입력, 보안 카드 인증 등 여러 단계를 거쳐야 합니다. Elasticsearch도 마찬가지입니다.

중요한 데이터가 저장되어 있기 때문에 아무나 접근하면 안 됩니다. auth 옵션이 바로 그 신분증 역할을 합니다.

username과 password를 설정하면 Elasticsearch는 이 정보를 확인한 후에만 요청을 처리합니다. 여기서 중요한 점은 절대로 코드에 비밀번호를 직접 작성하면 안 된다는 것입니다.

위 예제처럼 환경 변수를 사용하는 것이 안전합니다. nodes 옵션을 보면 배열로 여러 주소가 지정되어 있습니다.

이것은 마치 여러 개의 출입구를 만들어 두는 것과 같습니다. 만약 첫 번째 노드가 응답하지 않으면 클라이언트는 자동으로 두 번째 노드에 연결을 시도합니다.

이렇게 하면 서버 한 대가 다운되어도 서비스가 중단되지 않습니다. requestTimeout은 얼마나 기다릴지를 정합니다.

네트워크가 느리거나 Elasticsearch가 바쁠 때, 무한정 기다릴 수는 없습니다. 30000밀리초, 즉 30초로 설정하면 그 시간 내에 응답이 없으면 에러로 처리합니다.

상황에 따라 이 값을 조절해야 합니다. maxRetries는 실패했을 때 몇 번 다시 시도할지 정합니다.

네트워크는 불안정할 수 있습니다. 일시적인 문제로 첫 번째 요청이 실패해도 두 번째, 세 번째 시도에서 성공할 수 있습니다.

하지만 너무 많이 재시도하면 응답 시간이 길어지므로 적절한 값을 설정해야 합니다. tls 옵션은 HTTPS 연결의 보안 수준을 결정합니다.

rejectUnauthorized를 true로 설정하면 SSL 인증서를 엄격하게 검증합니다. 개발 환경에서는 자체 서명 인증서를 사용하느라 false로 설정하기도 하지만, 프로덕션에서는 반드시 true로 설정해야 합니다.

마지막으로 module.exports로 클라이언트를 내보냅니다. 이렇게 하면 애플리케이션 어디서든 이 클라이언트를 import해서 사용할 수 있습니다.

클라이언트는 내부적으로 연결 풀을 관리하므로, 매번 새로 생성할 필요 없이 하나의 인스턴스를 공유하는 것이 효율적입니다. 김개발 씨가 고개를 끄덕였습니다.

"아, 그래서 설정 파일을 따로 만드는 거군요!"

실전 팁

💡 - 환경 변수는 .env 파일과 dotenv 패키지로 관리하세요

  • 개발/스테이징/프로덕션 환경별로 다른 설정 파일을 준비해두면 편리합니다

3. 문서 인덱싱 구현

연결 설정을 마친 김개발 씨가 물었습니다. "이제 데이터를 어떻게 저장하나요?" 박시니어 씨가 대답했습니다.

"Elasticsearch에서는 데이터를 저장하는 것을 인덱싱이라고 해요. 마치 도서관에서 책을 정리해서 서가에 꽂아두는 것처럼요."

인덱싱은 Elasticsearch에 문서를 저장하는 과정입니다. 관계형 데이터베이스의 INSERT와 비슷하지만, Elasticsearch는 저장과 동시에 검색을 위한 색인을 자동으로 생성합니다.

단일 문서 저장, 대량 문서 저장, 그리고 업데이트까지 다양한 방식을 지원합니다.

다음 코드를 살펴봅시다.

const client = require('./elasticsearch');

// 단일 문서 인덱싱
async function indexDocument(index, document) {
  const result = await client.index({
    index: index,           // 인덱스 이름 (테이블과 유사)
    document: document,     // 저장할 데이터
    refresh: true           // 즉시 검색 가능하도록 설정
  });
  return result._id;        // 생성된 문서 ID 반환
}

// 대량 문서 인덱싱 (bulk)
async function bulkIndex(index, documents) {
  const operations = documents.flatMap(doc => [
    { index: { _index: index } },
    doc
  ]);

  const result = await client.bulk({
    operations,
    refresh: true
  });
  return result.items.length;
}

박시니어 씨가 도서관 비유를 이어갔습니다. "도서관을 상상해보세요.

새 책이 들어오면 어떻게 하나요?" 일반 창고라면 그냥 빈 공간에 던져두면 됩니다. 하지만 도서관은 다릅니다.

책이 들어오면 분류 번호를 붙이고, 저자별 색인 카드를 만들고, 주제별 목록에 추가하고, 서가의 정확한 위치에 꽂아둡니다. 나중에 누군가 책을 찾을 때 빠르게 찾을 수 있도록요.

Elasticsearch의 인덱싱도 정확히 같은 원리입니다. 문서를 저장하면 Elasticsearch는 그 내용을 분석합니다.

텍스트를 단어 단위로 쪼개고, 각 단어가 어느 문서에 있는지 기록합니다. 이것을 역색인이라고 합니다.

덕분에 나중에 검색할 때 번개처럼 빠르게 결과를 찾을 수 있습니다. 코드를 살펴보겠습니다.

client.index() 메서드가 핵심입니다. index 파라미터에는 인덱스 이름을 지정합니다.

이것은 관계형 데이터베이스의 테이블 이름과 비슷한 개념입니다. document 파라미터에는 실제 저장할 데이터를 JavaScript 객체로 전달합니다.

refresh: true 옵션이 중요합니다. Elasticsearch는 성능을 위해 기본적으로 데이터를 바로 검색 가능하게 만들지 않습니다.

약간의 지연이 있습니다. 하지만 refresh를 true로 설정하면 즉시 검색할 수 있습니다.

테스트할 때는 편리하지만, 대량 데이터를 저장할 때는 성능에 영향을 줄 수 있습니다. 만약 수천, 수만 개의 문서를 저장해야 한다면 어떻게 할까요?

하나씩 index()를 호출하면 네트워크 왕복 시간이 누적되어 매우 느려집니다. 이럴 때 bulk() 메서드를 사용합니다.

여러 문서를 한 번의 요청으로 모아서 전송하므로 훨씬 빠릅니다. bulk 요청의 형식이 조금 특이합니다.

operations 배열은 액션과 데이터가 번갈아가며 나타납니다. { index: { _index: 'products' } }는 "다음 데이터를 products 인덱스에 저장하라"는 명령이고, 그 다음에 실제 데이터가 옵니다.

flatMap을 사용하면 이 형식을 쉽게 만들 수 있습니다. 김개발 씨가 실제로 코드를 실행해봤습니다.

javascript await indexDocument('products', { name: '무선 마우스', price: 25000, category: '전자기기' }); 성공적으로 문서 ID가 반환되었습니다. "오, 정말 간단하네요!"

실전 팁

💡 - 대량 인덱싱 시에는 refresh를 false로 두고 마지막에 한 번만 refresh하세요

  • bulk 요청은 보통 1000~5000개 단위로 묶는 것이 효율적입니다

4. 검색 API 구현

데이터를 저장하는 방법을 배운 김개발 씨가 본격적으로 검색 기능 구현에 도전합니다. "검색이 Elasticsearch의 핵심이잖아요.

어떻게 하면 구글처럼 똑똑한 검색을 만들 수 있을까요?" 박시니어 씨가 미소 지으며 말했습니다. "Query DSL이라는 강력한 도구가 있어요."

Query DSL은 Elasticsearch의 검색 언어입니다. 단순 키워드 매칭부터 복잡한 조건 조합까지 다양한 검색을 표현할 수 있습니다.

match 쿼리는 텍스트 검색에, bool 쿼리는 여러 조건을 조합하는 데, range 쿼리는 범위 검색에 사용됩니다.

다음 코드를 살펴봅시다.

const client = require('./elasticsearch');

// 기본 검색 함수
async function search(index, keyword, options = {}) {
  const { from = 0, size = 10, category } = options;

  // bool 쿼리로 복잡한 조건 구성
  const query = {
    bool: {
      must: [
        { match: { name: keyword } }  // 반드시 매칭되어야 하는 조건
      ],
      filter: category ? [
        { term: { category: category } }  // 필터 조건 (점수에 영향 없음)
      ] : []
    }
  };

  const result = await client.search({
    index,
    query,
    from,                    // 시작 위치 (페이징용)
    size,                    // 가져올 개수
    highlight: {             // 검색어 하이라이트
      fields: { name: {} }
    }
  });

  return {
    total: result.hits.total.value,
    items: result.hits.hits.map(hit => ({
      ...hit._source,
      score: hit._score,
      highlight: hit.highlight
    }))
  };
}

박시니어 씨가 설명을 시작했습니다. "검색은 마치 탐정이 단서를 찾는 것과 같아요." 탐정에게는 여러 가지 조건이 있습니다.

"반드시 빨간 모자를 쓴 사람을 찾아라" - 이것은 필수 조건입니다. "가능하면 키가 큰 사람이면 좋겠다" - 이것은 선호 조건입니다.

"단, 20대여야 한다" - 이것은 필터 조건입니다. Elasticsearch의 bool 쿼리는 이런 다양한 조건을 조합합니다.

must는 반드시 만족해야 하는 조건입니다. 위 코드에서 match 쿼리가 must 안에 있습니다.

이것은 "검색어가 반드시 name 필드에 매칭되어야 한다"는 뜻입니다. match 쿼리는 단순 문자열 비교가 아니라 형태소 분석을 거친 스마트한 검색입니다.

"무선 마우스"를 검색하면 "마우스 무선"도 찾아줍니다. filter는 점수에 영향을 주지 않는 조건입니다.

Elasticsearch는 각 문서에 관련성 점수를 매깁니다. must 조건은 이 점수에 영향을 주지만, filter는 영향을 주지 않습니다.

단순히 "포함할지 말지"만 결정합니다. 카테고리 필터링처럼 점수와 무관한 조건에 적합합니다.

fromsize는 페이징을 위한 파라미터입니다. from은 건너뛸 문서 수, size는 가져올 문서 수입니다.

from: 0, size: 10이면 처음부터 10개를 가져옵니다. from: 10, size: 10이면 11번째부터 10개를 가져옵니다.

이렇게 페이지네이션을 구현할 수 있습니다. highlight 옵션은 검색 결과에서 매칭된 부분을 강조해줍니다.

구글 검색 결과를 보면 검색어가 굵은 글씨로 표시되죠? highlight가 바로 그 기능입니다.

fields에 하이라이트하고 싶은 필드를 지정하면 됩니다. 검색 결과는 hits 객체에 담겨옵니다.

hits.total.value에는 전체 매칭 문서 수가, hits.hits에는 실제 문서 배열이 들어있습니다. 각 문서의 _source에 원본 데이터가, _score에 관련성 점수가 담겨있습니다.

김개발 씨가 검색을 테스트해봤습니다. "무선"을 검색하니 "무선 마우스", "무선 키보드"가 점수 순으로 정렬되어 나왔습니다.

"와, 정말 똑똑하게 찾아주네요!"

실전 팁

💡 - 성능을 위해 필요한 필드만 _source에서 가져오려면 _source: ['name', 'price'] 옵션을 사용하세요

  • 대량의 결과를 처리할 때는 from/size 대신 scroll API나 search_after를 고려하세요

5. 자동완성 구현

검색 기능이 완성되자 기획팀에서 추가 요청이 들어왔습니다. "검색창에 글자를 입력하면 추천 검색어가 뜨는 자동완성 기능을 넣어주세요." 김개발 씨는 고민에 빠졌습니다.

일반 검색과 자동완성은 뭔가 다를 것 같았습니다. 빠르게 응답해야 하고, 부분 문자열도 매칭해야 하니까요.

자동완성은 사용자가 검색어를 입력하는 동안 실시간으로 추천어를 보여주는 기능입니다. Elasticsearch에서는 prefix 쿼리, match_phrase_prefix 쿼리, 또는 completion suggester를 사용할 수 있습니다.

각각 장단점이 있으며, 데이터 특성과 요구사항에 따라 선택합니다.

다음 코드를 살펴봅시다.

const client = require('./elasticsearch');

// 자동완성 검색 함수
async function autocomplete(index, prefix, size = 5) {
  // match_phrase_prefix로 접두어 검색
  const result = await client.search({
    index,
    query: {
      match_phrase_prefix: {
        name: {
          query: prefix,
          max_expansions: 10    // 확장할 최대 용어 수
        }
      }
    },
    size,
    _source: ['name'],          // 필요한 필드만 가져오기
    sort: [
      { _score: 'desc' },       // 관련성 점수 순
      { popularity: 'desc' }    // 인기도 순 (보조 정렬)
    ]
  });

  // 중복 제거 및 결과 포맷팅
  const suggestions = [...new Set(
    result.hits.hits.map(hit => hit._source.name)
  )];

  return suggestions;
}

// 사용 예시: autocomplete('products', '무선')
// 결과: ['무선 마우스', '무선 키보드', '무선 이어폰', ...]

박시니어 씨가 자동완성의 원리를 설명하기 시작했습니다. "자동완성은 마치 비서가 당신의 말을 예측하는 것과 같아요." 당신이 "회의실 예"까지 말하면 유능한 비서는 벌써 예측합니다.

"회의실 예약하시려고요?" 비서는 당신의 평소 패턴, 지금 시각, 이전 요청들을 종합해서 가장 가능성 높은 완성어를 제안합니다. Elasticsearch의 자동완성도 비슷하게 동작합니다.

match_phrase_prefix는 가장 직관적인 방법입니다. 입력된 텍스트로 시작하는 문구를 찾습니다.

"무선"을 입력하면 "무선 마우스", "무선 키보드" 등 "무선"으로 시작하는 모든 문서를 찾아줍니다. 마지막 단어만 접두어 매칭을 하고, 앞의 단어들은 완전 매칭을 합니다.

max_expansions 옵션이 중요합니다. 접두어 "마"를 검색하면 "마우스", "마이크", "마술", "마늘" 등 수많은 단어가 매칭될 수 있습니다.

max_expansions는 이 확장의 범위를 제한합니다. 10으로 설정하면 최대 10개의 용어만 확장하여 검색합니다.

이것이 성능과 품질 사이의 균형을 잡아줍니다. 왜 _source를 제한할까요?

자동완성은 빨라야 합니다. 사용자가 타이핑하는 동안 100~200밀리초 안에 응답이 와야 자연스럽습니다.

불필요한 필드를 전송하지 않으면 네트워크 비용을 줄일 수 있습니다. name 필드만 있으면 되니까 그것만 가져옵니다.

sort 옵션으로 결과 품질을 높입니다. 관련성 점수(_score)만으로 정렬하면 자주 검색되지 않는 항목이 위에 올라올 수 있습니다.

popularity 같은 인기도 필드를 추가로 정렬 기준에 넣으면 사용자가 원할 가능성이 높은 결과를 먼저 보여줄 수 있습니다. 마지막으로 중복 제거가 필요합니다.

같은 이름의 상품이 여러 개 있을 수 있습니다. Set을 사용해 중복을 제거하면 깔끔한 추천 목록을 만들 수 있습니다.

김개발 씨가 자동완성을 테스트해봤습니다. 검색창에 "무"를 입력하자 0.1초도 안 되어 추천어 목록이 나타났습니다.

"이 정도면 충분히 빠르네요!"

실전 팁

💡 - 더 빠른 자동완성이 필요하면 completion suggester와 전용 매핑을 사용하세요

  • 한글 자동완성은 형태소 분석기 설정에 따라 결과가 달라지니 테스트가 중요합니다

6. 에러 핸들링

모든 기능이 완성된 줄 알았던 김개발 씨에게 문제가 생겼습니다. 갑자기 Elasticsearch 서버가 응답하지 않자 애플리케이션 전체가 멈춰버린 것입니다.

박시니어 씨가 말했습니다. "에러 처리를 제대로 하지 않으면 이런 일이 생겨요.

Elasticsearch는 언제든 실패할 수 있다고 가정해야 합니다."

Elasticsearch 연동에서 에러 핸들링은 필수입니다. 네트워크 문제, 타임아웃, 인덱스 미존재, 쿼리 오류 등 다양한 에러가 발생할 수 있습니다.

각 에러 유형에 맞는 적절한 처리를 해야 서비스의 안정성을 확보할 수 있습니다.

다음 코드를 살펴봅시다.

const { Client, errors } = require('@elastic/elasticsearch');
const client = require('./elasticsearch');

// 안전한 검색 함수
async function safeSearch(index, query) {
  try {
    const result = await client.search({ index, query });
    return { success: true, data: result.hits.hits };
  } catch (error) {
    // 에러 유형별 처리
    if (error instanceof errors.ConnectionError) {
      console.error('ES 연결 실패:', error.message);
      return { success: false, error: 'SERVICE_UNAVAILABLE' };
    }
    if (error instanceof errors.TimeoutError) {
      console.error('ES 타임아웃:', error.message);
      return { success: false, error: 'TIMEOUT' };
    }
    if (error instanceof errors.ResponseError) {
      // HTTP 상태 코드별 처리
      if (error.statusCode === 404) {
        return { success: false, error: 'INDEX_NOT_FOUND' };
      }
      if (error.statusCode === 400) {
        console.error('잘못된 쿼리:', error.meta.body.error);
        return { success: false, error: 'INVALID_QUERY' };
      }
    }
    // 예상치 못한 에러
    console.error('예상치 못한 에러:', error);
    throw error;
  }
}

박시니어 씨가 진지한 표정으로 말했습니다. "외부 서비스와 연동할 때는 항상 최악의 상황을 가정해야 해요." 비행기 조종사를 생각해보세요.

조종사는 엔진이 고장 날 수 있다고 가정하고 비상 절차를 훈련합니다. 날씨가 나빠질 수 있다고 가정하고 대체 공항을 파악해둡니다.

개발자도 마찬가지입니다. Elasticsearch가 응답하지 않을 수 있다고 가정하고 대비해야 합니다.

@elastic/elasticsearch는 친절하게도 구체적인 에러 클래스를 제공합니다. errors 객체를 import하면 ConnectionError, TimeoutError, ResponseError 등을 사용할 수 있습니다.

instanceof로 에러 유형을 구분하면 상황에 맞는 처리를 할 수 있습니다. ConnectionError는 Elasticsearch에 연결 자체가 안 될 때 발생합니다.

서버가 다운되었거나, 네트워크 문제가 있거나, 방화벽이 막고 있을 때 이 에러가 발생합니다. 이런 경우 사용자에게 "검색 서비스가 일시적으로 불가능합니다"라고 안내하고, 캐시된 결과를 보여주거나 기본 목록을 표시할 수 있습니다.

TimeoutError는 요청이 시간 내에 완료되지 않았을 때 발생합니다. Elasticsearch가 너무 바쁘거나, 쿼리가 너무 복잡하거나, 네트워크가 느릴 때 발생합니다.

이런 경우 재시도를 하거나, 사용자에게 "잠시 후 다시 시도해주세요"라고 안내할 수 있습니다. ResponseError는 Elasticsearch가 응답은 했지만 오류를 반환했을 때 발생합니다.

statusCode로 HTTP 상태 코드를 확인할 수 있습니다. 404는 인덱스가 없다는 뜻이고, 400은 쿼리가 잘못되었다는 뜻입니다.

각각 다르게 처리해야 합니다. 왜 반환 형식을 **{ success, data/error }**로 통일할까요?

호출하는 쪽에서 일관된 방식으로 처리할 수 있기 때문입니다. if (result.success)로 성공 여부를 확인하고, 실패한 경우 error 코드에 따라 적절한 UI를 보여줄 수 있습니다.

예상치 못한 에러는 다시 throw합니다. 모든 에러를 삼켜버리면 디버깅이 어려워집니다.

처리 방법을 아는 에러만 처리하고, 모르는 에러는 상위로 전파해서 로그에 남기거나 알림을 받을 수 있게 해야 합니다. 김개발 씨가 에러 처리 코드를 추가한 후 다시 테스트해봤습니다.

이번에는 Elasticsearch가 꺼져도 애플리케이션이 멈추지 않고 "검색 서비스 점검 중" 메시지를 보여주었습니다. "이제야 제대로 된 서비스 같네요!"

실전 팁

💡 - 중요한 에러는 Slack이나 이메일로 알림을 받도록 설정하세요

  • 에러 발생 시 폴백 로직(캐시 조회, 기본 데이터 반환 등)을 준비해두면 사용자 경험이 좋아집니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Elasticsearch#Node.js#검색엔진#자동완성#Backend#Elasticsearch,Search,Backend

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.