Conversation
|
Note
|
| Layer / File(s) | Summary |
|---|---|
데이터 계약 src/features/survey-list/service/surveyList/types.ts, src/features/survey/service/surveyParticipation/types.ts, src/features/survey/hooks/useSectionBasedSurveyController.ts, src/features/screening/components/IneligibleSurveyBottomSheet.tsx |
새 SurveyOpenStats 인터페이스(openSurveyCount, maxRewardCoin), SurveyBasicInfo.totalSections?, SectionBasedSurveyState.sectionCount?, IneligibleSurveyBottomSheetProps에 title?, description?, confirmNavigatesHome? 추가. |
API 및 쿼리 레이어 src/features/survey-list/service/surveyList/api.ts, src/features/survey-list/hooks/useAllOngoingSurveys.ts, src/features/survey-list/hooks/useSurveyOpenStats.ts |
getSurveyOpenStats() API 추가 및 useSurveyOpenStats 훅 도입. useAllOngoingSurveys에 options?: { enabled?: boolean } 파라미터로 쿼리 실행 제어 추가. |
데이터 매핑 및 섹션 컨트롤러 src/features/survey-list/hooks/useProcessedOngoingSurveys.ts, src/features/survey/hooks/useSectionBasedSurveyController.ts |
mapOngoingSurveySummaryToSurveyListItem 추출·내보내기. 섹션 컨트롤러에 sectionCount 페칭(없을 시 API 조회), progress 계산(클램프), 마지막 섹션 판정에 선택지 빈 응답 검사 추가, isBeforeSubmitStep 반환. |
UI 컴포넌트 및 설정 src/features/screening/components/IneligibleSurveyBottomSheet.tsx, src/features/survey/pages/SectionBasedSurvey.tsx |
IneligibleSurveyBottomSheet에 제목/설명·confirm 동작 제어 추가. SectionBasedSurvey에 ProgressBar와 최종/마일스톤 메시지 렌더링 추가(컨트롤러 상태 사용). |
통합 및 네비게이션 src/app/Intro.tsx, src/features/survey/pages/Survey.tsx |
Intro에 참여 버튼 핸들러(토스 로그인 → ongoing 설문 조회(최대 100) → 자격 필터링 → 최고 가격 설문 선택 → allOngoingSurveys 쿼리 무효화 → 스크리닝 확인 → 온보딩/oxScreening/설문으로 라우팅) 및 프로모션 배너 노출(useSurveyOpenStats) 추가. Survey 페이지에서 sectionCount를 네비게이션 상태로 전달. |
예상 코드 리뷰 노력
🎯 4 (Complex) | ⏱️ ~45 minutes
관련된 가능성 있는 PR
- On-Survey/Frontend#141: 섹션 기반 설문 네비게이션/상태 변경 관련 수정이 겹칩니다.
- On-Survey/Frontend#112: Intro 로그인/온보딩 관련 리다이렉션 흐름이 유사합니다.
- On-Survey/Frontend#132: 섹션 기반 흐름·타입 변경과 관련된 교차 수정이 있습니다.
추천 리뷰어
- 78-artilleryman
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목은 이 PR의 주요 변경 사항들을 정확하게 반영하고 있습니다. 인트로 페이지의 설문 유입 흐름, open-stats 연동, 섹션 설문 UX/분기 판정 개선이라는 세 가지 핵심 변경 사항을 간결하게 요약했습니다. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
OMF-332
Tip
💬 Introducing Slack Agent: The best way for teams to turn conversations into code.
Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
- Generate code and open pull requests
- Plan features and break down work
- Investigate incidents and troubleshoot customer tickets together
- Automate recurring tasks and respond to alerts with triggers
- Summarize progress and report instantly
Built for teams:
- Shared memory across your entire org—no repeating context
- Per-thread sandboxes to safely plan and execute work
- Governance built-in—scoped access, auditability, and budget controls
One agent for your entire SDLC. Right inside Slack.
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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/features/survey/pages/Survey.tsx (1)
230-270:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
handleStart버튼 클릭 핸들러에 Sentryui.clickspan 계측을 추가하세요.라인 230의 핵심 CTA 핸들러가 라우팅까지 수행하지만, 사용자 인터랙션 span이 없어 모니터링 공백이 발생합니다. 코딩 가이드라인에 따라
Sentry.startSpan(op: 'ui.click')과span.setAttribute()를 통해 설문 ID와 출처를 추적하고, Sentry import를 추가하세요.🔧 제안 diff
+import * as Sentry from "@sentry/react"; ... const handleStart = () => { - if (isStartDisabled) { - if (surveyBasicInfoData?.isScreenRequired) { - navigate(`/oxScreening?surveyId=${numericSurveyId}`, { replace: true }); - } else if (surveyBasicInfoData?.isScreened) { - setErrorDialog({ - open: true, - title: "스크리닝 조건이 맞지 않습니다", - description: "설정하신 스크리닝 조건에 맞지 않아 설문에 참여할 수 없어요.", - redirectTo: "/home", - }); - } - return; - } - - if (surveyBasicInfoData?.isSurveyResponded) { - openToast("이미 참여한 설문입니다.", { - type: "bottom", - lottie: "https://static.toss.im/lotties-common/error-yellow-spot.json", - higherThanCTA: true, - }); - return; - } - - const source = locationState?.source ?? "main"; - - navigate("/survey/section", { - state: { - surveyId: numericSurveyId, - currentSection: 1, - sectionCount: surveyBasicInfoData?.totalSections, - answers: {}, - previousAnswers: {}, - surveyTitle: surveyTitle ?? "", - surveyDescription: surveyDescription ?? "", - source, - price: surveyBasicInfoData?.price ?? priceFromState, - }, - }); + Sentry.startSpan( + { name: "Survey start click", op: "ui.click" }, + (span) => { + span.setAttribute("survey.id", numericSurveyId ?? -1); + span.setAttribute("survey.source", locationState?.source ?? "main"); + + if (isStartDisabled) { + if (surveyBasicInfoData?.isScreenRequired) { + navigate(`/oxScreening?surveyId=${numericSurveyId}`, { replace: true }); + } else if (surveyBasicInfoData?.isScreened) { + setErrorDialog({ + open: true, + title: "스크리닝 조건이 맞지 않습니다", + description: "설정하신 스크리닝 조건에 맞지 않아 설문에 참여할 수 없어요.", + redirectTo: "/home", + }); + } + return; + } + + if (surveyBasicInfoData?.isSurveyResponded) { + openToast("이미 참여한 설문입니다.", { + type: "bottom", + lottie: "https://static.toss.im/lotties-common/error-yellow-spot.json", + higherThanCTA: true, + }); + return; + } + + const source = locationState?.source ?? "main"; + navigate("/survey/section", { + state: { + surveyId: numericSurveyId, + currentSection: 1, + sectionCount: surveyBasicInfoData?.totalSections, + answers: {}, + previousAnswers: {}, + surveyTitle: surveyTitle ?? "", + surveyDescription: surveyDescription ?? "", + source, + price: surveyBasicInfoData?.price ?? priceFromState, + }, + }); + }, + ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/survey/pages/Survey.tsx` around lines 230 - 270, Wrap the handleStart logic in a Sentry UI click span: import Sentry, call Sentry.startSpan({ op: "ui.click" }) at the start of handleStart, set attributes on the span (e.g., span.setAttribute("survey.id", numericSurveyId) and span.setAttribute("survey.source", source or locationState?.source ?? "main")), and finish the span right before each early return and after the navigate call so the span always ends; ensure span is cleaned up in every control path (errors/returns) and do not change existing navigation/state logic in handleStart.
🧹 Nitpick comments (4)
src/features/survey-list/hooks/useProcessedOngoingSurveys.ts (1)
86-86: ⚡ Quick win매직 넘버를 상수로 추출하세요.
기본 가격
200이 두 곳(lines 86, 89)에서 하드코딩되어 있습니다. 이를 파일 상단에DEFAULT_SURVEY_PRICE같은 명명된 상수로 추출하면 유지보수성과 의도 전달이 개선됩니다.♻️ 제안된 수정
파일 상단에 상수 추가:
const DEFAULT_TOPIC: SurveyListItem["topicId"] = "DAILY_LIFE"; +const DEFAULT_SURVEY_PRICE = 200;lines 86, 89에서 사용:
- surveyIdToPrice.set(s.surveyId, s.price ?? 200); + surveyIdToPrice.set(s.surveyId, s.price ?? DEFAULT_SURVEY_PRICE);- (sum, id) => sum + (surveyIdToPrice.get(id) ?? 200), + (sum, id) => sum + (surveyIdToPrice.get(id) ?? DEFAULT_SURVEY_PRICE),Also applies to: 89-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/survey-list/hooks/useProcessedOngoingSurveys.ts` at line 86, Extract the hardcoded default price (200) into a named constant at the top of the file (e.g., DEFAULT_SURVEY_PRICE) and replace both occurrences where the fallback is used (the nullish fallback in surveyIdToPrice.set(s.surveyId, s.price ?? 200) and the other similar occurrence) to use that constant; update any related JSDoc or comments if present and ensure imports/exports are unchanged.src/features/survey-list/hooks/useSurveyOpenStats.ts (1)
4-9: 💤 Low value
staleTime설정 검토 권장.
open-stats는 비인증 공개 통계로 자주 바뀌지 않으므로staleTime을 부여하면 인트로 재진입 시 불필요한 재요청을 줄일 수 있습니다.♻️ 제안 변경
export const useSurveyOpenStats = () => { return useQuery({ queryKey: ["surveyOpenStats"], queryFn: getSurveyOpenStats, + staleTime: 60 * 1000, }); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/survey-list/hooks/useSurveyOpenStats.ts` around lines 4 - 9, The query useSurveyOpenStats currently calls useQuery with queryKey ["surveyOpenStats"] and queryFn getSurveyOpenStats but no staleTime; since open-stats are unauthenticated and infrequently changing, add a staleTime (e.g., 5–15 minutes) to the useQuery options to prevent unnecessary refetches on re-entry—update useSurveyOpenStats to pass staleTime: 300000 (or chosen ms) alongside queryKey and queryFn.src/app/Intro.tsx (2)
21-31: 💤 Low value
pickHighestPriceAmongEligible단일 패스로 단순화 가능.현재는
filter → max → filter → sort과정을 거쳐 동일 데이터를 두 번 순회합니다. 한 번의reduce로도 동일한 동작(최고가 중 최저 surveyId 우선)이 가능합니다. 동작상 동등하며 가독성·미세한 성능 모두 약간 개선됩니다.♻️ 제안 변경
function pickHighestPriceAmongEligible( list: OngoingSurveySummary[], ): OngoingSurveySummary | undefined { - const eligible = list.filter((s) => s.isEligible ?? false); - if (eligible.length === 0) return undefined; - const maxPrice = Math.max(...eligible.map((s) => s.price ?? 0)); - const tied = eligible.filter((s) => (s.price ?? 0) === maxPrice); - tied.sort((a, b) => a.surveyId - b.surveyId); - return tied[0]; + return list + .filter((s) => s.isEligible ?? false) + .reduce<OngoingSurveySummary | undefined>((best, cur) => { + if (!best) return cur; + const bp = best.price ?? 0; + const cp = cur.price ?? 0; + if (cp > bp) return cur; + if (cp === bp && cur.surveyId < best.surveyId) return cur; + return best; + }, undefined); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/Intro.tsx` around lines 21 - 31, The function pickHighestPriceAmongEligible currently filters and scans the list multiple times; replace that with a single-pass reduce over list to track the best candidate: skip non-eligible items (s.isEligible), compare (s.price ?? 0) to current maxPrice and update the best item when price is higher or when equal but s.surveyId is smaller; return the reduced best item (or undefined) so you eliminate the separate eligible, maxPrice, tied, and sort steps while preserving the "highest price, tie-break by smallest surveyId" behavior.
145-241: ⚖️ Poor tradeoffSentry 로깅 및 스팬 추적 적용을 권장합니다.
handleParticipateLogin함수의 try-catch 블록에서console.error만 사용되고 있습니다. 코딩 가이드라인에 따라 예외는Sentry.captureException(error)로 캡처하고, UI 클릭은Sentry.startSpan({ op: 'ui.click', ... })으로, API 호출은op: 'http.client'스팬으로 감싸면 로그인 플로우의 오류 및 이탈 경로 가시성을 크게 향상할 수 있습니다. 프로젝트에 이미 Sentry가 초기화되어 있고 다른 모듈에서도 활용 중이므로, 신규 코드부터 점진적으로 적용하기 좋은 시점입니다.♻️ 적용 예시
-import { useEffect, useState } from "react"; +import * as Sentry from "@sentry/react"; +import { useEffect, useState } from "react"; @@ - const handleParticipateLogin = async () => { - pushGtmEvent({ ... }); - try { - const { authorizationCode, referrer } = await appLogin(); - const loginApiResponse = await loginApi(authorizationCode, referrer); + const handleParticipateLogin = async () => { + await Sentry.startSpan( + { op: "ui.click", name: "intro.participate" }, + async (span) => { + span.setAttribute("openSurveyCount", openSurveyCount); + pushGtmEvent({ ... }); + try { + const { authorizationCode, referrer } = await appLogin(); + const loginApiResponse = await Sentry.startSpan( + { op: "http.client", name: "POST /v1/auth/login" }, + () => loginApi(authorizationCode, referrer), + ); @@ - } catch (error) { - console.error("진행 설문 목록 조회 실패:", error); + } catch (error) { + Sentry.captureException(error); setIneligibleSurveySheet({ ... }); return; } @@ - } catch (error) { - console.error("토스 로그인 실패:", error); - } + } catch (error) { + Sentry.captureException(error); + } + }, + ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/Intro.tsx` around lines 145 - 241, Wrap the login flow in Sentry spans and replace console.error calls with Sentry.captureException: start a ui.click span at the start of handleParticipateLogin (op: 'ui.click') and create nested http.client spans around async calls appLogin(), loginApi(), getAllOngoingSurveys(), and getScreenings() so each API call is traced; in each catch block (the outer try-catch in handleParticipateLogin and the inner catches around getAllOngoingSurveys and getScreenings) call Sentry.captureException(error) (optionally setting span.setStatus or adding tags/context like { step: 'getAllOngoingSurveys' }) and finish/close any open spans before returning/continuing; ensure spans are finished in finally blocks where needed so Sentry shows complete timing for handleParticipateLogin, loginApi, getAllOngoingSurveys, and getScreenings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/Intro.tsx`:
- Around line 199-230: The current try/catch around getScreenings leaves
hasScreening=false on errors which can wrongly route users into the survey;
change the error handling to either perform one retry of getScreenings or set a
explicit screeningCheckFailed flag and branch to show the
IneligibleSurveyBottomSheet instead of navigating into the survey when the check
fails. Concretely: in the block that calls getScreenings (function getScreenings
and local var hasScreening), on catch attempt one immediate retry of
getScreenings and only if that also fails set screeningCheckFailed=true; then,
in the onboardingCompleted branch where navigate and navigateToSurveyInfo are
used, guard those navigations with screeningCheckFailed (or hasScreening) so
that if screeningCheckFailed is true you open the IneligibleSurveyBottomSheet
(or another blocking UI) instead of calling navigate or navigateToSurveyInfo.
- Around line 184-191: When targetSummary is falsy in Intro.tsx (the branch
using setIneligibleSurveySheet), avoid unguarded navigation to /home: either (A)
pass confirmNavigatesHome={false} into the IneligibleSurveyBottomSheet and
handle navigation in its onClose by checking
loginApiResponse.onboardingCompleted and routing to /onboarding when false, or
(B) before showing the sheet (the !targetSummary branch that calls
setIneligibleSurveySheet) check loginApiResponse.onboardingCompleted and
navigate to /onboarding immediately for incomplete users instead of allowing the
default confirmNavigatesHome behavior; update the code paths that call
pickHighestPriceAmongEligible and setIneligibleSurveySheet accordingly so
onboarding checks occur whenever the empty/error sheet would otherwise navigate
home.
In `@src/features/survey/hooks/useSectionBasedSurveyController.ts`:
- Around line 85-95: 현재 decidableBranchQuestion가 questions.find로 첫 번째
isSectionDecidable 객관식 문항만 검사해 뒤쪽 decider 문항의 미응답을 놓칠 수 있으니, 단일 항목 찾기 대신 해당 섹션의
모든 decider 객관식 문항을 검사하도록 변경하세요: questions에서 q.isSectionDecidable && q.type ===
"multipleChoice"인 항목들에 대해 answersRef.current[q.questionId]가 비어있는 항목이 하나라도
있는지(some)를 사용하여 awaitingDecidableBranch를 계산하고, 이 값을 isLastSection 계산(사용되는
actualNextSection과 결합) 부분에서 사용하도록 고치세요; 관련 식별자: decidableBranchQuestion,
awaitingDecidableBranch, isMultipleChoiceAnswerEmpty, answersRef.current,
isLastSection, actualNextSection.
- Around line 138-151: The getSurveyInfo call inside the useEffect (in
useSectionBasedSurveyController) currently swallows errors; wrap the async fetch
with a Sentry span and proper error reporting: start a span via
Sentry.startSpan({ op: 'http.client', description: 'getSurveyInfo' }) before
calling getSurveyInfo, finish the span after the request, and in the catch block
call Sentry.captureException(error) and logger.warn with the error and context;
ensure setSurveySectionCount unchanged on success and always finish the span in
finally to avoid leaked spans.
---
Outside diff comments:
In `@src/features/survey/pages/Survey.tsx`:
- Around line 230-270: Wrap the handleStart logic in a Sentry UI click span:
import Sentry, call Sentry.startSpan({ op: "ui.click" }) at the start of
handleStart, set attributes on the span (e.g., span.setAttribute("survey.id",
numericSurveyId) and span.setAttribute("survey.source", source or
locationState?.source ?? "main")), and finish the span right before each early
return and after the navigate call so the span always ends; ensure span is
cleaned up in every control path (errors/returns) and do not change existing
navigation/state logic in handleStart.
---
Nitpick comments:
In `@src/app/Intro.tsx`:
- Around line 21-31: The function pickHighestPriceAmongEligible currently
filters and scans the list multiple times; replace that with a single-pass
reduce over list to track the best candidate: skip non-eligible items
(s.isEligible), compare (s.price ?? 0) to current maxPrice and update the best
item when price is higher or when equal but s.surveyId is smaller; return the
reduced best item (or undefined) so you eliminate the separate eligible,
maxPrice, tied, and sort steps while preserving the "highest price, tie-break by
smallest surveyId" behavior.
- Around line 145-241: Wrap the login flow in Sentry spans and replace
console.error calls with Sentry.captureException: start a ui.click span at the
start of handleParticipateLogin (op: 'ui.click') and create nested http.client
spans around async calls appLogin(), loginApi(), getAllOngoingSurveys(), and
getScreenings() so each API call is traced; in each catch block (the outer
try-catch in handleParticipateLogin and the inner catches around
getAllOngoingSurveys and getScreenings) call Sentry.captureException(error)
(optionally setting span.setStatus or adding tags/context like { step:
'getAllOngoingSurveys' }) and finish/close any open spans before
returning/continuing; ensure spans are finished in finally blocks where needed
so Sentry shows complete timing for handleParticipateLogin, loginApi,
getAllOngoingSurveys, and getScreenings.
In `@src/features/survey-list/hooks/useProcessedOngoingSurveys.ts`:
- Line 86: Extract the hardcoded default price (200) into a named constant at
the top of the file (e.g., DEFAULT_SURVEY_PRICE) and replace both occurrences
where the fallback is used (the nullish fallback in
surveyIdToPrice.set(s.surveyId, s.price ?? 200) and the other similar
occurrence) to use that constant; update any related JSDoc or comments if
present and ensure imports/exports are unchanged.
In `@src/features/survey-list/hooks/useSurveyOpenStats.ts`:
- Around line 4-9: The query useSurveyOpenStats currently calls useQuery with
queryKey ["surveyOpenStats"] and queryFn getSurveyOpenStats but no staleTime;
since open-stats are unauthenticated and infrequently changing, add a staleTime
(e.g., 5–15 minutes) to the useQuery options to prevent unnecessary refetches on
re-entry—update useSurveyOpenStats to pass staleTime: 300000 (or chosen ms)
alongside queryKey and queryFn.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 74ae219e-baad-409b-8418-2cdb6fe7a61a
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
src/app/Intro.tsxsrc/features/screening/components/IneligibleSurveyBottomSheet.tsxsrc/features/survey-list/hooks/useAllOngoingSurveys.tssrc/features/survey-list/hooks/useProcessedOngoingSurveys.tssrc/features/survey-list/hooks/useSurveyOpenStats.tssrc/features/survey-list/service/surveyList/api.tssrc/features/survey-list/service/surveyList/types.tssrc/features/survey/hooks/useSectionBasedSurveyController.tssrc/features/survey/pages/SectionBasedSurvey.tsxsrc/features/survey/pages/Survey.tsxsrc/features/survey/service/surveyParticipation/types.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/Intro.tsx`:
- Around line 139-233: Import Sentry at the top (import * as Sentry from
'@sentry/react') and instrument the participate-login flow in
handleParticipateLogin: for the getAllOngoingSurveys and getScreenings try/catch
blocks call Sentry.captureException(error) in their catch blocks and
create/finish a span with op: 'http.client' around the failing API call
(referencing getAllOngoingSurveys and getScreenings); in the outermost catch for
the login flow (the catch that logs "토스 로그인 실패:") also call
Sentry.captureException(error). Additionally, wrap the UI button click that
invokes handleParticipateLogin with a Sentry.startSpan({ op: 'ui.click',
description: 'participate login click' }) and finish that span when the handler
completes/returns (reference the component/button that calls
handleParticipateLogin).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 927b74f8-196c-487b-8291-ecdd28eb8eb7
📒 Files selected for processing (3)
src/app/Intro.tsxsrc/features/survey/hooks/useSectionBasedSurveyController.tssrc/features/survey/pages/SectionBasedSurvey.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/features/survey/hooks/useSectionBasedSurveyController.ts
- src/features/survey/pages/SectionBasedSurvey.tsx
📌 Feat: 인트로 설문 유입·open-stats 연동 및 섹션 설문 UX/분기 판정 개선
요약
인트로에서 비인증 사용자를 대상으로 공개 설문 통계(open-stats)를 노출하고, 로그인 이후 자연스럽게 설문 참여로 이어지는 플로우를 연결했습니다.
또한 섹션 설문 UI를 다듬고, 마지막 섹션 여부를 잘못 판정하던 분기 로직을 수정해 치어 메시지 및 제출 단계 판정 오류를 개선했습니다.
주요 변경 사항
인트로 · 공개 통계 API 연동
GET /v1/surveys/open-stats연동openSurveyCountmaxRewardCoinuseSurveyOpenStats훅 추가설문 배너 노출 조건 및 카피 개선
참여하기액션 플로우 연결ongoing/all목록 조회isEligible) 설문만 필터링oxScreeninginfo로 진입참여 불가 또는 목록 조회 실패 시
설문 정보 API 타입 정리
SurveyBasicInfo에totalSections반영섹션 진행/네비게이션 state에서는 기존
sectionCount키 유지totalSections기준으로 사용하도록 정리섹션 설문 UI 개선
프로그레스 바 하단 안내 영역 스타일 개선
적용 스타일
🐞 버그 수정
마지막 섹션 오인 판정 수정
문제
기존에는 아래 조건만으로 마지막 섹션 여부를 판단했습니다.
하지만 분기형 객관식 문항에서 아직 답변하지 않은 상태에서도 API가
nextSection: 0을 반환하는 경우가 있어, 실제 마지막 섹션이 아닌데도 종료 직전으로 오인되는 문제가 있었습니다.영향
아직 마지막이 아닌데도
거의 다 왔어요메시지가 잠깐 노출됨이후 선택값에 따라 분기가 결정되면
잘 하고 있어요메시지와 혼재되어 보이는 현상 발생동일 로직이 제출 단계(
isBeforeSubmitStep) 판단에도 영향을 줄 가능성 존재대응
현재 섹션이 아래 조건을 만족하는 경우:
isSectionDecidable === truemultipleChoice문항 존재→
awaitingDecidableBranch상태로 간주하도록 수정했습니다.이 상태에서는
isLastSection을true로 판단하지 않도록 변경해,분기 확정 이전에 마지막 섹션으로 오인되지 않도록 처리했습니다.
🔗 관련 이슈
✅ 리뷰 요청 사항
🎨 스크린샷
Summary by CodeRabbit
새로운 기능
개선 사항