Skip to content

[BOM-490] feat: 완료한 챌린지 카드에서 해당 챌린지 대시보드로 이동#294

Open
JeLee-river wants to merge 5 commits into
devfrom
BOM-490-마이페이지-챌린지-완료-카드-네비게이션-추가

Hidden character warning

The head ref may contain hidden characters: "BOM-490-\ub9c8\uc774\ud398\uc774\uc9c0-\ucc4c\ub9b0\uc9c0-\uc644\ub8cc-\uce74\ub4dc-\ub124\ube44\uac8c\uc774\uc158-\ucd94\uac00"
Open

[BOM-490] feat: 완료한 챌린지 카드에서 해당 챌린지 대시보드로 이동#294
JeLee-river wants to merge 5 commits into
devfrom
BOM-490-마이페이지-챌린지-완료-카드-네비게이션-추가

Conversation

@JeLee-river

@JeLee-river JeLee-river commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

📌 What

  • 마이페이지 완료 챌린지 카드 클릭 시 해당 챌린지 대시보드로 이동
  • 탈락(FAIL) 챌린지 카드는 클릭 비활성화 (cursor: not-allowed, 호버 효과 없음)
2026-07-14.11.40.21.mov

❓ Why

  • 완료한 챌린지를 다시 보려면 챌린지 목록을 거쳐야 했음 — 기획 요청으로 마이페이지에서 바로 진입할 수 있도록 개선

🔧 How

  • CompletedChallengeCardContaineruseNavigate/challenge/$challengeId/dashboard 이동 연결
  • grade === 'FAIL'이면 disabled prop으로 클릭·호버 비활성화
  • 수료증 확인 버튼 클릭 시 stopPropagation으로 카드 이동 이벤트 차단

👀 Review Point

  • 탈락 카드: 호버해도 배경 변화 없고 클릭 무반응인지 확인
  • 수료증 확인 버튼 클릭 시 대시보드 이동 없이 모달만 열리는지 확인
  • 대시보드 페이지가 완료된 챌린지 기준으로 정상 노출되는지 확인

@jaeyoung-kwon jaeyoung-kwon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 PR Review

⚠️ 수정이 필요한 리뷰 코멘트가 있습니다.

완료 챌린지 카드에 대시보드 이동 기능을 추가한 변경입니다. FAIL 카드 클릭 비활성화, 수료증 버튼의 stopPropagation 처리는 의도대로 잘 구현되어 있습니다. 다만 challengeId가 없는 완료 챌린지에서의 이동 처리와 클릭 카드의 키보드 접근성 두 가지를 확인해 주세요.

🚨 0 Critical · ⚠️ 2 Major · 📝 1 Minor

수정 필요

  • web/src/pages/my-page/components/MyChallengeSection/CompletedChallengeCard.tsx:40 — challengeId가 없는 완료 챌린지에서 /challenge/undefined/dashboard로 이동할 수 있음 (인라인 코멘트 참조).
  • web/src/pages/my-page/components/MyChallengeSection/CompletedChallengeCard.tsx:56 — 클릭 가능한 카드에 키보드/스크린리더 접근성이 없음 (인라인 코멘트 참조).

참고

  • web/src/pages/my-page/components/MyChallengeSection/CompletedChallengeCard.tsx:80CertButtondisabled={isFail}은 항상 false
    CertButtongrade !== 'FAIL'일 때만 렌더링되므로 이 분기에서 isFail은 항상 false입니다. disabled prop이 실질적으로 아무 효과가 없어 읽는 사람에게 혼동을 줄 수 있습니다. 제거하거나 의도를 명확히 하는 주석을 남기는 것을 검토해 주세요.
📋 검증 과정
  • Claude structured review 결과 중 확신도가 있는 항목만 정리했습니다.
  • Critical/Major는 inline comment로 게시하고, Minor는 참고 항목으로 summary에 포함합니다.
  • 자동 생성된 OpenAPI 타입 선언 파일과 lock 파일은 리뷰 대상에서 제외합니다.

