Sentry 란 무엇인가: 에러 수집 도구가 하는 일과 대안 비교
헬스체크는 200 을 돌려주는데 화면은 500 입니다. 컨테이너는 떠 있고, 워치독은 조용하고, 서버 로그에는 같은 스택 트레이스가 수백 줄 흩어져 있습니다. 어느 배포부터 시작됐는지, 몇 명이 겪었는지, 지금도 나고 있는지는 로그를 아무리 봐도 안 나옵니다.
이 자리가 에러 수집 도구가 필요한 자리이고, 그 자리에서 가장 많이 나오는 이름이 Sentry 입니다.
왜 쓰는가: 고객이 말해 주기 전에, 그리고 어느 것부터
오류를 겪은 사용자 대부분은 신고하지 않습니다. 그냥 닫습니다. 그래서 「문의가 없으니 괜찮다」는 오류가 없다는 뜻이 아니라 아직 아무도 말해 주지 않았다는 뜻입니다. 에러 수집을 들이는 첫 번째 이유는 이 침묵을 신호로 바꾸는 것입니다. 오류가 나는 순간 우리가 먼저 압니다.
두 번째는 순서입니다. 오류는 언제나 여러 개이고 전부 고칠 시간은 없습니다. 어느 것이 하루에 300번 나고 어느 것이 한 번 났는지, 어느 것이 결제 화면에서 나고 어느 것이 설정 화면에서 나는지가 보이면 오늘 고칠 것이 정해집니다. 로그만으로는 이 순서를 사람이 세어서 만들어야 하고, 그 일은 매번 하지 않게 됩니다.
세 번째는 「고쳤나」입니다. 고쳤다고 생각한 오류가 다음 배포에서 다시 나는지를 사람이 기억해서 확인하지는 못합니다. 이슈를 닫아 두면 도구가 회귀를 알려 줍니다. 저는 이 세 번째가 가장 값이 크다고 봅니다. 앞의 둘은 부지런하면 흉내라도 내지만, 회귀 감지는 기록이 없으면 아예 못 합니다.
마지막으로 재현입니다. 「제 환경에서는 안 납니다」의 절반은 그 사용자의 브라우저·데이터·직전 동작을 모르기 때문이고, 그 셋이 오류와 함께 저장돼 있으면 재현 없이도 원인이 보이는 경우가 많습니다.
로그가 있으면 된다는 생각이 어긋나는 지점
흔히 에러 수집을 「로그를 한곳에 모으는 것」으로 이해합니다. 그러면 로그를 이미 모으고 있는 팀은 Sentry 가 왜 필요한지 모릅니다. 실제로 둘은 다른 질문에 답합니다.
로그는 「그때 무슨 일이 있었나」에 답합니다. 한 줄이 한 사건이고, 시간순으로 쌓입니다. 에러 수집은 「지금 무엇이 깨져 있나」에 답합니다. 같은 원인에서 난 오류를 하나로 묶고, 처음 난 때와 마지막 난 때와 횟수를 세고, 새로 생긴 것과 늘어나는 것을 골라냅니다.
로그에 같은 줄이 317번 있는 것과, 화면에 「이 오류 317회, 배포 v2.3.1 부터, 사용자 12명」이 한 줄로 있는 것의 차이입니다. 앞의 것에서 뒤의 것을 만들려면 사람이 grep 을 하고 세고 비교해야 하고, 그 일을 매번 하지 않으니 결국 고객이 먼저 말해 줍니다.
Sentry 가 실제로 하는 네 가지
Sentry 는 앱에 SDK 를 넣으면 잡히지 않은 예외를 자동으로 서버로 보내고, 서버가 그것을 묶고 셈하고 알리는 서비스입니다. 하는 일을 넷으로 가르면 이렇습니다.
잡기. 서버에서 던져진 예외, 브라우저의 window.onerror, 처리되지 않은 Promise 거부를 SDK 가 가로챕니다. Next.js 라면 @sentry/nextjs 가 서버·엣지·클라이언트 세 런타임에 각각 붙습니다.
묶기. 스택 트레이스에서 지문을 만들어 같은 원인의 오류를 하나의 「이슈」로 합칩니다. 이게 핵심입니다. 묶지 않으면 알림이 317번 오고, 묶으면 한 번 옵니다.
맥락. 오류가 난 요청의 URL·헤더, 로그인한 사용자, 브라우저·OS, 그리고 오류 직전에 일어난 일(브레드크럼: 어떤 버튼을 눌렀고 어떤 요청이 나갔는지)을 같이 저장합니다. 「재현이 안 된다」의 절반은 이 맥락이 없어서 생깁니다.
알림. 새 이슈가 생기거나 횟수가 평소보다 튀면 슬랙·메일·텔레그램 등으로 보냅니다. 회귀(고쳤다고 닫은 이슈가 다시 나는 것)를 따로 알려 주는 것이 로그 알림과 다른 점입니다.
소스맵과 릴리스가 없으면 반쪽인 이유
브라우저에서 난 오류의 스택은 압축된 코드를 가리킵니다. main-3f2a1.js:1:48213 같은 줄은 사람이 읽을 수 없습니다. 소스맵을 빌드 때 Sentry 에 올려 두면 그것을 원래 파일과 줄 번호로 되돌려 줍니다. 이 단계를 빼면 클라이언트 오류는 「어딘가에서 undefined 를 읽었다」 이상을 말해 주지 못합니다.
릴리스는 「이 오류가 어느 배포부터 시작됐나」의 축입니다. 배포마다 버전을 태그해 두면 Sentry 가 이슈마다 첫 발생 릴리스를 붙이고, 그러면 원인 후보가 그 배포의 변경분으로 좁혀집니다. 소스맵과 릴리스는 설치 마법사(npx @sentry/wizard)가 같이 잡아 주지만, CI 에서 빌드하는 팀은 그 단계가 CI 에도 들어가야 합니다. 저는 이 둘이 빠진 Sentry 설치를 「붙였다」고 부르지 않는 편입니다.
비용은 오류 수가 아니라 보내는 횟수에 걸립니다
Sentry 는 이벤트 수로 과금합니다. 글을 쓰는 시점의 요금표 기준으로 무료 플랜(Developer)은 월 오류 5,000건까지이고, 그 위는 이벤트 단가로 올라갑니다.
여기서 감이 잘못 잡히는 자리가 있습니다. 5,000건은 「오류 5,000종」이 아니라 「보낸 횟수 5,000번」입니다. 같은 오류가 한 시간에 1,000번 나면 그것만으로 하루에 한도를 넘깁니다. 그래서 SDK 쪽에서 샘플링(sampleRate)을 걸거나, 서버 쪽에서 같은 이슈의 반복을 버리는 설정(스팸 필터·레이트리밋)을 처음부터 봐야 합니다. 트래픽이 있는 서비스에서 「무료로 시작했다가 첫 장애 때 한도를 넘겨 그 장애가 안 보이는」 모양이 이 도구의 대표적인 함정이라고 봅니다.
SDK 한 줄이 무엇을 가져가는지 알아야 합니다
Sentry SDK 는 오류만 보내는 것이 아니라 그 순간의 맥락을 같이 보냅니다. 요청 URL 과 헤더, 쿠키, 로그인한 사용자 식별자, 브레드크럼에 담긴 요청 본문 일부까지 기본값에 따라 포함될 수 있습니다. 그것이 진단에는 값이지만, 동시에 고객 데이터가 회사 밖 서버로 나가는 통로이기도 합니다.
SDK 에는 보내기 직전에 이벤트를 고치거나 버리는 beforeSend 훅과, 개인정보를 기본으로 안 담는 옵션(sendDefaultPii)이 있습니다. 저는 이 설정을 「나중에」로 미루지 않겠습니다. 오류 안에 무엇이 섞이는지는 오류가 난 뒤에야 알게 되고, 그때는 이미 나간 뒤이기 때문입니다. 개인정보나 계약 문서를 다루는 서비스라면 이 훅을 먼저 쓰고 SDK 를 켜는 순서가 맞습니다.
데이터가 어디에 있느냐가 선택을 가릅니다
같은 「에러 수집」이라도 데이터가 어디에 쌓이느냐로 셋이 갈립니다.
SaaS. Sentry 서버에 쌓입니다. 붙이는 데 한나절이고 운영이 없습니다. 대신 위에서 말한 데이터가 밖으로 나갑니다.
자체 호스팅. Sentry 는 오픈소스라 직접 띄울 수 있지만 가볍지 않습니다. 공식 self-hosted 문서가 요구하는 최소 사양이 CPU 4코어·메모리 16GB 이고, 컨테이너가 수십 개입니다. 「Sentry 를 쓰고 싶은데 데이터는 안 내보낸다」의 정직한 값은 서버 한 대와 그 운영입니다.
직접 받는다. 오류를 묶고 세고 알리는 것만 필요하면 그 셋은 작은 코드입니다. Next.js 15 부터는 instrumentation.ts 의 onRequestError 훅이 서버 오류를 전부 넘겨주므로, 받는 쪽 엔드포인트 하나와 지문·횟수를 저장할 표 하나면 시작됩니다. 소스맵·릴리스·브레드크럼·사용자별 영향은 없습니다. 그 넷이 필요해지는 날이 Sentry 로 가는 날입니다.
비슷한 것들과 나란히 보면
| 데이터 위치 | 붙이기 | 비용 감각 | 어울리는 곳 | |
|---|---|---|---|---|
| Sentry (SaaS) | 해외 SaaS | 한나절, 마법사 | 무료 5,000건/월, 이벤트 단가 | 대부분의 팀. 기준점 |
| Sentry (self-hosted) | 우리 서버 | 며칠 | 서버 한 대(4코어·16GB)와 운영 | 데이터를 못 내보내는데 Sentry 기능이 다 필요할 때 |
| GlitchTip | 우리 서버 또는 SaaS | 한나절 | 컨테이너 몇 개. Sentry SDK 그대로 씀 | Sentry 의 핵심만 가볍게 자체 호스팅 |
| Rollbar | 해외 SaaS | 한나절 | 이벤트 단가 | Sentry 와 같은 자리. 취향 차이 |
| Bugsnag (Insight Hub) | 해외 SaaS | 한나절 | 이벤트 단가 | 모바일 앱 비중이 클 때 강함 |
| Datadog · New Relic | 해외 SaaS | APM 과 함께 | APM 요금 안에 포함 | 이미 APM 을 쓰고 있을 때. 에러만 보려고 들이진 않음 |
| PostHog | SaaS 또는 자체 | 한나절 | 제품 분석 요금 안에 | 제품 분석·세션 리플레이를 같이 볼 때 |
| 직접 만든다 | 우리 DB | 이틀 | 표 둘 | 묶기·세기·알림만 필요하고 데이터를 안 내보낼 때 |
표에서 갈리는 축은 결국 둘입니다. 데이터가 밖으로 나가도 되는가, 그리고 소스맵·릴리스·리플레이 같은 것까지 필요한가. 둘 다 「예」면 Sentry SaaS 고, 앞이 「아니오」면 GlitchTip 이나 직접 받는 쪽이고, 뒤가 「아니오」면 굳이 큰 도구를 들일 이유가 없습니다.
여기까지가 확실한 부분
에러 수집은 「깨진 것」을 보는 도구이지 「느린 것」이나 「죽은 것」을 보는 도구가 아닙니다. 응답 시간 추세는 APM 이나 자체 계측의 몫이고, 서비스가 통째로 죽으면 SDK 도 같이 죽어 아무것도 보내지 않습니다. 그래서 워치독이나 상태 페이지와 자리를 나눠 갖습니다.
그리고 어느 도구를 고르든 첫 주에 오는 것은 「이렇게 많이 나고 있었나」입니다. 그중 대부분은 봇의 이상한 요청, 광고 차단기가 막은 스크립트, 오래된 브라우저입니다. 그 소음을 걸러 내는 규칙을 세우기 전까지는 알림을 켜지 않는 편이 낫다고 봅니다. 알림이 매일 오면 아무도 안 보고, 그러면 진짜가 왔을 때도 안 봅니다.
함께 읽기
- cmux와 Orca 비교: 터미널을 늘릴 것인가, 시도를 늘릴 것인가두 앱의 소개 문구는 거의 같습니다. 코딩 에이전트를 여러 개 동시에 돌린다는 것입니다. 그런데 cmux는 Swift와 AppKit으로 짠 macOS 전용 앱이고, Orca는 Electron으로 짜서 Windows와 Linux, 심지어 휴대폰까지 갑니다. 라이선스도 GPL-3.0과 MIT로 갈립니다.
- colima 자동 기동: brew services가 10초마다 재실행되는 이유개발기에 도커를 새로 깔 일이 생겼습니다. 컨테이너 하나만 돌리면 되는 일이라 가볍게 끝날 줄 알았는데, 설치 자체는 5분이었고 그 뒤에 두 가지를 더 고쳐야 했습니다. 하나는 깃 저장소가 더러워진 것이고, 하나는 코어 하나의 2.8%가 계속 타고 있던 것입니다.
- 드라이런과 멱등성: 사고를 막는 시점의 차이배포 스크립트에 안전장치를 넣는다고 할 때 두 가지가 자주 같이 나옵니다.
- 새 맥북 개발 환경 세팅: git clone이 가져오지 않는 것들맥북을 새로 받고 하던 프로젝트를 이어서 하려 했습니다. 리포를 클론하고 npm install 을 돌린 다음 개발 서버를 띄웠는데, 브라우저에 이게 떴습니다.
- xcode-select와 DEVELOPER_DIR: 어느 쪽이 정석일까?Expo 앱을 iOS 시뮬레이터에 띄우려고 도구부터 점검했는데, 결과가 앞뒤가 안 맞았습니다.