Conversation
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
설문 목록 UI src/features/survey-list/components/CustomSurveyList.tsx, src/features/survey-list/components/SurveyList.tsx, src/features/survey-list/components/UrgentSurveyList.tsx |
설문 아이템의 보상 표시를 고정값에서 survey.price ?? 200 형태로 변경하여 동적 가격 노출. |
설문 목록 훅·페이지 src/features/survey-list/hooks/useProcessedOngoingSurveys.ts, src/features/survey-list/pages/SurveyList.tsx |
mapSurveyToItem에 price 추가. totalPromotionAmount 계산을 설문별 가격 맵(기본 200)으로 합산하도록 재구성. |
타입 정의 (목록/서비스) src/features/survey-list/service/surveyList/types.ts, src/shared/types/surveyList.ts, src/features/survey/service/surveyParticipation/types.ts |
OngoingSurveySummary, SurveyListItem, SurveyBasicInfo 등에 선택적 price?: number 필드 추가. |
설문 보상 컴포넌트 src/features/survey/components/SurveyRewardInfoCard.tsx |
price prop 및 DEFAULT_REWARD_PRICE 도입. getEstimatedTimeByPrice 내보내기 추가; 보상가격 기반 예상시간 계산 로직으로 변경 및 UI에 동적 보상가 표시. |
설문 네비게이션 · 페이지 흐름 src/features/survey/hooks/useSurveyNavigation.ts, src/features/survey/pages/SectionBasedSurvey.tsx, src/features/survey/pages/Survey.tsx, src/features/survey/pages/Complete.tsx |
surveyInfo를 외부 스코프로 유지하여 price와 isFree를 네비게이션 state로 전달. Complete 페이지에서 reward_amount 및 추적 이벤트에 price 반영; SectionBasedSurvey에서 질문 없을 때 조기 반환 제거(렌더 허용). |
Sequence Diagram
sequenceDiagram
participant User as 사용자
participant SurveyPage as 설문 페이지
participant Service as 설문 서비스
participant Navigation as 네비게이션
participant Complete as 완료 페이지
User->>SurveyPage: 설문 시작
SurveyPage->>Service: getSurveyInfo(surveyId)
Service-->>SurveyPage: surveyInfo { price, isFree, ... }
SurveyPage->>SurveyPage: SurveyRewardInfoCard에 price 전달 (동적 표시)
User->>SurveyPage: 설문 완료
SurveyPage->>Navigation: navigate(Complete, { price: surveyInfo?.price, isFree: surveyInfo?.isFree })
Navigation->>Complete: location state 전달
Complete->>Complete: reward_amount = locationState?.price ?? 200
Complete->>Complete: 완료 추적 이벤트에 rewardAmount 포함
Complete-->>User: 완료 화면 표시
예상 코드 리뷰 시간
🎯 3 (Moderate) | ⏱️ ~20 minutes
관련 PR
- [OMF-189] 큐시즘 사용자 시나리오를 바탕으로 서비스 개선 #134: SurveyRewardInfoCard에 가격 처리 및 예상 시간 계산 추가와 연관된 변경들이 포함되어 있음.
- [OMF-96] 설문 참여 UX 개선 및 제한 해제 (기타 입력, 무료 설문 처리, 문항/인원 제한 해제) #122: useSurveyNavigation과 Complete 페이지의 네비게이션 상태(가격) 전파 관련 변경과 밀접한 코드 연관성이 있음.
- [OMF-144] 섹션 기반 설문 분기 로직 구현 #132: SectionBasedSurvey 내 surveyInfo 스코프/플로우 재구성 관련 리팩토링과 중복되는 조정 지점이 있음.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목이 변경 사항의 핵심을 명확하게 요약하고 있습니다. 프로모션 가격 변경 UI 수정이라는 주요 목표를 직접적으로 반영하고 있으며, 기술 세부사항과 파일 목록 없이 간결하게 표현되어 있습니다. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
- 📝 Generate docstrings (stacked PR)
- 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
OMF-258
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/features/survey/pages/SectionBasedSurvey.tsx (1)
321-338:⚠️ Potential issue | 🔴 Critical완료 화면 state에 지급 결과를 안 넘겨서 프로모션이 두 번 시도됩니다.
src/features/survey/pages/Complete.tsx는promotionIssued !== true면 재시도를 시작합니다. 여기서는issuePromotion()이 성공해도promotionIssued를 넘기지 않아서 유료 설문이 항상 재시도 경로로 들어가고, 지급 API가 비멱등이면 중복 지급까지 가능합니다.수정 예시
let surveyInfo: Awaited<ReturnType<typeof getSurveyInfo>> | undefined; +let promotionIssued: boolean | undefined; try { surveyInfo = await getSurveyInfo(surveyId); if (!surveyInfo.isFree) { await issuePromotion({ surveyId }); } + promotionIssued = true; // 무료/유료 구분 없이 홈 설문 리스트 갱신 queryClient.invalidateQueries({ queryKey: ["allOngoingSurveys"] }); } catch { + promotionIssued = false; // 실패해도 완료 페이지 이동 } navigate("/survey/complete", { state: { surveyId: String(surveyId), + isFree: surveyInfo?.isFree, source: locationState?.source, + promotionIssued, price: surveyInfo?.price, }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey/pages/SectionBasedSurvey.tsx` around lines 321 - 338, The complete page navigation never passes whether promotion was issued, causing Complete.tsx to retry issues; modify the flow around getSurveyInfo/issuePromotion to track success and pass promotionIssued in navigate state: declare a local boolean (e.g., promotionIssued = false), set it to true only after await issuePromotion({ surveyId }) succeeds, and in the navigate("/survey/complete", { state: { surveyId: String(surveyId), source: locationState?.source, price: surveyInfo?.price, promotionIssued } }) include that flag so Complete.tsx can skip retrying when true; ensure promotionIssued remains false in the catch/failure paths.src/features/survey/pages/Complete.tsx (1)
25-31:⚠️ Potential issue | 🟠 Major완료 이벤트가 무료/가격 누락 설문을 200원으로 오집계합니다.
price가 optional인데 여기서?? 200을 쓰면 무료 설문과getSurveyInfo실패 경로가 모두 실제 200원 설문으로 기록됩니다.isFree === true면0을 보내고, 그 외에는 실제price를 확보했을 때만 넣는 쪽이 안전합니다.Also applies to: 57-69
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey/pages/Complete.tsx` around lines 25 - 31, The page reads location.state (locationState) and currently uses `price ?? 200`, which misrecords free surveys and error paths as 200; change both occurrences (the initial locationState handling and the later block around lines 57-69) so that if locationState.isFree === true you send price = 0, otherwise only include price when locationState.price is explicitly provided (omit the price field entirely if undefined) — i.e., replace the `?? 200` logic with a conditional that sets 0 for isFree, uses the provided price when present, and avoids defaulting to 200.src/features/survey-list/hooks/useProcessedOngoingSurveys.ts (1)
73-87:⚠️ Potential issue | 🟠 Major총 프로모션 금액 계산이 반환 리스트와 어긋납니다.
위에서
filteredRecommended/filteredImpending로 마감 설문을 걸러냈는데, 총액은 다시 원본 배열을 순회합니다. 그래서 화면에 안 보이는 설문과isFree설문(price없음)까지 200원으로 합산됩니다.수정 예시
- const uniqueSurveyIds = getUniqueSurveyIdsFromArrays( - result.recommended, - result.impending, - ); + const uniqueSurveyIds = getUniqueSurveyIdsFromArrays( + filteredRecommended, + filteredImpending, + ); const surveyIdToPrice = new Map<number, number>(); - for (const s of [ - ...(result.recommended ?? []), - ...(result.impending ?? []), - ]) { - surveyIdToPrice.set(s.surveyId, s.price ?? 200); + for (const s of [...filteredRecommended, ...filteredImpending]) { + surveyIdToPrice.set(s.surveyId, s.isFree ? 0 : s.price ?? 200); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey-list/hooks/useProcessedOngoingSurveys.ts` around lines 73 - 87, The totalAmount computation in useProcessedOngoingSurveys currently iterates over the original result.recommended/result.impending arrays and uses price fallback for surveys that were filtered out; update it to use the already-filtered lists: build uniqueSurveyIds from filteredRecommended and filteredImpending, populate surveyIdToPrice from those filtered arrays (so excluded or isFree surveys are not counted), and then compute totalAmount by reducing over the uniqueSurveyIds using surveyIdToPrice (with the same 200 fallback only for filtered items missing price). Ensure you update the references to uniqueSurveyIds, surveyIdToPrice, and totalAmount accordingly.
🧹 Nitpick comments (2)
src/features/survey/hooks/useSurveyNavigation.ts (1)
217-260: 새 예외 경로를 콘솔 로그만으로 끝내지 마세요.여기서 추가된
getSurveyInfo/issuePromotion실패는 보상 지급 재시도까지 이어지는 경로인데, 현재는console.error만 남겨서 운영 추적이 어렵습니다.Sentry.captureException(error)와const { logger } = Sentry기반 구조화 로그로 바꿔 주세요. As per coding guidelines,**/*.{js,jsx,ts,tsx}: Use `Sentry.captureException(error)` to capture exceptions and log errors in Sentry, particularly in try-catch blocks or areas where exceptions are expected`.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey/hooks/useSurveyNavigation.ts` around lines 217 - 260, Replace the raw console error handling in the getSurveyInfo/issuePromotion try-catch paths by capturing exceptions in Sentry and emitting structured logs: import Sentry, call Sentry.captureException(error) inside each catch (both the inner catch around issuePromotion and the outer catch around getSurveyInfo), and also use the Sentry logger (const { logger } = Sentry) to emit a contextual logger.error including identifiers like surveyId, promotionIssued, and a short message (e.g., "issuePromotion failed" or "getSurveyInfo failed") instead of console.error; keep promotionIssued assignment logic but ensure errors are captured and logged via Sentry for observability.src/features/survey/pages/SectionBasedSurvey.tsx (1)
321-331: 제출 액션의 예외/지연 경로를 Sentry로 남겨 주세요.이 핸들러는 클릭 이후
getSurveyInfo/issuePromotion을 호출하지만catch가 비어 있어서, 완료 페이지 fallback이 왜 실행됐는지 추적할 단서가 없습니다.Sentry.startSpan({ op: "ui.click", ... })로 제출 경로를 감싸고catch에서Sentry.captureException(error)를 남겨 주세요. As per coding guidelines, "UseSentry.captureException(error)to capture exceptions and log errors in Sentry, particularly in try-catch blocks or areas where exceptions are expected" and "In component action handlers, useSentry.startSpanwithop: 'ui.click'to create spans for button clicks and other UI interactions".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey/pages/SectionBasedSurvey.tsx` around lines 321 - 331, Wrap the submission flow that calls getSurveyInfo and issuePromotion in a Sentry span using Sentry.startSpan({ op: "ui.click", description: "submit survey" }) and ensure the span is finished after the flow (success or failure); inside the try block keep the existing calls (getSurveyInfo(surveyId), issuePromotion({ surveyId }), queryClient.invalidateQueries(...)) and in the catch block call Sentry.captureException(error) before performing the existing fallback navigation, so exceptions are recorded and the span is closed on both paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/features/survey-list/components/SurveyList.tsx`:
- Around line 77-80: The list view is hardcoding "3분", causing mismatch with the
detail view's computation; extract or reuse the reward-to-duration logic from
SurveyRewardInfoCard (export its calculation function, e.g., getEstimatedMinutes
or calculateEstimatedMinutes) and import it into SurveyList, then replace the
hardcoded "3분" branch so bottom uses survey.isFree ? "보상이 없어요" :
`${<sharedDurationFunction>(survey.price ?? 200)}분이면 ${survey.price ?? 200}원
획득`; ensure the shared function name you export/import matches the symbol you
add in SurveyRewardInfoCard.
In `@src/features/survey/hooks/useSurveyNavigation.ts`:
- Around line 216-220: The Complete screen sometimes loses the isFree flag
because handlePrev() and the navigation to Complete don't include
surveyInfo.isFree in the navigation state; update the code paths that call
navigate/replace to the Complete screen (the block after awaiting getSurveyInfo
and the code around the handlePrev logic referenced near the 262-269 area) to
include isFree: surveyInfo?.isFree (or Boolean(surveyInfo?.isFree)) in the
location/state object, and ensure any re-checks that re-fetch surveyInfo also
propagate surveyInfo.isFree into locationState so locationState?.isFree is never
left undefined.
---
Outside diff comments:
In `@src/features/survey-list/hooks/useProcessedOngoingSurveys.ts`:
- Around line 73-87: The totalAmount computation in useProcessedOngoingSurveys
currently iterates over the original result.recommended/result.impending arrays
and uses price fallback for surveys that were filtered out; update it to use the
already-filtered lists: build uniqueSurveyIds from filteredRecommended and
filteredImpending, populate surveyIdToPrice from those filtered arrays (so
excluded or isFree surveys are not counted), and then compute totalAmount by
reducing over the uniqueSurveyIds using surveyIdToPrice (with the same 200
fallback only for filtered items missing price). Ensure you update the
references to uniqueSurveyIds, surveyIdToPrice, and totalAmount accordingly.
In `@src/features/survey/pages/Complete.tsx`:
- Around line 25-31: The page reads location.state (locationState) and currently
uses `price ?? 200`, which misrecords free surveys and error paths as 200;
change both occurrences (the initial locationState handling and the later block
around lines 57-69) so that if locationState.isFree === true you send price = 0,
otherwise only include price when locationState.price is explicitly provided
(omit the price field entirely if undefined) — i.e., replace the `?? 200` logic
with a conditional that sets 0 for isFree, uses the provided price when present,
and avoids defaulting to 200.
In `@src/features/survey/pages/SectionBasedSurvey.tsx`:
- Around line 321-338: The complete page navigation never passes whether
promotion was issued, causing Complete.tsx to retry issues; modify the flow
around getSurveyInfo/issuePromotion to track success and pass promotionIssued in
navigate state: declare a local boolean (e.g., promotionIssued = false), set it
to true only after await issuePromotion({ surveyId }) succeeds, and in the
navigate("/survey/complete", { state: { surveyId: String(surveyId), source:
locationState?.source, price: surveyInfo?.price, promotionIssued } }) include
that flag so Complete.tsx can skip retrying when true; ensure promotionIssued
remains false in the catch/failure paths.
---
Nitpick comments:
In `@src/features/survey/hooks/useSurveyNavigation.ts`:
- Around line 217-260: Replace the raw console error handling in the
getSurveyInfo/issuePromotion try-catch paths by capturing exceptions in Sentry
and emitting structured logs: import Sentry, call Sentry.captureException(error)
inside each catch (both the inner catch around issuePromotion and the outer
catch around getSurveyInfo), and also use the Sentry logger (const { logger } =
Sentry) to emit a contextual logger.error including identifiers like surveyId,
promotionIssued, and a short message (e.g., "issuePromotion failed" or
"getSurveyInfo failed") instead of console.error; keep promotionIssued
assignment logic but ensure errors are captured and logged via Sentry for
observability.
In `@src/features/survey/pages/SectionBasedSurvey.tsx`:
- Around line 321-331: Wrap the submission flow that calls getSurveyInfo and
issuePromotion in a Sentry span using Sentry.startSpan({ op: "ui.click",
description: "submit survey" }) and ensure the span is finished after the flow
(success or failure); inside the try block keep the existing calls
(getSurveyInfo(surveyId), issuePromotion({ surveyId }),
queryClient.invalidateQueries(...)) and in the catch block call
Sentry.captureException(error) before performing the existing fallback
navigation, so exceptions are recorded and the span is closed on both paths.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0b444e19-7e77-4454-8420-b87e64261c97
📒 Files selected for processing (13)
src/features/survey-list/components/CustomSurveyList.tsxsrc/features/survey-list/components/SurveyList.tsxsrc/features/survey-list/components/UrgentSurveyList.tsxsrc/features/survey-list/hooks/useProcessedOngoingSurveys.tssrc/features/survey-list/pages/SurveyList.tsxsrc/features/survey-list/service/surveyList/types.tssrc/features/survey/components/SurveyRewardInfoCard.tsxsrc/features/survey/hooks/useSurveyNavigation.tssrc/features/survey/pages/Complete.tsxsrc/features/survey/pages/SectionBasedSurvey.tsxsrc/features/survey/pages/Survey.tsxsrc/features/survey/service/surveyParticipation/types.tssrc/shared/types/surveyList.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/features/survey-list/components/SurveyList.tsx (1)
1-1: 기본 리워드 fallback도 같은 공용 규칙으로 묶어두는 편이 안전합니다.시간 계산은 공용 헬퍼를 쓰는데 금액은 여기서만
200으로 다시 하드코딩하고 있습니다. 기본 리워드가 다시 바뀌면 규칙이 쉽게 어긋날 수 있으니, fallback 금액까지 공용 유틸/상수로 함께 재사용하면 유지보수가 더 쉬워집니다.Also applies to: 81-81
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/survey-list/components/SurveyList.tsx` at line 1, The fallback reward amount is hardcoded as 200 in SurveyList.tsx while time calculations reuse getEstimatedTimeByPrice; replace the local hardcoded fallback with a shared constant or util (e.g., export a DEFAULT_REWARD or getDefaultReward() from the same module that provides getEstimatedTimeByPrice) and import that constant into SurveyList.tsx so both the fallback amount and any other reward logic reference the single shared symbol instead of duplicating the value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/features/survey-list/components/SurveyList.tsx`:
- Line 1: The fallback reward amount is hardcoded as 200 in SurveyList.tsx while
time calculations reuse getEstimatedTimeByPrice; replace the local hardcoded
fallback with a shared constant or util (e.g., export a DEFAULT_REWARD or
getDefaultReward() from the same module that provides getEstimatedTimeByPrice)
and import that constant into SurveyList.tsx so both the fallback amount and any
other reward logic reference the single shared symbol instead of duplicating the
value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d36a3467-89d8-4d02-a8db-0bcca19ecab7
📒 Files selected for processing (3)
src/features/survey-list/components/SurveyList.tsxsrc/features/survey/components/SurveyRewardInfoCard.tsxsrc/features/survey/hooks/useSurveyNavigation.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/features/survey/components/SurveyRewardInfoCard.tsx
- src/features/survey/hooks/useSurveyNavigation.ts
📌 주요 변경 사항
🔗 관련 이슈
Jira 링크: https://onsurvey.atlassian.net/browse/OMF-258, https://onsurvey.atlassian.net/browse/OMF-256
✅ 리뷰 요청 사항 (Need Review)
🎨 스크린샷 (선택)
Summary by CodeRabbit
릴리스 노트
New Features
Chores