태그
가이드경험공유
최종 편집
Dec 30, 2022 2:32 AM
발행일
October 25, 2021
회사 블로그에 쓰려고 + 사내 공유용으로 만들었는데 이직도 하게 됐고.. 더 편집하는 시간이 아까워서 이대로라도 올려둡니다.
Sentry?
- 퍼포먼스 모니터링, 유저 피드백 수집 등도 가능하긴 하나, 근본은 에러 모니터링 도구
- 에러 모니터링에 Sentry가 유명하긴 하나 정답은 아님. 환경에 따라 더 적합한 게 있을 수 있음
- 서버 / 웹 / 앱
- React / Vue / Angular
- SPA / MPA / SDK / Node Package
- Sentry의 큰 장점이 Breadcrumb인데 이게 다른 툴에 없는 것은 아님
- 검색해보면 대체재도 많음. 그리고 대체재마다 일반적으로 다른 툴(특히 Sentry)와 비교해서 자기네가 왜 좋은지 설명하기도 함
- https://www.g2.com/products/sentry/competitors/alternatives
- https://www.honeybadger.io/vs/sentry/
- https://rollbar.com/vs/sentry/
- sentry-javascript는 버전이 올라갈수록 패키지 사이즈가 엄청나게 커지고 있어서 불만이 많음
- 다음 메이저 버전(v7)에서 고친다고 했는데 이 버전도 나오지 않고 있음
- micro-sentry 같은 대체재도 논의되고 있는듯
시작하기
- 플랫폼별로 문서화가 상당히 잘 되어있는 편. React는 여기를 참조
- 프레임워크 선택해서 프로젝트 새로 만들면
dsn
을 발급받고, 이걸 이용해서init
이 가능해짐 - 웹 클라이언트에서는, npm에서 각 프레임워크에 맞는 패키지를 설치해서 사용하면 됨
init
할 때 여러 옵션을 선택할 수 있고, integration도 이것저것 있음- release: 직접 설정할 수 있지만, 최대한 자동화하기 위해 KCD 프론트엔드에서는 Latest git commit hash(
git rev-parse HEAD
)를 버전으로 사용. Github이 public이라면 자동 연결도 가능 - ignoreErrors: 우리의 노력으로 줄이기 어려운(주로 네트워크와 관련된) 에러는 Sentry에서 ignore 하는 대신 애초에 보내지 않는 게 좋음(그래야 과금되지 않음). KCD는 배포 없이 이러한 에러를 추가/수정할 수 있게 CDN에 올려두고 fetch해와서 사용.
- 그러면 내 클라이언트에 대한 정보를 Sentry에 어떻게 올리는가? → sentry-cli를 사용
- 현재 commit association이 잘 안되고, 새 버전이 올라오면 이전 버전의 소스 맵이 없어지는 등 몇가지 문제가 있어서 리서치 및 개선 예정
export async function initialize({
dsn,
allowUrls,
environment,
release,
ignoreErrors = [],
integrations = [new Integrations.BrowserTracing()],
tracesSampleRate = 0.1,
}: BrowserOptions) {
const IGNORE_ERRORS = (await fetchIgnoreErrors()) as string[];
Sentry.init({
dsn, // 프로젝트별 유니크 키
allowUrls, // 어떤 URL에서 오는 에러 리포트만 허용할 것인가
environment, // develop, staging, production...
release, // 클라이언트의 릴리즈 버전. Latest git commit hash를 버전으로 사용
ignoreErrors: [...IGNORE_ERRORS, ...ignoreErrors], // 클라이언트에서 무시할 에러 목록
integrations,
tracesSampleRate, // 퍼포먼스 트래킹 비율. 낮을수록 이 측정이 유저에게 미치는 영향이 적고 높을수록 정확함
});
const appVersionString = appVersion();
if (appVersionString) { // 커스텀 태그도 설정 가능(e.g., 캐시노트 앱 버전)
Sentry.setTag('appVersion', `${platform()}-${appVersionString}`);
}
}
// script in package.json: 빌드할 때 환경변수로 commit hash 주입
"build:production": "REACT_APP_DEPLOY_ENV=production REACT_APP_GIT_SHA=`git rev-parse HEAD` craco build",
// Sentry init할 때 변수로 주입
initialize({
dsn: ...,
allowUrls: [
/^https:\/\/app2\.cashnote\.kr/,
/^https:\/\/app2-staging\.cashnote\.kr/,
],
environment: process.env.REACT_APP_DEPLOY_ENV,
release: process.env.REACT_APP_GIT_SHA,
});
[
"Failed to fetch",
"The operation couldn’t be completed. Software caused connection abort",
"작업을 완료할 수 없습니다. 소프트웨어에 의한 연결 중단",
"The network connection was lost.",
"네트워크 연결이 유실되었습니다.",
"WebKit encountered an internal error",
"The Internet connection appears to be offline.",
"Non-Error promise rejection captured with keys: currentTarget, isTrusted, target, type",
"취소됨",
"cannot parse response",
"Network request failed",
"Network Error",
"Loading chunk",
"Loading CSS chunk",
"Could not connect to the server",
"An SSL error has occurred",
"Java exception was raised",
"checkDomStatus"
]
#!/bin/sh
SENTRY_CLI=node_modules/@sentry/cli/sentry-cli
# this exits with code 1 when the given release does not exist
$SENTRY_CLI releases info $COMMIT_REF --quiet
if [ "$?" == "1" ]
then
$SENTRY_CLI releases new $COMMIT_REF
$SENTRY_CLI releases set-commits $COMMIT_REF --commit KoreaCreditData/kestrel@$COMMIT_REF
$SENTRY_CLI releases files $COMMIT_REF upload-sourcemaps --no-rewrite build/static/js
$SENTRY_CLI releases finalize $COMMIT_REF
fi
$SENTRY_CLI releases deploys $COMMIT_REF new -e $REACT_APP_DEPLOY_ENV -u $DEPLOY_PRIME_URL
활용하기
- 에러 처리의 과정을 러프하게 표현하면
- 에러 발생 인지
- 에러 빈도, 영향 범위 등을 기반으로 해결 우선순위 설정
- 에러 근본 원인 탐색
- 해결 후 재배포
- 여기서 "에러 발생 인지"를 위해 하는 것이 Alerts
- 일정 시간 안에 몇 명에게 and / or 몇 번 에러가 발생했는지를 기준으로, threshold를 넘으면 Slack으로 리포트하는 식
- 처음에는 수치를 좀 낮게 잡고, 서비스 성숙도(트래픽)에 따라 수치 조정해가는 게 좋음
- Sentry를 통해 모니터링할 수 있는 에러는 크게 두 종류
- uncaught exception
- 내 소스코드에 있을 수도 있고
- 써드파티에 있을 수도 있음 (추적 코드라든가, 앱 웹뷰이면 앱 네이티브가 호출한 무언가라든가)
- 직접 설정한 에러
- 유저가 이 경로를 경험할 수 없는데 왔다 → 경로 파악해서 제거
- 에러 재현하여 근본 원인 파악하기
- Sentry가 기본으로 넣어주는 여러가지 tag들이 있고, 이에 더해 릴리즈 버전과 커스텀 태그를 이용해서 정확한 '환경'을 우선 파악
- 그다음 breadcrumb을 관찰하여 유저가 어떤 경로의 행동을 거쳐 이 에러에 도달했는지 확인
- API 호출, 네비게이션, UI 요소 클릭 등이 기록되는데, 클라이언트 소스 코드가 익명화되어있거나 등의 이유로 UI 클릭을 봐도 뭘 했다는 건지 모를 수 있음
- Amplitude User Lookup이나 서버 로그 등의 도움을 더 받아야 할 수도 있음
- KCD 프론트엔드 팀에서 에러를 발견했을 때 처리하는 대략적인 방법
- (2주 단위로 운영업무 담당이 바뀜)
- 슬랙 채널에 리포트가 오면 원인 파악
- 무시해도 되는 에러인지, 스테이징 환경에서 테스트하다가 나온 것인지, 브라우저 특성 때문에 폴리필이 필요한지 등
- 컨텍스트 파악이 추가로 필요하면 서버/클라이언트 개발 리드들, 고인물들 핑
- 무시해도 되는 에러면 ignore_errors 에 추가
- 원인을 파악하기 위해 좀 더 패턴이 필요한 것이면 "n회 더 발생할때까지 ignore" 같은 걸 찍어둠
- 원인도 알고 해결해야 하는 것이면 PR 날림
- 영향 범위에 따라 즉시 해결할 것인지 나중에 볼 것인지 결정하긴 하나, 기본적으로 운영업무 담당은 메인 업무와 에러 탐색/해결을 7:3 정도의 비중으로 시간을 쓰는듯
- Some Tips
- 내가 이 에러를 인지하고 싶은가? 인지하면 고칠 수 있는가? 를 생각해야 함
- 예를 들어 서버 API로부터 500 에러가 떨어졌는데 이걸 클라이언트가 잘 핸들링하고 있다면, 서버 Sentry에는 에러가 뜨고 클라이언트에는 안 뜰 수도 있음. → 이 상황을 클라이언트에서도 에러로 판단해서 Sentry에 리포트할 것인가는 팀 안에서 판단해야 함
- 가장 자주 나오는 에러는 null/undefined reference 에러들 줄이는 방법
- 클라에서는 TypeScript 쓰면 거의 해결되고
- API 받아온 결과값이 예상과 다를 때에도 자주 발생하는데, OpenAPI Generator 따위를 통해 API 스키마를 TypeScript type 등으로 만들어놔서, 개발 단계에서 핸들링할 수 있도록 하면 좋음