Files
ttipo b77f95fdff docs: 앱 새 메일 알림 · 앱 설정을 계약 · 스킬 · 규칙에 적는다
계약 1절 끝의 옛 메모(/api/push-devices · { minVersion, latestVersion, storeUrl })를 확정한 경로 · 모양 · 규칙으로 바꾸고
(N · 간격 · 따라잡기 · 엔진 after 의 초 경계 · "알림이 멈췄습니다"를 보내는 까닭 표 · 실패 처리 · 실기기로 확인할 것),
"클라이언트 — 앱"에 앱이 실제로 부르는 때를 적었다. 2절 실시간 알림의 붙잡기와 6절 퇴사 마무리 목록도 더했다.
nestjs · docker 스킬과 env · 보안 · 로그 규칙에 FCM 키 자리 · 바깥 연결 · 가릴 값을 더했다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:51:58 +09:00

4.8 KiB
Raw Permalink Blame History

로깅 규칙

console.log 사용 금지. 백엔드는 winston(shared/logger/winston.config.ts), Nest 내부는 Logger 를 쓴다.

레벨 기준

레벨 쓰는 상황 예
error 서버 책임 오류(5xx), 즉시 조치가 필요한 실패 미정의 예외, DB 커넥션 실패
warn 사용자 책임 실패(4xx), 예상된 비정상 검증 실패, 권한 없음, Rate Limit 초과
log(info) 상태 변화가 있는 주요 이벤트 서버 기동, 가입 완료, 결제 승인
debug 개발 환경 진단 외부 API 요청/응답 요약
  • 4xx 를 error 로 남기지 않는다 — 알림 피로도가 올라가 진짜 장애를 놓친다.
  • 반복되는 정상 흐름(헬스체크, 폴링)은 로그를 남기지 않는다.

필수 포함 정보

모든 에러 로그에는 에러 코드와 추적 ID를 함께 남긴다.

[RES_001][3f1c0a2e-0f1b-4f2a-9a1e-6c9f0a1b2c3d] GET /api/users/1 {"userId":1}
  • 추적 ID는 RequestIdMiddleware 가 발급하는 x-request-id 이며, 실패 응답의 error.traceId 와 같은 값이다.
  • 사용자 문의가 들어오면 이 값으로 로그를 검색한다 (error-codes).
  • 개발자용 컨텍스트는 BusinessException 의 context 에 담는다 — 로그에만 남고 응답에는 포함되지 않는다.

민감정보 마스킹 (필수)

아래 값은 어떤 레벨에서도 원문 그대로 남기지 않는다.

  • 비밀번호, 토큰(JWT·리프레시·API 키), 쿠키, Authorization 헤더
  • 앱 푸시(앱 1단계 — FCM)의 휴대폰 주소(FID — 지문도 남기지 않는다: 이어 붙여 기기를 좇지 못하게) · 서비스 계정 키 · 개인 키 · 서비스 계정 JWT · Google access token · FCM · Google 의 오류 설명 글(상태 · 오류 코드만 남긴다)
  • 주민등록번호·카드번호·계좌번호 등 개인 식별/금융 정보
  • 요청 본문 전체 덤프 (JSON.stringify(req.body) 금지 — 필요한 필드만 선택해 남긴다)
  • DB 쿼리 로그(매개변수) — 개발 환경도 남기지 않는다. 메일 글 · 제목 · 주소가 DB 에 있다(14단계 검색 색인). TypeORM logging 은 개발에서 오류 · 경고 · 마이그레이션만, 운영 · 시험은 끈다 (shared/config/database.config.ts 의 databaseLogging)
  • 사용자가 친 검색어(쿼리의 q — 메일 검색 · 받는 사람 추천) — 요청 URL 을 남길 때는 shared/utils/mask.ts 의 maskUrlForLog() 를 거친다(q · 조건 검색의 from · to · subject · body(14단계) · cursor 는 길이만 — 목록 커서에는 검색어의 지문이 들어 있어 후보 낱말을 대입해 볼 수 있다). 앞단 nginx 도 접근 로그에 쿼리를 남기지 않는다 — /api 와 화면 요청 모두(메일 검색은 화면 주소 /mail/search?q=… 에도 검색어가 들어간다, docker). 예외: nginx 오류 로그(413 · 502 · 504 등)는 장애 원인 줄을 잃지 않게 요청 줄 · referrer 를 쿼리째 남긴다 — 오류 로그를 볼 수 있는 사람을 운영 담당으로 한정한다
// ❌ 금지
this.logger.log(`로그인 시도: ${JSON.stringify(dto)}`);

// ✅ 필요한 식별자만
this.logger.log(`로그인 시도: email=${maskEmail(dto.email)}`);
  • 마스킹 유틸은 순수 함수로 shared/utils/ 에 둔다 (coding-common).

  • 메일 프로그램 자동 설정(autoconfig. · autodiscover. — 13단계)의 요청 주소에는 직원 주소가 쿼리로 실린다(?emailaddress=) — 앞단 nginx 는 쿼리 없이 남기고(위 규칙 그대로), 외부 프록시의 그 Proxy Host 는 접근 로그를 끈다 (docker).

클라이언트 로깅

  • 프론트/앱에서 사용자 정보를 콘솔에 출력하지 않는다. 운영 빌드에는 디버그 로그를 남기지 않는다.
  • 사용자 노출 문구는 로그가 아니라 에러 사전에서 온다 (error-codes).

운영 확장

  • 운영 환경은 JSON 포맷 + 외부 수집기(Loki/ELK/CloudWatch) 연동을 권장한다.
  • 파일로 남길 경우 로테이션(winston-daily-rotate-file)을 적용하고 보존 기간을 정한다.
  • 컨테이너 로그 보존(13단계): compose 의 모든 서비스에 json-file 회전을 명시한다(x- 앵커 — 기본 10m×5, mail · frontend · backend 는 20m×10, 압축 — 운영에서 mail 2주 · frontend 1주쯤). 새 서비스에도 앵커를 붙인다(CI 가 서비스 수와 logging 수를 견준다). 더 오래 남기려면 위 수집기로.
  • 운영 도구 실행 기록(ops_audit_log — 13단계): 언제 · 누가(운영자 이름) · 명령 · 대상 · 결과. 대상 주소는 감사용이라 원문으로 둔다(DB 백업에 든다). 비밀번호 · 임시 비밀번호는 남기지 않는다.

보안 규칙은 security, 테스트 규칙은 testing 참조.