Node.js · 2026-05-02

서비스 디스커버리와 동적 구성(Node.js 마이크로서비스)

Node.js 기반 마이크로서비스 환경에서 Consul을 활용한 서비스 디스커버리와 동적 구성을 설명하는 기술적 개념과 구현 사례

작성일 : 2026-05-02 ㆍ 작성자 : 관리자
post
목차

소개

마이크로서비스는 개별 서비스가 독립적으로 배포되고 확장되는 아키텍처다. 하지만 서비스가 늘어나면 서로를 찾는 문제와 설정을 일괄 관리하는 문제가 생긴다. 이 글은 Node.js 환경에서 서비스 디스커버리와 동적 구성의 개념을 설명하고, Consul과 연동해 실제로 구현하는 방법을 예제로 보여준다.

서비스 디스커버리 개념

서비스 디스커버리는 런타임에 서비스 인스턴스의 위치를 찾는 과정이다. 전통적인 정적 설정은 오케스트레이션 환경에서 유지보수가 어렵다. 반면, 서비스 레지스트리와 헬스체크를 사용하면 클라이언트는 최신 인스턴스 목록을 조회해 트래픽을 분산할 수 있다.

핵심 요소

  • 서비스 등록: 인스턴스 시작 시 레지스트리에 자신을 등록
  • 헬스체크: 상태를 주기적으로 보고해 비정상 인스턴스를 제외
  • 서비스 조회: 클라이언트가 레지스트리에서 가용 인스턴스 조회

Consul 개요

Consul은 서비스 레지스트리와 키값 저장소(KV), 헬스체크, DNS/HTTP 인터페이스를 제공한다. 비교적 가볍고 사용성이 좋아 Node.js 마이크로서비스 환경과 잘 어울린다. Consul을 사용하면 동적 구성도 KV를 통해 중앙에서 관리할 수 있다.

Node.js와 Consul 연동

공식 npm 라이브러리나 HTTP API를 통해 통신할 수 있다. 여기서는 npm 패키지 'consul'을 활용한 기본 흐름을 다룬다. 예제는 서비스 등록, 서비스 조회, KV 감시로 구성된다.

서비스 등록 예제

다음 코드는 간단한 Express 서비스가 시작될 때 Consul에 자신을 등록하고, 종료 시 등록을 해제하는 예다.

const express = require('express')
const Consul = require('consul')

const app = express()
const port = process.env.PORT || 3000

const consul = new Consul({ host: '127.0.0.1', port: '8500' })
const serviceId = 'user-service-' + port

app.get('/', (req, res) => {
  res.send('hello from user service')
})

app.listen(port, async () => {
  console.log('listening on', port)
  await consul.agent.service.register({
    id: serviceId,
    name: 'user-service',
    address: '127.0.0.1',
    port: Number(port),
    check: {
      http: 'http://127.0.0.1:' + port + '/',
      interval: '10s'
    }
  })
  console.log('registered', serviceId)
})

process.on('SIGINT', async () => {
  await consul.agent.service.deregister(serviceId)
  process.exit()
})

서비스 조회 예제

클라이언트는 Consul을 통해 가용한 인스턴스 목록을 조회하고 라운드로빈이나 클라이언트 사이드 로드밸런싱을 적용할 수 있다.

async function discoverService(consul, name) {
  const result = await consul.catalog.service.nodes(name)
  // result는 인스턴스 목록
  return result
}

// 사용 예시
// const instances = await discoverService(consul, 'user-service')

동적 구성: KV와 Watch

애플리케이션 설정을 KV에 넣어 중앙에서 관리하면 배포 없이도 설정을 변경 가능하다. Consul은 키값 변경을 감지하는 Watch 메커니즘을 제공한다. Node 프로세스는 변경 이벤트를 받아 런타임 설정을 교체할 수 있다.

KV 읽기와 감시 예제

아래 예제는 특정 키를 읽고, 변경되면 콜백을 통해 설정을 갱신한다.

const key = 'config/user-service'

// 초기 로드
async function loadConfig(consul) {
  const item = await consul.kv.get(key)
  if (item && item.Value) {
    return Buffer.from(item.Value, 'base64').toString()
  }
  return null
}

// 감시
const watcher = consul.watch({ method: consul.kv.get, options: { key: key, recurse: false } })
watcher.on('change', data => {
  if (data && data.Value) {
    const cfg = Buffer.from(data.Value, 'base64').toString()
    console.log('config updated', cfg)
    // 런타임 설정 반영 로직
  }
})
watcher.on('error', err => {
  console.error('watch error', err)
})

운영 고려사항

  • 헬스체크 경량화: 과도한 헬스체크는 레지스트리 부하를 유발
  • 레지스트리 고가용성: Consul 서버는 클러스터로 구성
  • 보안: ACL과 TLS로 접근 제어 및 암호화 적용
  • 캐싱: 빈번한 조회는 클라이언트 캐시로 최적화
  • 점진적 배포: 새 인스턴스 등록 시 트래픽 이동 전략 필요

요약과 권장 패턴

서비스 디스커버리와 동적 구성은 마이크로서비스 운영을 단순화한다. Consul과 같은 레지스트리는 인스턴스 목록 관리와 설정 중앙화를 동시에 제공한다. Node 서비스 디스커버리 구현에서는 다음을 권장한다.

  • 서비스는 시작 시 자동 등록하고 정상 종료 시 해제
  • 헬스체크를 통해 가용성 상태를 실시간 반영
  • 중앙 KV로 설정을 관리하고 Watch로 변경 반영
  • 보안과 고가용성은 초기에 설계

마무리

초기에는 단순한 등록과 조회만으로도 큰 이득을 볼 수 있다. 이후 동적 구성과 감시를 추가하면 더 유연한 운영이 가능해진다. 설명한 예제 코드를 바탕으로 실제 환경에 맞게 헬스체크, 보안, 캐시 정책을 조정하면 안정적인 Node 마이크로서비스 운영에 도움이 된다.

Node 서비스 디스커버리 구현 Consul Node.js 연동 동적 구성 Node 마이크로서비스 Node.js 마이크로서비스 서비스 레지스트리 Consul KV 서비스 헬스체크 서비스 디스커버리