Skip to content

Comments

feat(api,mobile): ADMIN 무제한 우회, Sentry 고도화, 알림 Lock dedup (#217)#218

Merged
dydals3440 merged 3 commits intomainfrom
feature/admin-bypass-sentry-enhance
Feb 24, 2026
Merged

feat(api,mobile): ADMIN 무제한 우회, Sentry 고도화, 알림 Lock dedup (#217)#218
dydals3440 merged 3 commits intomainfrom
feature/admin-bypass-sentry-enhance

Conversation

@dydals3440
Copy link
Contributor

📋 개요

ADMIN 역할 무제한 우회, Sentry 에러 모니터링 고도화, 알림 중복 방지 Lock 메커니즘 도입, 알림 라우팅 수정, 다크모드 UI 개선

🏷️ 변경 유형

  • feat - 새로운 기능 추가
  • 🔒 security - 보안 개선
  • 🐛 fix - 버그 수정

📦 영향 범위

  • apps/api - NestJS 백엔드
  • apps/mobile - Expo 모바일 앱

📝 변경 내용

API (apps/api)

ADMIN 역할 무제한 우회

  • Cheer/Nudge 서비스: ADMIN 역할은 구독 상태와 무관하게 일일 제한 없음
  • getUserSubscriptionInfo() 메서드 추가 (구독 상태 + 역할 동시 조회)
  • 캐시 CachedSubscriptionisAdmin 필드 추가

Sentry 고도화

  • GlobalExceptionFilter: withScope로 에러에 user/tags/extra 컨텍스트 추가
  • instrument.ts: beforeSend 콜백으로 민감 헤더(Authorization, Cookie, X-Forwarded-For) 및 token 파라미터 자동 제거
  • main.ts: Graceful shutdown 시 Sentry 버퍼 flush (SIGTERM/SIGINT)
  • SENTRY_TRACES_SAMPLE_RATE 환경변수 추가 (동적 샘플링 비율 설정)

알림 중복 방지 Lock

  • NotificationService: ILockProvider 기반 분산 잠금으로 dedup race condition 해결
  • 잠금 획득 실패 시 graceful skip (null 반환)
  • try/finally 패턴으로 잠금 해제 보장

Mobile (apps/mobile)

  • User 모델에 role 필드 추가, isPremiumUser에 ADMIN 체크
  • FOLLOW_ACCEPTED 알림 라우팅을 /feed로 변경
  • 프로필 편집 아이콘 다크모드 지원 (dark:bg-gray-8, dark:border-gray-6, dark:text-gray-4)

🧪 테스트

테스트 방법

pnpm test

테스트 결과

  • ADMIN 무제한 우회 테스트 (Cheer/Nudge)
  • Sentry withScope 컨텍스트 테스트
  • Lock 획득/해제/실패 테스트
  • Mobile UserPolicy ADMIN 테스트

✅ 체크리스트

작성자 확인

  • 코드가 프로젝트의 코딩 컨벤션을 따릅니다
  • pnpm check (Biome) 검사를 통과했습니다
  • 모든 테스트가 통과합니다 (pnpm test)
  • 커밋 메시지가 Conventional Commits 규칙을 따릅니다
  • 타입 체크를 통과했습니다 (pnpm typecheck)

🔗 관련 이슈

Closes #217

@dydals3440 dydals3440 added type: feat 🚀 신규 기능 - 사용자에게 새로운 비즈니스 가치를 제공하는 작업 scope: api 🌐 API - NestJS 서버 로직, DB 스키마 및 백엔드 인프라 관련 scope: mobile 📱 모바일 - Expo 기반 모바일 앱 UI/UX 및 클라이언트 로직 관련 status: needs review 🔍 리뷰 대기 - 작업 완료 후 병목 제거를 위해 팀원의 빠른 피드백이 필요한 상태 labels Feb 24, 2026
@dydals3440 dydals3440 self-assigned this Feb 24, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello @dydals3440, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 백엔드 API와 모바일 애플리케이션 전반에 걸쳐 중요한 기능 개선과 안정성 향상을 목표로 합니다. ADMIN 사용자를 위한 서비스 제한 우회 기능을 도입하여 관리 편의성을 높였고, Sentry 에러 모니터링을 고도화하여 에러 진단 및 디버깅 효율을 개선했습니다. 또한, 알림 시스템에 분산 잠금 기반의 중복 방지 메커니즘을 적용하여 알림의 정확성과 시스템 안정성을 확보했습니다. 마지막으로, 모바일 앱의 알림 라우팅을 조정하고 UI 요소를 다크 모드에 맞게 개선하여 사용자 경험을 향상시켰습니다.

Highlights

  • ADMIN 역할 무제한 우회: ADMIN 역할의 사용자는 Cheer 및 Nudge 서비스의 일일 제한에 구애받지 않도록 로직이 변경되었습니다. 구독 상태와 관계없이 무제한으로 서비스를 이용할 수 있습니다.
  • Sentry 에러 모니터링 고도화: Sentry 에러 보고 시 사용자 ID, HTTP 메서드, URL, 상태 코드 등 상세 컨텍스트 정보가 추가되었으며, 민감한 헤더(Authorization, Cookie, X-Forwarded-For)와 토큰 파라미터는 자동 필터링됩니다. 또한, 서비스 종료 시 Sentry 버퍼를 안전하게 비우는 Graceful Shutdown 로직이 추가되었습니다.
  • 알림 중복 방지 Lock 메커니즘 도입: 알림 서비스에 분산 잠금(Distributed Lock) 메커니즘이 도입되어 알림 중복 생성(deduplication) 시 발생할 수 있는 Race Condition을 해결했습니다. 잠금 획득 실패 시 알림 생성을 건너뛰고, 잠금은 try/finally 패턴으로 항상 해제되도록 보장합니다.
  • 모바일 알림 라우팅 및 UI 개선: 모바일 앱에서 'FOLLOW_ACCEPTED' 알림의 내부 라우팅이 /feed로 변경되었으며, 프로필 편집 아이콘에 다크 모드 지원을 위한 스타일이 추가되었습니다.
Changelog
  • .env.docker.prod.example
    • SENTRY_TRACES_SAMPLE_RATE 환경 변수 예시를 추가했습니다.
  • apps/api/src/common/cache/cache.service.ts
    • CachedSubscription 인터페이스에 isAdmin 필드를 추가했습니다.
  • apps/api/src/common/config/schemas/external.schema.ts
    • SENTRY_TRACES_SAMPLE_RATE 환경 변수 스키마를 추가했습니다.
  • apps/api/src/common/config/services/config.service.ts
    • sentryTracesSampleRate getter를 추가했습니다.
  • apps/api/src/common/exception/filters/global-exception.filter.spec.ts
    • Sentry.withScope 모킹을 추가했습니다.
    • 5xx 서버 에러 테스트 케이스에서 Sentry.withScope 호출을 검증하도록 업데이트했습니다.
    • 4xx 클라이언트 에러 테스트 케이스에서 Sentry.withScope가 호출되지 않음을 검증하도록 업데이트했습니다.
    • 알 수 없는 에러 테스트 케이스에서 Sentry.withScope가 호출되지 않음을 검증하도록 업데이트했습니다.
  • apps/api/src/common/exception/filters/global-exception.filter.ts
    • 5xx 에러 발생 시 Sentry.withScope를 사용하여 사용자 ID, HTTP 정보, 에러 코드 등의 컨텍스트를 추가하도록 로직을 변경했습니다.
  • apps/api/src/instrument.ts
    • Sentry 초기화 시 SENTRY_TRACES_SAMPLE_RATE 환경 변수를 사용하여 트레이스 샘플링 비율을 동적으로 설정하도록 변경했습니다.
    • Sentry beforeSend 훅을 추가하여 요청 헤더(Authorization, Cookie, X-Forwarded-For)에서 민감 정보를 제거하고, 쿼리스트링의 토큰 파라미터를 마스킹하도록 했습니다.
  • apps/api/src/main.ts
    • Sentry 모듈을 임포트했습니다.
    • Graceful shutdown 시 SIGTERM 및 SIGINT 시그널을 감지하여 Sentry 버퍼를 비우도록 로직을 추가했습니다.
  • apps/api/src/modules/cheer/cheer.repository.ts
    • 사용자의 구독 상태와 역할을 동시에 조회하는 getUserSubscriptionInfo 메서드를 추가했습니다.
  • apps/api/src/modules/cheer/cheer.service.spec.ts
    • Cheer 전송 테스트에서 ADMIN 역할 사용자가 구독 상태와 무관하게 일일 제한을 받지 않음을 검증하는 테스트 케이스를 추가했습니다.
    • getLimitInfo 테스트에서 getUserSubscriptionStatus 대신 getUserSubscriptionInfo를 사용하도록 업데이트했습니다.
    • ADMIN 역할 사용자가 무제한임을 검증하는 테스트 케이스를 추가했습니다.
    • 캐시 미스 시 ADMIN 정보를 DB에서 조회하여 캐싱하는 테스트 케이스를 추가했습니다.
    • 구독 상태가 없는 경우 FREE로 처리하는 테스트 케이스에서 getUserSubscriptionInfo를 사용하도록 업데이트했습니다.
    • 남은 횟수가 음수가 되지 않음을 검증하는 테스트 케이스에서 getUserSubscriptionInfo를 사용하도록 업데이트했습니다.
  • apps/api/src/modules/cheer/cheer.service.ts
    • sendCheer 메서드에서 ADMIN 역할을 확인하여 일일 제한을 우회하도록 로직을 추가했습니다.
    • getLimitInfo 메서드에서 캐시된 구독 정보에 isAdmin 필드를 활용하고, 캐시 미스 시 getUserSubscriptionInfo를 통해 ADMIN 역할을 조회하여 캐싱하도록 로직을 변경했습니다. 또한, ADMIN 역할 사용자는 일일 제한이 없도록 처리했습니다.
  • apps/api/src/modules/notification/notification.service.spec.ts
    • ILockProvider 인터페이스 및 LOCK_PROVIDER 토큰을 임포트했습니다.
    • lockProvider mock을 추가했습니다.
    • TestBed 설정에 LOCK_PROVIDER mock을 추가했습니다.
    • lockProvider의 acquire 메서드 기본 동작을 설정했습니다.
    • dedup 처리 시 잠금을 획득하고 해제하는 테스트 케이스를 추가했습니다.
    • 잠금 획득 실패 시 null을 반환하고 DB 조회를 하지 않음을 검증하는 테스트 케이스를 추가했습니다.
    • DB 조회 실패 시에도 잠금이 해제됨을 검증하는 테스트 케이스를 추가했습니다.
    • 전략이 없는 알림 타입은 잠금을 획득하지 않음을 검증하는 테스트 케이스를 추가했습니다.
  • apps/api/src/modules/notification/notification.service.ts
    • ILockProvider 인터페이스와 LOCK_PROVIDER 토큰을 임포트했습니다.
    • LOCK_PROVIDER를 주입받도록 생성자를 업데이트했습니다.
    • dedup 잠금의 TTL(Time To Live) 상수를 추가했습니다.
    • createAndSendWithDedup 메서드에 분산 잠금 로직을 적용하여 Race Condition을 방지하고, 잠금 획득 실패 시 null을 반환하도록 변경했습니다. 또한, try/finally 블록을 사용하여 잠금 해제를 보장했습니다.
    • 중복 방지 잠금 키를 생성하는 #buildDedupKey private 메서드를 추가했습니다.
  • apps/api/src/modules/nudge/nudge.repository.ts
    • 사용자의 구독 상태와 역할을 동시에 조회하는 getUserSubscriptionInfo 메서드를 추가했습니다.
  • apps/api/src/modules/nudge/nudge.service.spec.ts
    • ADMIN 역할 사용자가 구독 상태와 무관하게 무제한 Nudge를 보낼 수 있음을 검증하는 테스트 케이스를 추가했습니다.
    • getLimitInfo 테스트에서 getUserSubscriptionStatus 대신 getUserSubscriptionInfo를 사용하도록 업데이트했습니다.
    • ADMIN 역할 사용자가 무제한임을 검증하는 테스트 케이스를 추가했습니다.
    • EXPIRED 사용자 및 사용자가 없는 경우의 테스트 케이스에서 getUserSubscriptionInfo를 사용하도록 업데이트했습니다.
  • apps/api/src/modules/nudge/nudge.service.ts
    • sendNudge 메서드에서 ADMIN 역할을 확인하여 일일 제한을 우회하도록 로직을 추가했습니다.
    • getLimitInfo 메서드에서 getUserSubscriptionInfo를 통해 ADMIN 역할을 조회하고, ADMIN 역할 사용자는 일일 제한이 없도록 처리했습니다.
  • apps/mobile/src/features/notification/models/notification.model.ts
    • FOLLOW_ACCEPTED 알림의 내부 라우팅을 /feed로 변경했습니다.
  • apps/mobile/src/features/notification/presentations/utils/get-internal-route.ts
    • FOLLOW_ACCEPTED 알림의 내부 라우팅을 /feed로 변경했습니다.
  • apps/mobile/src/features/user/models/user.model.test.ts
    • createUser 헬퍼 함수에 role 필드를 추가했습니다.
    • ADMIN 역할 사용자가 구독 상태와 무관하게 프리미엄 사용자로 간주됨을 검증하는 테스트 케이스를 추가했습니다.
  • apps/mobile/src/features/user/models/user.model.ts
    • userSchema에 role 필드를 추가했습니다.
    • isPremiumUser 함수에 ADMIN 역할 체크 로직을 추가하여 ADMIN 사용자는 항상 프리미엄으로 간주되도록 변경했습니다.
  • apps/mobile/src/features/user/presentations/components/ProfileInfoCard.tsx
    • 프로필 편집 아이콘의 배경색, 테두리, 텍스트 색상에 다크 모드 스타일을 추가했습니다.
  • apps/mobile/src/features/user/services/user.mapper.ts
    • toUser 매퍼 함수에 role 필드를 추가했습니다.
Activity
  • 이 Pull Request는 아직 인간 활동이 없습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이번 PR은 ADMIN 역할에 대한 일일 제한 우회 로직 추가, Sentry 에러 모니터링 고도화, 그리고 알림 중복 방지를 위한 분산 락 메커니즘 도입을 포함하고 있습니다. 전반적으로 코드의 의도가 명확하고 안정적으로 구현되었습니다. 다만, Sentry 샘플링 비율 설정 시 0 값이 무시되는 버그와, 서비스 간 캐시 사용의 불일치 사항이 발견되어 수정을 권장합니다.

Comment on lines 7 to 9
tracesSampleRate:
Number(process.env.SENTRY_TRACES_SAMPLE_RATE) ||
(process.env.NODE_ENV === "production" ? 0.2 : 1.0),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

환경변수 SENTRY_TRACES_SAMPLE_RATE0으로 설정된 경우, JS의 falsy 체크(||)로 인해 기본값(0.2 또는 1.0)으로 덮어씌워지는 버그가 있습니다. 샘플링을 완전히 끄고 싶은 경우(0)를 위해 환경변수 존재 여부를 먼저 확인하도록 수정해야 합니다.

Suggested change
tracesSampleRate:
Number(process.env.SENTRY_TRACES_SAMPLE_RATE) ||
(process.env.NODE_ENV === "production" ? 0.2 : 1.0),
tracesSampleRate:
process.env.SENTRY_TRACES_SAMPLE_RATE
? Number(process.env.SENTRY_TRACES_SAMPLE_RATE)
: (process.env.NODE_ENV === "production" ? 0.2 : 1.0),

Comment on lines 326 to 327
const userInfo = await this.nudgeRepository.getUserSubscriptionInfo(userId);
const isAdmin = userInfo?.role === "ADMIN";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

CheerServicegetLimitInfo 구현과 달리, NudgeService에서는 구독 정보 및 ADMIN 여부 조회 시 캐시(CacheService)를 사용하지 않고 매번 DB를 조회하고 있습니다. 시스템 전반의 일관성과 성능 최적화를 위해 CacheService를 도입하여 캐싱 로직을 추가하는 것을 권장합니다.

@dydals3440 dydals3440 requested a review from hijjoy February 24, 2026 15:43
…일관성 통일

- EntitlementService(@global) 생성: CHEER/NUDGE/AI_PARSE 기능별 제한을 역할/구독 상태 기반으로 통합 관리
- AI Parse: 고정 dailyLimit → EntitlementService 위임 (ADMIN/ACTIVE 무제한, FREE/EXPIRED/CANCELLED 5회)
- Cheer/Nudge: 리포지토리 구독 조회 제거, EntitlementService로 위임
- AiService/EmailService spec: Test.createTestingModule → Suites TestBed.solitary 패턴 통일
- validators: AI_PARSE_LIMITS 상수 추가, UsageInfo.limit nullable 지원
@dydals3440 dydals3440 merged commit 33b575f into main Feb 24, 2026
2 of 3 checks passed
@dydals3440 dydals3440 deleted the feature/admin-bypass-sentry-enhance branch February 24, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: api 🌐 API - NestJS 서버 로직, DB 스키마 및 백엔드 인프라 관련 scope: mobile 📱 모바일 - Expo 기반 모바일 앱 UI/UX 및 클라이언트 로직 관련 status: needs review 🔍 리뷰 대기 - 작업 완료 후 병목 제거를 위해 팀원의 빠른 피드백이 필요한 상태 type: feat 🚀 신규 기능 - 사용자에게 새로운 비즈니스 가치를 제공하는 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: ADMIN 무제한 우회, Sentry 고도화, 알림 Lock 기반 dedup

1 participant