-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 튜토리얼 페이지 제작 #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이 변경 사항은 주식 거래 튜토리얼 페이지를 위한 여러 컴포넌트를 추가합니다. Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (10)
src/components/common/tutorial/types/index.ts (1)
1-9: 타입 정의에 문서화 추가 필요인터페이스의 목적과 각 속성의 역할을 명확히 하기 위해 JSDoc 문서화가 필요합니다.
다음과 같이 문서화를 추가하는 것을 제안합니다:
+/** + * 튜토리얼의 개별 콘텐츠 항목을 정의하는 인터페이스 + */ export interface TutorialContentItem { + /** 콘텐츠의 부제목 */ subtitle: string; + /** 콘텐츠의 상세 설명 */ description: string; } +/** + * 튜토리얼의 단계별 구조를 정의하는 인터페이스 + */ export interface TutorialStep { + /** 튜토리얼 단계의 제목 */ title: string; + /** 해당 단계의 콘텐츠 항목 배열 */ content: TutorialContentItem[]; }src/components/common/tutorial/_components/tutorial-container.tsx (1)
7-20: 타입 안전성 개선 필요컴포넌트와 props에 대한 명시적인 타입 정의가 필요합니다.
다음과 같이 수정하는 것을 제안합니다:
-export default function TutorialContainer() { +interface TutorialModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function TutorialContainer(): React.ReactElement { const [isOpen, setIsOpen] = useState<boolean>(false); useEffect(() => { // ... 이전 코드 ... }, []); - return <TutorialModal isOpen={isOpen} onClose={() => setIsOpen(false)} />; + const handleClose = useCallback(() => { + setIsOpen(false); + }, []); + + return <TutorialModal isOpen={isOpen} onClose={handleClose} />; }src/app/search/[id]/_components/tutorial/tutorial-container.tsx (1)
20-22: 튜토리얼 완료 처리 로직 추가 필요모달이 닫힐 때 튜토리얼 완료 상태를 저장하는 로직을 추가하는 것이 좋습니다.
다음과 같이 개선해보세요:
+const handleClose = () => { + try { + localStorage.setItem("chart-tutorial-completed", "true"); + setIsOpen(false); + } catch (error) { + console.error('튜토리얼 완료 상태 저장 중 오류 발생:', error); + setIsOpen(false); + } +}; return ( - <ChartTutorialModal isOpen={isOpen} onClose={() => setIsOpen(false)} /> + <ChartTutorialModal isOpen={isOpen} onClose={handleClose} /> );src/components/common/tutorial/_components/tutorial-contents.tsx (1)
1-2: eslint-disable 범위 축소 필요전체 파일에 대한 eslint 비활성화보다는 특정 규칙에 대해서만 비활성화하는 것이 좋습니다.
-/* eslint-disable */ +/* eslint-disable react/no-array-index-as-key */src/app/search/[id]/_components/tutorial/constant/index.ts (1)
3-43: 타입 안정성 및 국제화 지원 개선 필요현재 구조에 다음과 같은 개선이 필요합니다:
- 문자열 리터럴 타입 정의
- 다국어 지원을 위한 구조 개선
- 콘텐츠 분리
다음과 같은 구조적 개선을 제안합니다:
- 타입 정의 개선:
type TutorialStepTitle = '차트 보기' | '거래하기' | '주문 관리'; type TutorialSubtitle = '기간 설정' | '이동평균선(MA)' | '매수/매도' | '실시간 호가' | '체결 내역' | '미체결 주문'; interface TutorialStep { title: TutorialStepTitle; content: { subtitle: TutorialSubtitle; description: string; }[]; }
- 다국어 지원을 위한 키 기반 구조:
const TUTORIAL_STEPS_KEYS = { CHART_VIEW: 'chart_view', TRADING: 'trading', ORDER_MANAGEMENT: 'order_management', } as const; const TUTORIAL_STEPS = [ { titleKey: TUTORIAL_STEPS_KEYS.CHART_VIEW, content: [ { subtitleKey: 'period_setting', descriptionKey: 'period_setting_desc', }, // ... ], }, // ... ];
- 별도의 파일로 콘텐츠 분리:
// tutorial-content.ko.ts export const TUTORIAL_CONTENT = { [TUTORIAL_STEPS_KEYS.CHART_VIEW]: { title: '차트 보기', period_setting: { subtitle: '기간 설정', description: '일/주/월 버튼으로 원하는 기간의 차트를 확인하세요.', }, // ... }, // ... };src/components/common/tutorial/constant/index.ts (1)
3-58: 다국어 지원을 위한 i18n 구현 검토 필요현재 튜토리얼 텍스트가 한국어로 하드코딩되어 있습니다. 향후 다국어 지원을 위해 i18n 구현을 고려해보시는 것이 좋을 것 같습니다.
예시 구현:
const TUTORIAL_STEPS_KO = { home: { title: "홈", content: [ { subtitle: "투자 현황", description: "전체 계좌의 수익률과 포트폴리오 현황을 한눈에 확인하세요." }, // ... ] }, // ... }; const TUTORIAL_STEPS: Record<string, typeof TUTORIAL_STEPS_KO> = { ko: TUTORIAL_STEPS_KO, // en: TUTORIAL_STEPS_EN, // ... };src/app/layout.tsx (1)
7-7: import 문 정렬 개선 필요관련된 import 문들을 함께 그룹화하여 가독성을 높이는 것이 좋습니다.
TutorialContainer를 다른 컴포넌트 import 문들과 함께 그룹화하는 것을 추천드립니다.import Toast from "@/components/common/toast/index"; -import TutorialContainer from "@/components/common/tutorial/_components/tutorial-container"; import MainContent from "@/components/main-content"; import NavBar from "@/components/nav-bar"; +import TutorialContainer from "@/components/common/tutorial/_components/tutorial-container";src/app/search/[id]/_components/tutorial/tutorial-modal.tsx (2)
29-29: 매직 스트링 상수화 필요"stock-tutorial-completed"와 같은 매직 스트링은 상수로 분리하여 관리하는 것이 좋습니다.
const TUTORIAL_STORAGE_KEY = { STOCK_COMPLETED: "stock-tutorial-completed" } as const;
56-82: 버튼 컴포넌트 추출 권장반복되는 버튼 컴포넌트를 별도의 컴포넌트로 추출하여 재사용성을 높이는 것이 좋습니다.
interface TutorialButtonProps { onClick: () => void; children: React.ReactNode; variant: 'primary' | 'secondary'; } function TutorialButton({ onClick, children, variant }: TutorialButtonProps) { const baseStyles = "px-8 py-4 text-16-600 rounded-md"; const variantStyles = { primary: "bg-green-500 text-white hover:bg-green-600", secondary: "border border-gray-300 text-gray-700 hover:bg-gray-50" }; return ( <button type="button" onClick={onClick} className={`${baseStyles} ${variantStyles[variant]}`} > {children} </button> ); }src/components/common/tutorial/_components/tutorial-modal.tsx (1)
37-41: 클라이언트 사이드 네비게이션 최적화 필요매 스텝 변경마다
router.push를 호출하면 불필요한 페이지 리로드가 발생할 수 있습니다.
shallow옵션을 사용하여 최적화하는 것을 고려해보세요:const handleNext = useCallback(() => { setCurrentStep((prev) => Math.min(prev + 1, TUTORIAL_STEPS.length - 1)); - router.push(getPathForStep(currentStep + 1)); + router.push(getPathForStep(currentStep + 1), undefined, { shallow: true }); }, [currentStep, router]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (11)
src/app/layout.tsx(2 hunks)src/app/search/[id]/_components/tutorial/constant/index.ts(1 hunks)src/app/search/[id]/_components/tutorial/tutorial-container.tsx(1 hunks)src/app/search/[id]/_components/tutorial/tutorial-modal.tsx(1 hunks)src/app/search/[id]/_components/tutorial/types/index.ts(1 hunks)src/app/search/[id]/page.tsx(2 hunks)src/components/common/tutorial/_components/tutorial-container.tsx(1 hunks)src/components/common/tutorial/_components/tutorial-contents.tsx(1 hunks)src/components/common/tutorial/_components/tutorial-modal.tsx(1 hunks)src/components/common/tutorial/constant/index.ts(1 hunks)src/components/common/tutorial/types/index.ts(1 hunks)
🔇 Additional comments (1)
src/app/search/[id]/page.tsx (1)
6-6: 적절한 튜토리얼 컨테이너 통합
튜토리얼 컨테이너가 페이지 최상단에 적절히 배치되어 있으며, 기존 컴포넌트들과의 통합이 깔끔하게 이루어졌습니다.
Also applies to: 63-63
| export interface TutorialContentItem { | ||
| subtitle: string; | ||
| description: string; | ||
| } | ||
|
|
||
| export interface TutorialStep { | ||
| title: string; | ||
| content: TutorialContentItem[]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
중복된 타입 정의 제거 필요
동일한 인터페이스가 src/components/common/tutorial/types/index.ts에도 정의되어 있습니다. 코드 중복을 피하고 유지보수성을 높이기 위해 다음과 같은 개선이 필요합니다:
- common 디렉토리의 타입 정의를 재사용
- 필요한 타입만 import하여 사용
다음과 같이 수정하는 것을 제안합니다:
-export interface TutorialContentItem {
- subtitle: string;
- description: string;
-}
-
-export interface TutorialStep {
- title: string;
- content: TutorialContentItem[];
-}
+import type { TutorialContentItem, TutorialStep } from '@/components/common/tutorial/types';
+
+export type { TutorialContentItem, TutorialStep };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface TutorialContentItem { | |
| subtitle: string; | |
| description: string; | |
| } | |
| export interface TutorialStep { | |
| title: string; | |
| content: TutorialContentItem[]; | |
| } | |
| import type { TutorialContentItem, TutorialStep } from '@/components/common/tutorial/types'; | |
| export type { TutorialContentItem, TutorialStep }; |
| useEffect(() => { | ||
| const hasCompletedTutorial = localStorage.getItem("nav-tutorial-completed"); | ||
| if (!hasCompletedTutorial) { | ||
| setIsOpen(true); | ||
| // 튜토리얼을 본 것으로 표시 | ||
| localStorage.setItem("nav-tutorial-completed", "true"); | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
localStorage 접근 및 useEffect 개선 필요
localStorage 접근 시 발생할 수 있는 예외 처리와 useEffect의 안전한 구현이 필요합니다.
다음과 같이 수정하는 것을 제안합니다:
+const TUTORIAL_COMPLETED_KEY = 'nav-tutorial-completed';
+
export default function TutorialContainer() {
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
- const hasCompletedTutorial = localStorage.getItem("nav-tutorial-completed");
- if (!hasCompletedTutorial) {
- setIsOpen(true);
- // 튜토리얼을 본 것으로 표시
- localStorage.setItem("nav-tutorial-completed", "true");
+ try {
+ const hasCompletedTutorial = localStorage.getItem(TUTORIAL_COMPLETED_KEY);
+ if (!hasCompletedTutorial) {
+ setIsOpen(true);
+ localStorage.setItem(TUTORIAL_COMPLETED_KEY, 'true');
+ }
+ } catch (error) {
+ console.error('튜토리얼 상태 저장 중 오류 발생:', error);
}
+
+ return () => {
+ // cleanup 로직이 필요한 경우 여기에 추가
+ };
}, []);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| const hasCompletedTutorial = localStorage.getItem("nav-tutorial-completed"); | |
| if (!hasCompletedTutorial) { | |
| setIsOpen(true); | |
| // 튜토리얼을 본 것으로 표시 | |
| localStorage.setItem("nav-tutorial-completed", "true"); | |
| } | |
| }, []); | |
| const TUTORIAL_COMPLETED_KEY = 'nav-tutorial-completed'; | |
| useEffect(() => { | |
| try { | |
| const hasCompletedTutorial = localStorage.getItem(TUTORIAL_COMPLETED_KEY); | |
| if (!hasCompletedTutorial) { | |
| setIsOpen(true); | |
| localStorage.setItem(TUTORIAL_COMPLETED_KEY, 'true'); | |
| } | |
| } catch (error) { | |
| console.error('튜토리얼 상태 저장 중 오류 발생:', error); | |
| } | |
| return () => { | |
| // cleanup 로직이 필요한 경우 여기에 추가 | |
| }; | |
| }, []); |
| useEffect(() => { | ||
| const hasCompletedTutorial = localStorage.getItem( | ||
| "chart-tutorial-completed", | ||
| ); | ||
| if (!hasCompletedTutorial) { | ||
| setIsOpen(true); | ||
| localStorage.setItem("chart-tutorial-completed", "true"); | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
localStorage 접근 및 상태 관리 개선 필요
다음과 같은 개선사항을 고려해주세요:
- localStorage 접근 시 예외 처리가 필요합니다
- 튜토리얼을 실제로 완료했을 때 localStorage에 저장하는 것이 더 적절할 수 있습니다
다음과 같이 개선해보세요:
useEffect(() => {
- const hasCompletedTutorial = localStorage.getItem(
- "chart-tutorial-completed",
- );
- if (!hasCompletedTutorial) {
- setIsOpen(true);
- localStorage.setItem("chart-tutorial-completed", "true");
- }
+ try {
+ const hasCompletedTutorial = localStorage.getItem(
+ "chart-tutorial-completed",
+ );
+ if (!hasCompletedTutorial) {
+ setIsOpen(true);
+ }
+ } catch (error) {
+ console.error('로컬 스토리지 접근 중 오류 발생:', error);
+ }
}, []);Committable suggestion skipped: line range outside the PR's diff.
| <div className="mb-20"> | ||
| <h3 className="text-20-500 mb-10 font-semibold text-gray-800"> | ||
| {step.title} | ||
| </h3> | ||
|
|
||
| <div className="space-y-20"> | ||
| {step.content.map((item, idx) => ( | ||
| <div key={`${step.title}-content-${idx}`}> | ||
| <h4 className="text-16-600 text-gray-700 mb-10">{item.subtitle}</h4> | ||
| <p className="text-14-500 text-gray-600">{item.description}</p> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
접근성 및 시맨틱 마크업 개선 필요
현재 구조에 다음과 같은 개선이 필요합니다:
- 시맨틱한 HTML 구조 사용
- 접근성 속성 추가
- 더 안정적인 키 생성 방식 적용
다음과 같이 개선해보세요:
- <div className="mb-20">
+ <section className="mb-20" aria-labelledby="tutorial-title">
- <h3 className="text-20-500 mb-10 font-semibold text-gray-800">
+ <h3 id="tutorial-title" className="text-20-500 mb-10 font-semibold text-gray-800">
{step.title}
</h3>
- <div className="space-y-20">
+ <ul className="space-y-20 list-none" role="list">
{step.content.map((item, idx) => (
- <div key={`${step.title}-content-${idx}`}>
+ <li key={`${step.title}-${item.subtitle}-${idx}`}>
<h4 className="text-16-600 text-gray-700 mb-10">{item.subtitle}</h4>
<p className="text-14-500 text-gray-600">{item.description}</p>
- </div>
+ </li>
))}
- </div>
- </div>
+ </ul>
+ </section>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="mb-20"> | |
| <h3 className="text-20-500 mb-10 font-semibold text-gray-800"> | |
| {step.title} | |
| </h3> | |
| <div className="space-y-20"> | |
| {step.content.map((item, idx) => ( | |
| <div key={`${step.title}-content-${idx}`}> | |
| <h4 className="text-16-600 text-gray-700 mb-10">{item.subtitle}</h4> | |
| <p className="text-14-500 text-gray-600">{item.description}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <section className="mb-20" aria-labelledby="tutorial-title"> | |
| <h3 id="tutorial-title" className="text-20-500 mb-10 font-semibold text-gray-800"> | |
| {step.title} | |
| </h3> | |
| <ul className="space-y-20 list-none" role="list"> | |
| {step.content.map((item, idx) => ( | |
| <li key={`${step.title}-${item.subtitle}-${idx}`}> | |
| <h4 className="text-16-600 text-gray-700 mb-10">{item.subtitle}</h4> | |
| <p className="text-14-500 text-gray-600">{item.description}</p> | |
| </li> | |
| ))} | |
| </ul> | |
| </section> |
| <button | ||
| type="button" | ||
| onClick={onClose} | ||
| className="rounded-full p-1 hover:bg-gray-100" | ||
| > | ||
| <X className="size-25 text-gray-500" /> | ||
| </button> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
접근성 개선 필요
버튼 요소들에 대한 접근성 속성이 누락되어 있습니다. 스크린 리더 사용자를 위한 aria 속성 추가가 필요합니다.
<button
type="button"
onClick={onClose}
+ aria-label="튜토리얼 닫기"
className="rounded-full p-1 hover:bg-gray-100"
>
<X className="size-25 text-gray-500" />
</button>
<button
type="button"
onClick={handlePrev}
+ aria-label="이전 단계로 이동"
className="flex items-center gap-4 rounded-md border border-gray-300 px-8 py-4 text-16-600 text-gray-700 hover:bg-gray-50"
>Also applies to: 56-64, 66-74, 75-82
| const handleComplete = useCallback(() => { | ||
| if (typeof window !== "undefined") { | ||
| localStorage.setItem("stock-tutorial-completed", "true"); | ||
| } | ||
| onClose(); | ||
| }, [onClose]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
localStorage 접근 시 예외 처리 필요
localStorage 접근 시 발생할 수 있는 예외 상황(private 모드 등)에 대한 처리가 필요합니다.
const handleComplete = useCallback(() => {
if (typeof window !== "undefined") {
+ try {
localStorage.setItem("stock-tutorial-completed", "true");
+ } catch (error) {
+ console.error("Failed to save tutorial status:", error);
+ }
}
onClose();
}, [onClose]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleComplete = useCallback(() => { | |
| if (typeof window !== "undefined") { | |
| localStorage.setItem("stock-tutorial-completed", "true"); | |
| } | |
| onClose(); | |
| }, [onClose]); | |
| const handleComplete = useCallback(() => { | |
| if (typeof window !== "undefined") { | |
| try { | |
| localStorage.setItem("stock-tutorial-completed", "true"); | |
| } catch (error) { | |
| console.error("Failed to save tutorial status:", error); | |
| } | |
| } | |
| onClose(); | |
| }, [onClose]); |
| const handleComplete = useCallback(() => { | ||
| if (typeof window !== "undefined") { | ||
| localStorage.setItem("nav-tutorial-completed", "true"); | ||
| } | ||
| onClose(); | ||
| }, [onClose]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
localStorage 접근 에러 처리 개선 필요
localStorage 접근 시 발생할 수 있는 예외 상황(예: 사용자가 쿠키를 차단한 경우)에 대한 처리가 필요합니다.
다음과 같이 개선해보세요:
const handleComplete = useCallback(() => {
if (typeof window !== "undefined") {
+ try {
localStorage.setItem("nav-tutorial-completed", "true");
+ } catch (error) {
+ console.error("Failed to save tutorial state:", error);
+ }
}
onClose();
}, [onClose]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleComplete = useCallback(() => { | |
| if (typeof window !== "undefined") { | |
| localStorage.setItem("nav-tutorial-completed", "true"); | |
| } | |
| onClose(); | |
| }, [onClose]); | |
| const handleComplete = useCallback(() => { | |
| if (typeof window !== "undefined") { | |
| try { | |
| localStorage.setItem("nav-tutorial-completed", "true"); | |
| } catch (error) { | |
| console.error("Failed to save tutorial state:", error); | |
| } | |
| } | |
| onClose(); | |
| }, [onClose]); |
| // 스텝에 따른 경로 반환 | ||
| const getPathForStep = (step: number) => { | ||
| switch (step) { | ||
| case 0: | ||
| return "/"; | ||
| case 1: | ||
| return "/search"; | ||
| case 2: | ||
| return "/my-account"; | ||
| case 3: | ||
| return "/portfolio"; | ||
| default: | ||
| return "/"; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
라우팅 로직 개선 필요
현재 구현된 getPathForStep 함수는 하드코딩된 경로를 사용하고 있어 유지보수가 어려울 수 있습니다. 상수로 분리하거나 설정 파일로 관리하는 것이 좋습니다.
다음과 같이 개선해보세요:
+ const STEP_PATHS = {
+ 0: "/",
+ 1: "/search",
+ 2: "/my-account",
+ 3: "/portfolio"
+ } as const;
const getPathForStep = (step: number) => {
- switch (step) {
- case 0:
- return "/";
- case 1:
- return "/search";
- case 2:
- return "/my-account";
- case 3:
- return "/portfolio";
- default:
- return "/";
- }
+ return STEP_PATHS[step as keyof typeof STEP_PATHS] || "/";
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 스텝에 따른 경로 반환 | |
| const getPathForStep = (step: number) => { | |
| switch (step) { | |
| case 0: | |
| return "/"; | |
| case 1: | |
| return "/search"; | |
| case 2: | |
| return "/my-account"; | |
| case 3: | |
| return "/portfolio"; | |
| default: | |
| return "/"; | |
| } | |
| }; | |
| const STEP_PATHS = { | |
| 0: "/", | |
| 1: "/search", | |
| 2: "/my-account", | |
| 3: "/portfolio" | |
| } as const; | |
| // 스텝에 따른 경로 반환 | |
| const getPathForStep = (step: number) => { | |
| return STEP_PATHS[step as keyof typeof STEP_PATHS] || "/"; | |
| }; |
| return ( | ||
| <BaseModal isOpen={isOpen} onClose={onClose}> | ||
| <div className="shadow-xl rounded-lg bg-white px-26 py-20"> | ||
| <div className="mb-15 flex items-center justify-between"> | ||
| <h2 className="text-24-600 text-gray-900">GrowFoilo 둘러보기</h2> | ||
| <button | ||
| type="button" | ||
| onClick={onClose} | ||
| className="rounded-full p-3 hover:bg-gray-100" | ||
| > | ||
| <X className="size-25 text-gray-500" /> | ||
| </button> | ||
| </div> | ||
|
|
||
| <TutorialContent step={TUTORIAL_STEPS[currentStep]} /> | ||
|
|
||
| <div className="flex items-center justify-between"> | ||
| <div className="text-16-600 text-gray-500"> | ||
| {currentStep + 1} / {TUTORIAL_STEPS.length} | ||
| </div> | ||
| <div className="flex gap-10"> | ||
| {currentStep > 0 && ( | ||
| <button | ||
| type="button" | ||
| onClick={handlePrev} | ||
| className="flex items-center gap-4 rounded-md border border-gray-300 px-8 py-4 text-16-600 text-gray-700 hover:bg-gray-50" | ||
| > | ||
| <ChevronLeft className="size-16" /> | ||
| 이전 | ||
| </button> | ||
| )} | ||
| {currentStep < TUTORIAL_STEPS.length - 1 ? ( | ||
| <button | ||
| type="button" | ||
| onClick={handleNext} | ||
| className="flex items-center gap-4 rounded-md bg-green-500 px-8 py-4 text-16-600 text-white hover:bg-green-600" | ||
| > | ||
| 다음 | ||
| <ChevronRight className="size-16" /> | ||
| </button> | ||
| ) : ( | ||
| <button | ||
| type="button" | ||
| onClick={handleComplete} | ||
| className="rounded-md bg-green-500 px-8 py-4 text-16-600 text-white hover:bg-green-600" | ||
| > | ||
| 시작하기 | ||
| </button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </BaseModal> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
접근성 개선 필요
모달 컴포넌트의 접근성이 부족합니다. 키보드 네비게이션과 스크린 리더 지원을 추가해야 합니다.
다음 개선사항을 적용해주세요:
- ARIA 레이블 추가:
- <div className="shadow-xl rounded-lg bg-white px-26 py-20">
+ <div
+ className="shadow-xl rounded-lg bg-white px-26 py-20"
+ role="dialog"
+ aria-labelledby="tutorial-title"
+ aria-describedby="tutorial-desc"
+ >- 키보드 네비게이션:
<button
type="button"
onClick={handleNext}
+ onKeyDown={(e) => e.key === 'Enter' && handleNext()}
className="flex items-center gap-4 rounded-md bg-green-500 px-8 py-4 text-16-600 text-white hover:bg-green-600"
>Committable suggestion skipped: line range outside the PR's diff.
#️⃣ 이슈
📝 작업 내용
간단한 웹사이트 이용방법 튜토리얼을 제작했습니다
📸 스크린샷
default.webm
✅ 체크 리스트
👩💻 공유 포인트 및 논의 사항
Summary by CodeRabbit