🤖 Claude PR Review


Generated by Claude Code

Comment on lines +40 to +47
const handleCardClick = () => {
if (isFail) return;

navigate({
to: '/challenge/$challengeId/dashboard',
params: { challengeId: String(challengeId) },
});
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [Major] challengeId가 없는 완료 챌린지에서 /challenge/undefined/dashboard로 이동할 수 있음

MyCompletedChallenge.challengeId는 OpenAPI 스키마상 number | undefined입니다(web/src/types/openapi.d.tsCompletedChallengeResponse.challengeId?: number). 실제로 90번째 줄에서는 CertificateModal을 렌더링할 때 challengeId !== undefined를 명시적으로 체크하고 있어, 이 값이 비어 있을 수 있다는 걸 이미 인지하고 있는 것으로 보입니다.

하지만 handleCardClickisFail만 체크하고 challengeId의 존재 여부는 확인하지 않습니다. 등급이 FAIL이 아니면서 challengeId가 없는 카드를 클릭하면 String(challengeId)"undefined" 문자열이 되어 /challenge/undefined/dashboard로 이동하게 됩니다.

Claude 검증: 변경 diff의 web/src/pages/my-page/components/MyChallengeSection/CompletedChallengeCard.tsx:40-47 기준으로 확인했습니다. 실제 코드와 다르면 이 코멘트는 무시해 주세요. (confidence 0.75).

수정 제안:
이 finding이 여전히 유효한지 먼저 확인한 뒤 (예: 실제 API 응답에서 완료 챌린지의 challengeId가 항상 존재하는지), 유효하다면 handleCardClick에서 if (isFail || challengeId === undefined) return;처럼 가드를 추가하는 것을 검토해 주세요.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

챌린지 id가 없으면 클릭 이벤트가 발생하지 않도록 가드를 추가했습니다

fix: 챌린지 id가 없을때 페이지로 라우팅 되지 않도록 개선

return (
<>
<Container>
<Container disabled={isFail} onClick={handleCardClick}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [Major] 클릭 가능한 카드에 키보드/스크린리더 접근성이 없음

ContaineronClick으로 실질적인 네비게이션 트리거가 되었지만 role="button", tabIndex, 키보드 핸들러(Enter/Space)가 없어 마우스 없이는 이 카드로 대시보드에 진입할 수 없습니다.

같은 저장소의 web/src/pages/challenge/index/components/ChallengeCard/CardContainer.tsx는 동일한 "클릭 가능한 카드" 패턴에서 interactive = Boolean(onClick) && !disabled를 기준으로 role, tabIndex, onClick을 조건부로 부여하는 방식을 이미 사용하고 있어, 이 PR만 해당 컨벤션을 따르지 않게 됩니다.

Claude 검증: 변경 diff의 web/src/pages/my-page/components/MyChallengeSection/CompletedChallengeCard.tsx:56 기준으로 확인했습니다. 실제 코드와 다르면 이 코멘트는 무시해 주세요. (confidence 0.7).

수정 제안:
이 finding이 여전히 유효한지 먼저 확인한 뒤, CardContainer.tsx의 패턴을 참고해 role="button"/tabIndex={0}/키보드 핸들러를 추가하거나, 팀에서 이 정도는 허용 범위라고 판단하면 그대로 두어도 됩니다.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

div 태그에 onClick이 props로 전달되고 있어서 접근성 관련 코멘트가 달린 것 같아, button 태그로 수정했습니다!

refactor: 완료한 챌린지 카드를 버튼 태그로 변경

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

수정했지만.. button 안에 button(수료증 확인)이 있는 중첩스타일이라서 div as="button" 으로 변경하고 접근성 개선 코드를 추가했습니다

refactor: 카드를 button에서 div as button으로 수정

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants