Sentry 전화 알림: 운영을 망가뜨린 오류만 울리기

Sentry에는 전화 액션이 없습니다. 웹훅으로 이슈 알림을 흘려보내 운영을 망가뜨리는 오류만 전화로 울리게 하는 설정, 페이로드, 필터링 방법.

업데이트

목차

Sentry는 여러분에게 전화를 걸 수 없습니다. 메일을 보내거나 Slack에 올리거나 PagerDuty로 알림을 넘길 수는 있지만, 내장된 음성 액션은 없습니다. 인시던트 플랫폼을 구매하지 않고 그 기능을 얻는 방법은, Sentry 이슈 알림을 "울리는 웹훅"으로 보내는 것입니다. 내부 인테그레이션을 만들고, Echobell의 전화 채널로 향하게 한 다음, 그 앞에 필터를 두어 실제로 운영을 망가뜨리는 오류만 통과시키면 됩니다.

이 글은 전체 경로를 다룹니다. 인테그레이션, 알림 규칙, Sentry가 실제로 보내는 페이로드, 그것을 읽는 템플릿, 그리고 사람들이 중간에 포기하게 만드는 두 가지 함정까지.

Sentry 혼자서는 왜 전화를 울리지 못하나

Sentry 이슈 알림의 액션은 알림(메일, Slack, Discord, Microsoft Teams), 티켓 생성(Jira, GitHub, Azure DevOps), 페이징 제품으로의 전달(PagerDuty, Opsgenie)로 나뉩니다. 모두 "보고 있어야 알 수 있는 화면"이나 "다른 플랫폼의 유료 좌석"으로 끝납니다.

오후 2시라면 괜찮습니다. 하지만 새벽 3시에 Slack 메시지는 침묵과 구별되지 않고, 푸시 알림은 방해 금지 모드에 집니다. 두 시간 지연이 실제 돈으로 이어지는 소수의 오류 — 결제가 500을 반환하거나, 인증이 모든 로그인을 거부하거나, 워커가 조용히 작업을 버리는 상황 — 에는 울리는 기기가 필요합니다.

그 접합부가 웹훅입니다. Sentry는 알림 규칙의 액션으로 임의의 HTTPS 엔드포인트를 호출할 수 있고, Echobell은 그 HTTP 요청을 iOS 집중 모드를 뚫는 전화형 알림으로 바꿉니다.

준비물

  • Settings → Developer Settings에 들어갈 수 있는 Sentry 조직(owner 또는 manager)
  • Echobell 설치(App Store / Google Play)
  • 5분

여러분 쪽에서 인터넷으로 접근 가능해야 하는 것은 없습니다. 아웃바운드 요청은 Sentry가 보내고, 여러분은 받기만 합니다.

1단계 — 울리는 채널 만들기

Echobell에서 채널을 만들고 알림 유형을 전화(Calling) 로 설정합니다. 이것이 이 작업의 핵심입니다. 전화 채널은 푸시가 아니라 수신 전화처럼 동작하므로 집중 모드와 방해 금지를 통과합니다.

새벽 3시에도 이해되는 템플릿을 넣으세요. Sentry 페이로드는 깊게 중첩되어 있어 변수 경로가 평소보다 깁니다.

제목: {{data.event.level}}: {{data.event.metadata.type}}
본문: {{data.event.title}} — {{data.event.culprit}}

고급 설정에서 링크 템플릿을 지정하면 알림을 눌렀을 때 해당 이슈가 열립니다.

{{data.event.web_url}}

채널 상세 화면에서 웹훅 URL을 복사합니다. 형태는 이렇습니다.

https://hook.echobell.one/t/<channel-token>

2단계 — Sentry 내부 인테그레이션 만들기

Sentry는 인테그레이션을 통해서만 웹훅을 알림 규칙의 액션으로 제공하므로, 하나 만들어야 합니다. 서비스를 만드는 게 아니라 양식을 채우는 일이고, 코드는 한 줄도 필요 없습니다.

  1. Settings → Developer Settings → Custom Integrations로 이동
  2. Create New Integration → Internal Integration
  3. Name: Echobell (나중에 알림 규칙에서 고를 이름)
  4. Webhook URL: 1단계에서 얻은 채널 URL
  5. Alert Rule Action 토글을 켜기
  6. Permissions: Issue & Event → Read면 충분
  7. Webhooks 아래 체크박스는 모두 해제한 채로 — 이유는 아래 함정 참고
  8. 저장

내부 인테그레이션은 소속 조직 안에서만 쓰이고 스스로 설치됩니다. 생성되는 토큰은 이 구성에서는 쓸 일이 없습니다.

3단계 — 알림 규칙의 액션으로 추가하기

Alerts → Create Alert → Issue Alert로 가거나 기존 규칙을 편집합니다.

Then perform these actions에서 Send a notification via an integration을 추가하고 Echobell을 선택합니다.

Action interval("이 알림이 두 번 이상 발생했을 때"의 스로틀)을 최소 30 minutes로 설정하세요. 기본값은 발생할 때마다 전송이라, 분당 400번 터지는 오류는 여러분이 기능을 끌 때까지 계속 전화를 겁니다.

저장한 뒤 규칙의 테스트를 실행해, 실제 페이로드가 도착하는 것을 확인하고 나서 신뢰하세요.

4단계 — 실제로 무엇이 도착하는지 알기

대부분의 구성이 여기서 깨집니다. 페이로드 모양이 예상과 다르기 때문입니다. Sentry는 모든 것을 감쌉니다.

{
  "action": "triggered",
  "actor": { "id": "sentry", "name": "Sentry", "type": "application" },
  "data": {
    "event": {
      "event_id": "e4874d664c3540c1a32eab185f12c5ab",
      "level": "error",
      "title": "ReferenceError: heck is not defined",
      "culprit": "?(<anonymous>)",
      "platform": "javascript",
      "project": 1,
      "release": null,
      "metadata": { "type": "ReferenceError", "value": "heck is not defined" },
      "tags": [["level", "error"], ["browser", "Chrome 75.0.3770"]],
      "issue_id": "1117540176",
      "issue_url": "https://sentry.io/api/0/issues/1117540176/",
      "web_url": "https://sentry.io/organizations/test-org/issues/1117540176/events/e4874.../"
    },
    "triggered_rule": "Very Important Alert!"
  },
  "installation": { "uuid": "a8e5d2..." }
}

템플릿을 쓰기 전에 알아둘 네 가지:

  • 쓸 만한 값은 전부 data.event 아래에 있습니다. {{title}}은 아무것도 렌더링하지 않고, {{data.event.title}}이 오류를 렌더링합니다.
  • data.event.project는 숫자 ID이지 슬러그가 아닙니다. 알림에 읽을 수 있는 프로젝트 이름을 넣고 싶다면 제목 템플릿에 문자열로 직접 쓰고, 프로젝트마다 채널을 나누세요.
  • environment 필드는 없습니다. 환경은 data.event.tags 안에 ["environment", "production"] 쌍으로 들어오고, 배열 내 위치가 고정되지 않습니다. 인덱스로 접근하지 마세요. 환경 필터링은 Sentry 규칙에서 처리합니다(5단계).
  • data.triggered_rule 은 규칙 이름입니다. 한 채널이 여러 규칙을 담당할 때 본문에 넣으면 유용합니다.

이슈 알림의 Sentry-Hook-Resource 헤더 값은 event_alert입니다. Echobell 채널 조건에서 이를 요구하면 다른 어떤 것도 이 채널을 울릴 수 없습니다.

header["sentry-hook-resource"] == "event_alert"

5단계 — 전화를 받을 만한 것까지 좁히기

새 이슈마다 울리는 전화 채널은 채널이 없느니만 못합니다. 일주일이면 음소거할 테고, 그러면 정작 중요한 순간에도 울리지 않습니다. 두 군데서 걸러내세요.

Sentry 쪽은 규칙의 conditions와 filters로:

목표규칙 설정
운영 환경만규칙의 Environment를 production으로
진짜 장애만필터: The event's level equals fatal (또는 error)
일시적 흔들림 무시조건: The issue is seen more than 25 times in 1 hour
핵심 경로만필터: The event's tags match transaction contains /checkout
재발만조건: A resolved issue changes state from resolved to unresolved

Echobell 쪽에서는 Sentry로 표현할 수 없는 것, 또는 오늘 당장 규칙을 바꿀 수 없을 때의 보완책으로 채널 조건을 씁니다.

data.event.level == "fatal" || data.event.level == "error"

심각도 필터링은 보통 Sentry 규칙 쪽이 낫습니다. 스로틀도 같은 자리에 있기 때문입니다. Echobell 쪽이 나은 경우는 하나의 Sentry 규칙에서 두 단계의 긴급도를 뽑고 싶을 때입니다.

6단계 — 경고에는 조용한 문을 따로

단계를 나누는 이유는 전화가 무게를 유지하게 하기 위해서입니다. 시간 민감(Time Sensitive) 유형의 두 번째 Echobell 채널을 만들고, 기준이 더 낮은 두 번째 Sentry 규칙을 추가해 두 번째 내부 인테그레이션으로 향하게 하세요(인테그레이션 하나에 웹훅 URL 하나이므로, 채널을 늘리면 인테그레이션도 늘려야 합니다).

실제 한 주를 버티는 구성은 대략 이렇습니다.

Sentry 규칙레벨 / 임계값Echobell 채널동작
prod-fatalfatal, 운영전화집중 모드를 뚫고 울림
prod-error-spikeerror, 1시간에 100회 초과시간 민감잠금 화면에 표시, 울리지 않음
new-issue-digest모든 새 이슈일반평범한 푸시, 시간 날 때 확인

업무 시간 외에만 울리기

낮에는 어차피 Sentry를 보고 있습니다. Echobell은 조건에서 쓸 수 있는 UTC 시스템 시간 변수를 제공하므로, Sentry에 규칙을 하나 더 만들지 않고도 시간대별로 동작을 달리할 수 있습니다.

data.event.level == "fatal" && (hour >= 17 || hour < 9)

주말이 정말 쉬는 날이라면 요일 검사도 더하세요.

data.event.level == "fatal" && (hour >= 17 || hour < 9 || dayOfWeek == 0 || dayOfWeek == 6)

모두 UTC 기준이므로 숫자를 확정하기 전에 자신의 표준시로 환산하세요. 전체 변수 목록은 조건 레퍼런스에 있습니다.

두 가지 함정

함정 1: Webhooks 체크박스를 켜는 것. 내부 인테그레이션에는 서로 독립적인 두 개의 웹훅 경로가 있습니다. Alert Rule Action 토글은 알림 규칙에서 이 인테그레이션을 고를 수 있게 해주는 것 — 여러분이 원하는 쪽입니다. 반면 Webhooks 체크박스(issue, error, comment)는 해당 리소스의 모든 이벤트를 구독합니다. 조직 전체에서 이슈가 생성·해결·할당·보관·무시될 때마다 전부요. issue를 켜면 동료가 무언가를 해결할 때마다 전화가 울립니다. 전부 꺼두면 여러분의 알림 규칙만 웹훅을 발생시킵니다.

함정 2: 구형 Webhooks 플러그인 사용. 프로젝트 단위의 옛 Legacy Integrations → WebHooks 플러그인은 여전히 존재하고 여전히 동작합니다. 인테그레이션을 만들지 않고 URL만 붙여넣으면 되니 지름길처럼 보이죠. 하지만 페이로드 모양이 다르고(더 평평하고), 요청에 서명이 없으며, Sentry 스스로도 신규 구성을 다른 쪽으로 유도합니다. 이걸 쓰면 위와 다른 변수 경로가 필요해집니다. 내부 인테그레이션을 쓰세요.

페이로드 크기, 오류 폭주, 잘림

규모가 커지기 전에 알아둘 만한 제한이 셋 있습니다.

  • 본문 1 MiB. Echobell은 1 MiB를 넘는 트리거 본문을 HTTP 413으로 거부합니다. Sentry 페이로드는 전체 스택 트레이스와 요청 컨텍스트를 실어 보통 수십 KB에 머물지만, 요청 본문이 큰 이벤트는 한계에 근접할 수 있습니다. Sentry 쪽에는 max_alerts 같은 손잡이가 없으므로, 대응책은 SDK의 beforeSend에서 큰 요청 본문을 정리하는 것입니다. 프라이버시 측면에서도 어차피 해야 할 일입니다.
  • 토큰당 분당 120회 요청. 초과하면 트리거는 429와 RATE_LIMIT_EXCEEDED, Retry-After를 반환합니다. 이 한도 아래로 유지해 주는 것이 Sentry의 action interval이고, 30 minutes면 충분합니다.
  • 알림 본문 1500바이트. 그보다 긴 결과는 기기에 도달하기 전에 잘립니다. data.event.title과 culprit은 넉넉히 들어가지만 data.event.exception을 통째로 붓는 건 무리이고, 잠금 화면에서 읽히지도 않습니다. 상세 내용은 링크 템플릿 뒤에 두세요.

팀과 공유하기

Echobell 채널은 여러 사람이 구독할 수 있고, 구독자마다 알림 유형을 직접 고릅니다. 따라서 같은 Sentry 규칙이 온콜 담당자의 전화는 울리고 나머지에게는 평범한 푸시로 도착하게 할 수 있습니다. 좌석당 과금도, 로테이션 설정도 없습니다.

다만 이것은 에스컬레이션 정책이 아닙니다. "5분 안에 아무도 확인하지 않으면 다음 사람에게 전화" 같은 건 없습니다. 그게 필요하면 진짜 온콜 플랫폼이 필요하고, Echobell은 그 아래의 전달 계층을 담당합니다.

이 구성이 제공하지 않는 것

분명히 말해 둡니다.

  • 확인(ack)이 없습니다. 전화를 받아도 Sentry에는 아무것도 전달되지 않고, 다른 구독자의 기기도 멈추지 않습니다.
  • 로테이션도 에스컬레이션도 없습니다. 구독자 전원이 받거나, 아무도 받지 않거나입니다.
  • Sentry를 넘어서는 중복 제거가 없습니다. 그룹화와 스로틀은 Sentry 규칙에서 일어나고, Echobell은 도착한 것을 전달합니다.
  • 양방향 동기화가 없습니다. Sentry에서 이슈를 해결해도 휴대폰에서는 아무것도 정리되지 않습니다.

이것들이 결정적 문제라면 맞는 도구가 아닙니다. 필요한 게 "결제가 망가지면 깨워줘"라면, 그 결과를 얻는 가장 저렴하고 확실한 방법에 가깝습니다.

문제 해결

규칙은 발동하는데 아무것도 오지 않음. 인테그레이션에서 Alert Rule Action이 켜져 있는지 확인하세요. 꺼져 있으면 규칙의 액션 목록에 아예 나타나지 않고, 켜기 전에 저장한 규칙은 낡은 액션을 그대로 갖고 있습니다.

알림은 오는데 내용이 비어 있음. 템플릿이 최상위 키를 읽고 있습니다. Sentry는 모든 것을 data.event 아래에 중첩합니다.

예상치 못한 것에 울림. 먼저 인테그레이션의 Webhooks 체크박스(함정 1)를, 다음으로 규칙의 환경이 "All Environments"로 남아 있지 않은지 확인하세요.

같은 오류로 반복해서 울림. Sentry 규칙의 action interval을 올리세요. Echobell의 재시도는 별개입니다. 앱 설정의 실패한 통화 재시도는 놓친 전화를 다시 거는 기능입니다.

테스트조차 오지 않음. 먼저 curl로 채널을 울려 Echobell 쪽을 배제하세요.

curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"data":{"event":{"level":"fatal","title":"Test error","culprit":"manual test","metadata":{"type":"TestError"}}}}'

이건 울리는데 Sentry는 안 울린다면, 문제는 채널이 아니라 인테그레이션에 있습니다.

자주 묻는 질문

Sentry가 자체적으로 전화를 걸 수 있나요?

아니요. Sentry 이슈 알림의 액션은 알림, 티켓 생성, 페이징 제품과의 연동입니다. 음성 통화에는 외부 서비스가 필요합니다. PagerDuty 같은 페이징 플랫폼이거나, Echobell처럼 울리는 웹훅 수신기입니다.

Sentry 알림이 방해 금지를 뚫나요?

전화형 알림으로 도착할 때만 그렇습니다. 전화 유형의 Echobell 채널은 수신 전화처럼 동작하므로 iOS 집중 모드와 방해 금지가 통과시킵니다. 어떤 앱이든 일반 푸시는 통과하지 못합니다.

웹훅에 Sentry 유료 플랜이 필요한가요?

내부 인테그레이션과 알림 규칙 액션은 Sentry Developer 플랜 이상에서 사용할 수 있습니다. 웹훅 자체에 추가 비용은 없습니다.

템플릿 변수가 왜 비어 있나요?

거의 항상 경로가 짧기 때문입니다. 알림 페이로드는 이벤트를 data.event 아래에 중첩하므로 {{title}}이 아니라 {{data.event.title}}입니다. 채널을 한 번 울려보고 앱에 기록된 요청 본문에서 실제 형태를 확인하세요.

특정 환경에만 알림을 보내려면?

Sentry 알림 규칙의 Environment 필드를 설정하세요. data.event.tags에서 읽으려 하지 마세요. 순서가 보장되지 않는 [키, 값] 쌍 배열입니다.

같은 오류로 두 사람에게 전화할 수 있나요?

가능합니다. 채널을 공유하고 각자 원하는 알림 유형으로 구독하면 됩니다. 확인 기능이 없으므로 "전화"를 고른 사람 모두에게 전화가 갑니다.

필터링은 Sentry와 Echobell 중 어디서?

가능하면 Sentry에서. 규칙에는 스로틀과 환경 범위도 함께 있습니다. 하나의 규칙에서 두 단계 긴급도를 만들고 싶을 때, 시간대 창이 필요할 때, 오늘 규칙을 바꿀 수 없을 때는 Echobell에서.

정리

구성 요소는 넷입니다. 전화 채널, Alert Rule Action이 켜져 있고 Webhooks 체크박스는 꺼진 내부 인테그레이션, 전화를 받을 만큼 좁혀진 알림 규칙, 그리고 data.event를 읽는 템플릿. 이 페이지의 나머지는 전부, 한 달 뒤에도 그 벨소리가 의미를 갖도록 충분히 좁게 유지하는 방법에 관한 이야기입니다.

iPhone용 Echobell 다운로드 또는 Google Play에서 받기. 정말 중요한 일을 이 경로에 맡기기 전에, 위의 curl부터 한 번 보내보세요.

관련 글