Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
a3c9a37
#250 [Fix] 투표 비율 계산 기준을 preVoteOption에서 postVoteOption으로 수정 (#252)
HYH0804 May 27, 2026
a38c141
#254 [Refactor] 관점 등록/수정 시 GPT 검수 코드 제거 (#256)
HYH0804 Jun 3, 2026
cee2c5d
#255 [Refactor] VoteStatsResponse OptionStat에 title 필드 추가 (#257)
HYH0804 Jun 3, 2026
53533b8
#258 [Feat] 출석체크 API 추가
si-zero Jun 3, 2026
77db7a1
#260 [Feat] 스웨거 어드민/유저 분리
si-zero Jun 4, 2026
a9d4293
[Feat] 알림기능 (#263)
HYH0804 Jun 11, 2026
b5a681b
#262 [Fix] APNs 자격증명 S3 다운로드 시 임시파일 충돌 오류 수정
HYH0804 Jun 11, 2026
d281fa3
#264 [Fix] AdMob SSV 포인트 미적립 버그 수정
si-zero Jun 11, 2026
968d6a3
#262 merge (#266)
HYH0804 Jun 14, 2026
d041631
[Chore] adMob 크롤링 파일 설정: adMob 크롤링 파일 설정 #269
si-zero Jun 16, 2026
ec25882
#270 [Feat] 설정 파일 추가
si-zero Jun 16, 2026
4c45999
[Chore] JWT필터 설정 #271
si-zero Jun 16, 2026
e0c9c1c
#275 [Fix] AdMob SSV 콜백 파라미터 optional 처리 및 서명 검증 활성화
si-zero Jun 18, 2026
ab2cb78
#277 [Fix] AdMob SSV 콜백 URL 검증 요청 처리 실패
si-zero Jun 18, 2026
04d947c
#279 [Fix] AdMob SSV 엔드포인트 검증 실패 시 항상 200 반환
si-zero Jun 18, 2026
d9ea3b5
#283 [Fix] /me/content-activities voteSide null 반환 버그
si-zero Jun 19, 2026
ec27b01
#285 [Feat] content-activities 응답에 option_title 필드 추가
si-zero Jun 20, 2026
6663e7a
#287 [Fix] AdMob SSV 서명 검증 오류로 인한 포인트 미지급 버그
si-zero Jun 24, 2026
39aca84
#289 [Fix] 오늘의 배틀 개수 변경
si-zero Jul 5, 2026
11e2da1
#293 [Fix] 사전투표 철학자 아이콘 미노출
si-zero Jul 5, 2026
aa449f0
#297 [Fix] 댓글 작성 시각이 9시간 전으로 표시되는 버그
si-zero Jul 6, 2026
3259a1a
#301 [Fix] robots.txt 파일 부재로 AdMob app-ads.txt 크롤링 차단
si-zero Jul 6, 2026
e91819b
#305 [Fix] S3 오디오 조각 재다운로드 시 FileAlreadyExistsException으로 TTS 파이프라인 실패
si-zero Jul 6, 2026
d64d5ca
#309 [Fix] JwtFilter 자체 화이트리스트에 /robots.txt 누락으로 401 발생
si-zero Jul 6, 2026
ff16d85
#316 [Fix] 포인트 정책표 대비 코드 불일치 4건 수정
si-zero Jul 7, 2026
f50c749
Merge branch 'main' into dev
si-zero Jul 7, 2026
d9e28a1
#321 [Feat] 알림 미읽음 여부 확인 API 추가
si-zero Jul 7, 2026
4279341
#313 [Hotfix] Railway 배포 환경 FFmpeg 자동 설치 적용 (#320)
jucheonsu Jul 7, 2026
f77048c
#327 [Fix] 알림 전체 읽음 처리 후 모두 읽음 신호가 전달되지 않던 문제 수정
si-zero Jul 13, 2026
99886f9
#331 [Docs] 출석체크 API 명세서를 실제 코드에 맞춰 갱신
si-zero Jul 13, 2026
087b8c0
#333 [Fix] app-ads.txt가 text/html로 응답되어 AdMob 크롤러가 무시하는 문제 수정
si-zero Jul 13, 2026
d33ff1f
#337 [Feat] 인앱 알림 테스트용 API 추가
si-zero Jul 14, 2026
17f35a4
#341 [Fix] 미문서화/중복 포인트 지급 로직 정리
si-zero Jul 15, 2026
acbe078
관리자 페이지를 새 admin-web(Next.js) 앱으로 리다이렉트 (#346)
Jul 15, 2026
de75bdf
#349 [Feat] admin.web.url env 주입 + 기본값 picke.up.railway.app (#350)
Jul 15, 2026
9493351
#352 [Fix] JwtFilter 화이트리스트 /api/v1/auth 전체 매칭으로 logout 인증 우회, 500 에러 발생
si-zero Jul 16, 2026
00bfcb3
#356 [Feat] 이용약관/개인정보처리방침 페이지 백엔드 서빙
si-zero Jul 20, 2026
99c936d
#361 [Feat] 관리자용 테스트 계정 로컬 로그인 기능 추가
si-zero Jul 24, 2026
f0a837d
#360 [Refactor] 1인 1관점 제한을 해제하고 1인 다수 관점 등록 허용
si-zero Jul 24, 2026
9fb601f
#366 [Feat] 내 콘텐츠 활동 내역(댓글 탭)에 내가 작성한 관점 포함
si-zero Jul 24, 2026
6a1abde
#370 [Fix] 사전투표만 한 기록도 내 배틀 기록에 노출되는 버그
si-zero Jul 24, 2026
80f5b9c
[Feat] 관점 200자 제한 해제 (#375)
si-zero Aug 2, 2026
f9bc278
#378 [Feat] 관리자 예약 푸시알림(제목/소제목/발송시간) 설정 및 자동발송
si-zero Aug 8, 2026
99f4765
#382 [Fix] 내 콘텐츠 활동 댓글 정보 매핑 오류 수정 (#383)
jucheonsu Aug 9, 2026
79cb355
#387 [Refactor] 철학자 태그 제한 및 가치관 육각형 계산 보정 (#388)
jucheonsu Aug 13, 2026
bdbe3b3
#391 [Remove] 미사용 파일 정리 (비활성 CI 워크플로우 + 레거시 admin 프론트)
si-zero Aug 13, 2026
a82c575
#392 [CI/CD] Java CI 파이프라인 브랜치명 오류 및 지속적 빌드 실패 수정
si-zero Aug 13, 2026
70ccc90
#395 [Feat] 관리자용 푸시 알림 발송 테스트 API 추가
si-zero Aug 16, 2026
2e72bf7
#396 [Fix] Android FCM 푸시에 title/body가 실리지 않아 알림이 안 뜨던 문제 수정
si-zero Aug 16, 2026
1fc8488
#401 [Fix] FCM 만료된 디바이스 토큰이 정리되지 않아 반복 발송 실패 로그가 쌓이는 문제 수정
si-zero Aug 17, 2026
a4d3b31
#405 [Feat] 어드민 예약 알림 On/Off 전환 API 추가 및 관리자 알림 API 문서화
si-zero Aug 26, 2026
0b9a374
#407 [Feat] 어드민 유저 검색 API 추가
si-zero Aug 26, 2026
869de77
#409 [Feat] 어드민 공지사항 수정/삭제 API 추가
si-zero Aug 26, 2026
087fea5
#411 [Feat] 어드민 공지 발송 결과 조회 API 추가
si-zero Aug 26, 2026
4bdbdf3
#413 [Feat] 어드민 공지 발송 대상자 수 미리보기 API 추가
si-zero Aug 26, 2026
76e8097
#414 [Feat] 어드민 공지 작성 옵션(카테고리) 조회 API 추가
si-zero Aug 26, 2026
26116e8
#415 [Feat] 어드민 예약 알림 테스트 발송 API 추가
si-zero Aug 26, 2026
4234c29
#423 [Docs] 누락된 마이그레이션 SQL 파일 보완 (#409, #411)
si-zero Aug 30, 2026
7aaa1f4
#421 [Feat] 유저 일별 활동 트래킹(DAU/MAU 기반) 인프라 구축
si-zero Aug 30, 2026
d3b9343
#425 [Feat] 어드민 대시보드 오늘 요약 API 추가
si-zero Sep 1, 2026
2129d35
#427 [Feat] 어드민 대시보드 DAU/MAU 추이 API 추가
si-zero Sep 1, 2026
d8fe672
#429 [Feat] 어드민 대시보드 신규 가입자 추이 API 추가
si-zero Sep 1, 2026
6eea0d4
#431 [Feat] 어드민 대시보드 배틀 참여율 API 추가
si-zero Sep 1, 2026
1e134bc
#433 [Feat] 어드민 대시보드 출석 체크율 API 추가
si-zero Sep 1, 2026
cba91cb
#436 [Feat] 어드민 대시보드 크레딧 지급 현황 API 추가
si-zero Sep 2, 2026
23496d1
#444 [Refactor] AWS S3를 Railway Bucket으로 마이그레이션 (개발 서버)
si-zero Sep 7, 2026
7b0b68f
🐛 Fix: BattleVoteServiceImplTest 시간대 불일치로 15시(UTC) 이후 CI 실패 (#442)
Roy-wonji Sep 7, 2026
c99065e
✨ Feat: ad.picke.store 제휴 광고(쿠팡 파트너스·애드픽) 연동 (#443)
Roy-wonji Sep 8, 2026
d22f21a
#441 [Fix] 쿠팡 파트너스 ID 환경변수 누락 시 앱 부팅 실패 해소 (#448)
Roy-wonji Sep 8, 2026
9745f2e
#441 [Fix] 광고 스웨거 기본 서버를 문서를 연 주소로 바꿈 (#449)
Roy-wonji Sep 8, 2026
e5a0422
#441 [Fix] 필수 쿼리 파라미터 누락 시 500 대신 400 응답 (#450)
Roy-wonji Sep 8, 2026
c3bd52b
#441 [Feat] 광고 스웨거 탭에 운영 광고 도메인 추가 (#451)
Roy-wonji Sep 8, 2026
476b0dd
#441 [Feat] 애드픽 쇼핑·핫딜 상품을 광고 재고로 편입 (#454)
Roy-wonji Sep 8, 2026
495f772
#441 [Feat] 애드픽 쇼핑 상품에 더 높은 로테이션 가중치 부여 (#457)
Roy-wonji Sep 9, 2026
093278c
AdFit 수익과 비용을 기록해 관리자 ROI를 확인하도록 지원
Roy-wonji Sep 10, 2026
73a4214
test: 사용자 광고 API 조회 계약 검증 (#460)
Roy-wonji Sep 10, 2026
27e7b79
fix: 사용자 광고에서 설치·가입 캠페인 제외 (#463)
Roy-wonji Sep 10, 2026
a612828
feat: AdFit 일별 수익 자동 조회 연결 (#465)
Roy-wonji Sep 10, 2026
6891d33
#455 [Feat] 배틀 시나리오 감정 톤 + 철학자 보이스 매핑 인프라
si-zero Sep 10, 2026
291918c
#467 [Feat] 배틀 대본 붙여넣기 파서 (배틀·시나리오 자동 등록)
si-zero Sep 11, 2026
3ed96b8
#469 [Feat] 배틀 대본 파서 대사를 문장 종결부호 단위로 분리
si-zero Sep 11, 2026
8efa2ad
#471 [Feat] 배틀 대본 파서 summary·description·선택지 title 매핑 수정
si-zero Sep 11, 2026
073dfd5
#473 [Feat] 시나리오 선택지에 선택의 시간 설명 텍스트(title) 추가
si-zero Sep 11, 2026
58ce7ad
#475 [Feat] 철학자 보이스 매핑에 이미지 키(image_key) 추가
si-zero Sep 12, 2026
759f746
#477 [Docs] 철학자 보이스 매핑에 실제 이미지 키 반영
si-zero Sep 12, 2026
7a41a14
#479 [Fix] 철학자 보이스 imageKey 응답을 호출 가능한 경로로 변환
si-zero Sep 12, 2026
4cf5227
#481 [Fix] 배틀 옵션 태그 수정 시 유니크 제약 위반(23505) 발생
si-zero Sep 12, 2026
a7b0c3b
#483 [Fix] 로컬 draft→S3 promote가 DB 트랜잭션과 분리되어 orphan 파일/유실 위험
si-zero Sep 12, 2026
e3e5bd8
feat: 애드픽 쇼핑 재고 확대 + AdFit 세션 갱신 + Mixpanel·Sentry 지표 (#487)
Roy-wonji Sep 14, 2026
d6566c3
개발 서버도 운영과 같은 관리자 지표 계약을 제공하게 한다
Roy-wonji Sep 14, 2026
9ef0aa0
feat: Sentry 전체 관측 데이터와 가입 추이 개발 반영 (#492)
Roy-wonji Sep 14, 2026
df9acca
[Fix] Sentry trace metric 시계열 집계 오류 수정 (#494)
Roy-wonji Sep 14, 2026
88661ac
feat: Sentry 회원가입 지표를 일별 시계열로 제공 (#496)
Roy-wonji Sep 14, 2026
1e01dde
feat: Sentry 사용자 이벤트 전체 집계 (#498)
Roy-wonji Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
64 changes: 0 additions & 64 deletions .github/workflows/deploy-dev.yml.disabled

This file was deleted.

66 changes: 0 additions & 66 deletions .github/workflows/deploy-main.yml.disabled

This file was deleted.

7 changes: 5 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Java CI for Picke

on:
push:
branches: [ "develop", "main" ]
branches: [ "dev", "main" ]
pull_request:
branches: [ "develop", "main" ]
branches: [ "dev", "main" ]

jobs:
build:
Expand All @@ -21,6 +21,9 @@ jobs:
distribution: 'temurin'
cache: gradle # 1. 빌드 속도를 획기적으로 줄이기 위해 그래들 캐시 적용

- name: Install ffmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg

- name: Grant execute permission for gradlew
run: chmod +x gradlew

Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ out/
.env
postgres_data/
src/main/resources/application-local.yml
.claude
.claude

### macOS ###
.DS_Store
**/.DS_Store
29 changes: 29 additions & 0 deletions docs/api-specs/adfit-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 관리자 AdFit 광고와 ROI

- `GET /api/v1/admin/adfit?from=YYYY-MM-DD&to=YYYY-MM-DD`: ADMIN 전용. 최대 366일.
- `PUT /api/v1/admin/adfit/daily`: ADMIN 전용. `{date, unit, revenue, cost, costBasis}`.
- `GET /api/v1/admin/adfit/session-cookie`: ADMIN 전용. `{configured, source, updatedAt}`. 쿠키 값은 응답에 담지 않는다.
- `PUT /api/v1/admin/adfit/session-cookie`: ADMIN 전용. `{cookie}`. AdFit 콘솔에 로그인한 브라우저의 Cookie 헤더 전체를 넣는다.
- `GET` 응답에는 기존 수동 입력 단위 리포트(`source`, `units`, `days`)와 별도로 `account`가 포함된다.
- `account.status`: `NOT_CONFIGURED`, `CONNECTED`, `RECONNECT_REQUIRED`, `UNAVAILABLE`.
- `account.days[]`: `{date, revenue, ctr, ecpm, fillRate, winFillRate}`. AdFit 콘솔에 값이 없거나 누락된 날짜는 `null`이며 0으로 대체하지 않는다.
- `account.fetchedAt`: `CONNECTED`일 때 이번 관리자 조회에서 AdFit 응답을 성공적으로 파싱한 시각이다. 원천 데이터의 최종 집계 시각이나 배치 동기화 시각이 아니다.
- `account.revenue`: 조회 기간 중 실제 내려온 일별 수익 합계. 수익 데이터가 전부 `null`이면 `null`.
- `account.cost`, `account.roi`: AdFit 계정 자동 보고서가 광고 비용을 제공하지 않으므로 항상 `null`.
- 자동 보고서는 세션 쿠키가 있을 때 AdFit 콘솔 계정 종합 일별 API를 조회한다. 쿠키는 매 조회 시점에 다시 읽는다.
- 쿠키 우선순위: 관리자 화면 입력값(`adfit_session_cookies` 테이블, `source=ADMIN_CONSOLE`) > 환경변수 `ADFIT_SESSION_COOKIE`·`picke.adfit.session-cookie`(`source=ENVIRONMENT`). 둘 다 없으면 `source=NONE`.
- 세션이 만료되면 `RECONNECT_REQUIRED`가 된다. 재배포 없이 `PUT /session-cookie`로 새 쿠키를 넣어 복구한다.
- 세션 쿠키가 없으면 `NOT_CONFIGURED`, 로그인 만료·리다이렉트·HTML 로그인 응답이면 `RECONNECT_REQUIRED`, API 장애·스키마 불일치면 `UNAVAILABLE`.
- `unit`: NATIVE_WIDE(홈·큐레이션·마이페이지 공유), BANNER(탐색), APP_TRANSITION(앱 시작).
- `costBasis`: AD_OPERATIONS(광고 운영비), ACQUISITION(유입 광고비), SERVICE_OPERATIONS(서비스 운영비).
- KRW 금액, 소수 둘째 자리까지, 음수 불가. 같은 날짜·단위는 수정. 미래 날짜 입력 불가.
- source=MANUAL_CONSOLE: `units`와 `days`는 수동 입력 데이터다. 자동 계정 수익은 `account`만 사용한다.
- 광고 단위는 로컬 Picke-iOS의 SDK 연결 기준이다. 개별 사용자에게 SDK가 선택한 이미지·광고주 소재를 재현하거나 실시간 노출을 보증하지 않는다.
- 같은 단위를 여러 화면에서 사용하더라도 수익은 한 번만 합산한다. 비용도 단위별 배분액으로 입력하며 전체 운영비를 각 단위에 중복 입력하지 않는다.
- 수익과 비용은 입력된 날짜들의 합계다. reportedDays/expectedDays로 부분 입력을 표시한다. 미입력은 null이며 0원이 아니다.
- ROI = (수익 - 비용) / 비용 × 100. 모든 날짜가 입력되고 같은 비용 기준이며 비용이 양수일 때만 계산한다. 그 외 null.
- 수익 0, 비용 양수인 정상 입력은 ROI -100%다. 미입력과 구분한다.
- AdFit 계정 자동 보고서는 콘솔 내부 API(`accountTotal/periodicIndicators`)를 사용한다. 공개 파트너 REST API가 아니므로 세션 만료 시 재연결이 필요하다.
- 공식 참고: https://adfit.kakao.com/ , https://adfit.github.io/
- 카카오 REST API 키(`Authorization: KakaoAK ...`)로는 조회할 수 없다. 애드핏 매체주 수익용 공개 REST API가 없다. 카카오 디벨로퍼스 REST API 레퍼런스에 해당 엔드포인트가 없고, AdX Report API는 RTB 연동 DSP 전용, 카카오모먼트 리포트 API는 광고주 집행 측이다.
- DB: `docs/db/20260910_create_adfit_daily_reports.sql`, `docs/db/20260913_create_adfit_session_cookies.sql`. 현재 프로젝트는 Hibernate ddl-auto=update를 사용한다.
65 changes: 65 additions & 0 deletions docs/api-specs/admin-analytics-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 관리자 지표 (Mixpanel · Sentry)

- `GET /api/v1/admin/analytics/mixpanel?from=YYYY-MM-DD&to=YYYY-MM-DD&events=A,B`: ADMIN 전용. 최대 366일.
- `GET /api/v1/admin/analytics/sentry?from=YYYY-MM-DD&to=YYYY-MM-DD`: ADMIN 전용. 최대 366일.
- 두 응답의 `status`: `NOT_CONFIGURED`, `CONNECTED`, `UNAVAILABLE`. 조회 실패를 0으로 대체하지 않는다.
- `NOT_CONFIGURED`는 오류가 아니다. 토큰을 넣기 전 상태이므로 화면은 지면을 비우고 안내만 띄운다.

## Mixpanel

집계 API가 아니라 **원본 이벤트를 내려받아 서버가 직접 센다.**

- `GET https://data.mixpanel.com/api/2.0/export?from_date&to_date`. 응답은 한 줄에 이벤트 하나인 NDJSON.
- 인증은 프로젝트 API 비밀을 Basic 사용자명 자리에 넣고 비밀번호를 비우는 레거시 방식이다. 이 방식에 `project_id`를 넣으면 400이 된다. 비밀이 이미 프로젝트를 특정한다.
- `events[].days[]`: `{date, count}`. 최신 날짜부터. 원본을 전부 받아 세므로 이벤트가 없던 날짜는 미집계가 아니라 **0**이다.
- `signUpDays[]`: 선택 기간 전체의 `sign_up` 일별 가입 수. 이벤트 선택값과 무관하게 집계하며 날짜 오름차순이다.
- `events`를 비우면 기간에 나타난 이벤트 전부를 발생 수 내림차순으로 준다. 이벤트 이름을 미리 설정해 둘 필요가 없다.
- 원본을 전부 받으므로 기간은 **31일까지**다(`MixpanelClient.MAX_DAYS`). 4일치가 약 2MB다.
- `properties.time`은 프로젝트 타임존 기준 epoch 초이고 `from_date`·`to_date` 경계도 같은 타임존을 따른다. 둘을 같은 타임존으로 묶어야 Mixpanel 화면 숫자와 맞는다. `picke.analytics.mixpanel.project-zone`(기본 `UTC`)로 맞춘다. 이 프로젝트는 UTC로 실측 확인했다.
- 경계 하루가 타임존 차이로 걸쳐 들어올 수 있어 요청 기간 밖 이벤트는 버린다.

### 왜 집계 API를 안 쓰는가

- **현재 Picke의 Mixpanel 플랜은 Query API를 허용하지 않는다.** 2026-09-14 실측: `/api/query/segmentation`·`/api/query/insights` 모두 `HTTP 402 Your plan does not allow API calls`. 인증은 통과하므로 자격 문제가 아니다.
- 같은 플랜에서 Raw Export는 **200으로 열려 있다.** 그래서 이쪽으로 붙였다. Mixpanel MCP나 다른 클라이언트를 붙여도 Query API를 호출하는 한 같은 402를 받는다.
- 서비스 계정 방식(`username:secret`)은 이 프로젝트에서 401이다. 프로젝트 토큰(`project_token`)은 이벤트 수집용이라 조회 인증에 쓰이지 않는다(401).
- 실측(2026-09-14, 09-10~09-13): 16종 이벤트, `screen_view` 644 · `network_request` 561 · `ui_action` 172 · `onboarding_step` 65 · `battle_step` 44 · `sign_up` 3 등.

### 환경변수

- `MIXPANEL_API_SECRET` 하나면 된다. 설정 키는 `picke.analytics.mixpanel.*`.
- EU·인도 데이터 거주 프로젝트는 호스트가 다르다. `picke.analytics.mixpanel.base-url`로 바꾼다.
- `ad_click` 이벤트에 `unit`·`placement`·`format` 속성이 붙어 온다. AdFit 클릭은 `unit` 없이 `format=popup`·`placement=app_start`로 들어온다. 광고 클릭을 매체별로 나누려면 이 속성을 쓴다.

## Sentry 사용자 이벤트

- iOS 분석 모듈은 Mixpanel에 보내는 모든 이벤트를 Sentry에도 info 이벤트로 전송하고 `analytics_event` 태그를 붙인다.
- `projects[].analyticsEvents.events[]`는 이 태그를 기준으로 이벤트명·기간 발생 수·고유 사용자·최초/마지막 발생·일별 발생 수/고유 사용자를 제공한다.
- 이벤트 이름은 서버에 하드코딩하지 않는다. 선택 기간에 실제 수집된 태그 값을 전부 조회하므로 `sign_up`, `ui_action` 등 새 액션도 자동으로 노출된다.
- Sentry Explore의 한 요청당 상위 시계열 10개 제한 때문에 이벤트명을 먼저 조회한 뒤 10개씩 나누어 일별 추이를 조회한다.
- `analyticsEvents.status`가 `UNAVAILABLE`이어도 오류·세션·릴리즈 등 다른 Sentry 데이터는 가능한 범위에서 유지한다.

## Sentry 오류

- `GET /api/0/projects/{org}/{project}/issues/?query=is:unresolved&sort=freq`. 조직 인증 토큰 `Authorization: Bearer`.
- 절대 기간을 쓰려면 `statsPeriod`를 빈 값으로 함께 보낸다. 생략하면 Sentry 기본 기간이 적용된다.
- iOS·Android를 각각 호출해 `projects[]`로 나눠 준다.
- 한 프로젝트가 막혀도 다른 프로젝트는 살린다. 대신 최상위 `status`는 `UNAVAILABLE`, 최상위 `totalEvents`는 `null`이다. 일부만 더한 값을 전체 합계처럼 보여주지 않는다.
- `projects[].totalEvents`는 stats API의 선택 기간 전체 수신 이벤트 합계다. `issues[]`는 미해결 상위 20건이다.
- `sort=freq`는 절대 기간에서 이벤트 수 내림차순을 보장하지 않는다(실측 확인). 응답 순서를 믿지 않고 서버가 다시 정렬한다.
- `issues[].events`는 Sentry가 문자열로 주기도 한다. 숫자·문자열 모두 읽는다.
- `recentEvents[]`는 `/events/?full=true`의 최근 오류 이벤트 최대 10건이다. 자주 쓰는 필드를 정규화하고 `details`에 사용자·브레드크럼·컨텍스트·예외·스택트레이스 등 전체 JSON을 보존한다.
- `datasets[]`는 Picke-iOS에서 활성화한 `errors`, `logs`, `spans`, `profile_functions`, `tracemetrics` 데이터셋의 일별 발생량이다. 각 데이터셋 실패는 프로젝트 핵심 오류/이슈 조회를 실패시키지 않는다.
- `metricCatalog`는 커스텀 메트릭의 이름·타입·단위·건수·마지막 수집 시각과 컨텍스트를 원본 형태로 준다.
- `sessionHealth`는 자동 세션 추적의 `healthy`, `errored`, `crashed` 등 상태별 합계와 일별 시리즈를 준다.
- `releases`는 프로젝트 최근 릴리즈 최대 20개의 버전·빌드·커밋·배포·상태 원본을 준다.
- iOS 설정에서 Session Replay는 샘플 비율이 0으로 꺼져 있으므로 Replay 녹화 데이터는 조회하지 않는다.
- 환경변수: `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`(기본 `picke`), `SENTRY_PROJECTS`(기본 `picke-ios,picke-android`). 설정 키는 `picke.analytics.sentry.*`.
- Explore·메트릭·세션 API는 `org:read`, 오류 이벤트는 `event:read`, 프로젝트·릴리즈 조회는 `project:read` 또는 해당 상위 권한이 필요하다.
- 앱은 이미 같은 org·project로 Sentry에 리포트한다. 토큰만 서버에 넣으면 같은 데이터를 읽는다.
- 실측(2026-09-14, 최근 30일): `picke-ios` 미해결 2건·1,765 이벤트, `picke-android` 9건·181 이벤트.

## 공통

- 외부 호출 실패가 관리자 화면 전체를 500으로 만들지 않는다. 타임아웃은 연결 3초, 요청 10초다.
- 토큰은 응답에 담지 않는다. 서버 설정에만 둔다.
Loading
Loading