Skip to content

Create Week10 Mission1, 2 - #84

Open
endl24 wants to merge 2 commits into
mainfrom
endl24/Week10
Open

Create Week10 Mission1, 2#84
endl24 wants to merge 2 commits into
mainfrom
endl24/Week10

Conversation

@endl24

@endl24 endl24 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

📝 미션 번호

10주차 Misson 1

📋 구현 사항

  • 영화 검색 페이지 구현
  • 영화 검색 옵션 구현
  • 최적화 구현

배포 주소

📎 스크린샷

image image

✅ 체크리스트

  • Merge 하려는 브랜치가 올바르게 설정되어 있나요?
  • 로컬에서 실행했을 때 에러가 발생하지 않나요?
  • 불필요한 주석이 제거되었나요?
  • 코드 스타일이 일관적인가요?

🤔 질문 사항

Summary by CodeRabbit

릴리스 노트

  • New Features
    • React 성능 최적화 예제 프로젝트에 useCallback/useMemo 학습 화면 추가(소수 계산 유틸 포함)
    • TMDB 기반 영화 검색 앱 추가: 언어/성인 필터, 카드 결과 목록, 상세 화면(라우팅), 모달 정보 표시
  • Chores
    • React + TypeScript + Vite 개발 환경 기본 설정 추가(ESLint Flat Config, Tailwind 스타일 연동)
  • Documentation
    • 템플릿/설정 안내 README 추가

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

React/Vite 기반 두 신규 프로젝트가 추가됨. mission00useCallback·useMemo 훅 동작을 데모하는 앱이며, mission0102는 TMDB API를 이용한 영화 검색 앱으로 axios 클라이언트, useFetch 훅, 영화 도메인 타입, 공용 UI 컴포넌트, 필터·모달 상태 관리 HomePage, 상세 페이지 스텁을 포함함.

Changes

mission00: useCallback & useMemo 데모 앱

Layer / File(s) Summary
프로젝트 스캐폴딩 및 앱 엔트리
Week10/endl24/mission00/package.json, Week10/endl24/mission00/tsconfig.app.json, Week10/endl24/mission00/tsconfig.json, Week10/endl24/mission00/tsconfig.node.json, Week10/endl24/mission00/vite.config.ts, Week10/endl24/mission00/eslint.config.js, Week10/endl24/mission00/index.html, Week10/endl24/mission00/src/index.css, Week10/endl24/mission00/src/App.css, Week10/endl24/mission00/src/main.tsx, Week10/endl24/mission00/src/App.tsx, Week10/endl24/mission00/.gitignore, Week10/endl24/mission00/README.md
Vite + React + Tailwind 기반 프로젝트 구성 전체 추가. App은 UseMemoPage를 렌더링하며 UseCallbackPage는 import되지만 미사용. TypeScript 타입 검사, 린팅, 빌드 설정 포함.
useMemo 데모: 소수 계산 유틸 및 페이지
Week10/endl24/mission00/src/useMemo/utils/math.ts, Week10/endl24/mission00/src/useMemo/components/TextInput.tsx, Week10/endl24/mission00/src/useMemo/UseMemoPage.tsx
isPrime 소수 판별 및 findPrimeNumbers 배열 생성 유틸 추가. UseMemoPage는 limit 변경 시에만 useMemo로 소수 배열을 재계산하고 text 입력 변경은 컴포넌트만 재렌더링.
useCallback 데모: 메모이즈된 이벤트 핸들러
Week10/endl24/mission00/src/useCallback-memo/components/CountButton.tsx, Week10/endl24/mission00/src/useCallback-memo/components/TextInput.tsx, Week10/endl24/mission00/src/useCallback-memo/useCallbackPage.tsx
memo로 래핑된 CountButton(고정값 10 전달), TextInput, UseCallbackPage 추가. useCallback으로 정의된 handleText/handleIncreaseCount 핸들러를 통해 count·text 상태 분리 관리. 렌더링 로그로 최적화 확인 가능.

mission0102: TMDB 영화 검색 앱

Layer / File(s) Summary
프로젝트 스캐폴딩 및 앱 엔트리
Week10/endl24/mission0102/package.json, Week10/endl24/mission0102/tsconfig.app.json, Week10/endl24/mission0102/tsconfig.json, Week10/endl24/mission0102/tsconfig.node.json, Week10/endl24/mission0102/vite.config.ts, Week10/endl24/mission0102/eslint.config.js, Week10/endl24/mission0102/index.html, Week10/endl24/mission0102/src/index.css, Week10/endl24/mission0102/src/App.css, Week10/endl24/mission0102/src/main.tsx, Week10/endl24/mission0102/src/App.tsx, Week10/endl24/mission0102/.gitignore, Week10/endl24/mission0102/README.md, Week10/endl24/mission0102/vercel.json
Vite + React Router + Tailwind + axios 기반 프로젝트 구성 전체 추가. App은 BrowserRouter/Routes로 HomePage(/)와 MovieDetailPage(/movies/:movieId) 라우트 정의. Vercel 배포 설정(SPA 리라이트) 포함.
도메인 타입, API 클라이언트, useFetch 훅, 언어 상수
Week10/endl24/mission0102/src/types/movie.ts, Week10/endl24/mission0102/src/apis/axiosClient.ts, Week10/endl24/mission0102/src/hooks/useFetch.ts, Week10/endl24/mission0102/src/constants/movie.ts
Movie/MovieFilters/MovieResponse/MovieLanguage 타입 정의. axiosClient는 TMDB baseURL과 Bearer 토큰 헤더 설정. useFetch 제네릭 훅은 axiosClient.get 기반으로 data/error/isLoading 상태 관리 및 URL/option 변경 시 재요청. LANGUAGE_OPTIONS는 ko-KR, en-US, ja-JP 지원.
원자 UI 컴포넌트
Week10/endl24/mission0102/src/components/input.tsx, Week10/endl24/mission0102/src/components/SelectBox.tsx, Week10/endl24/mission0102/src/components/LanguageSelector.tsx
memo 기반 Input(텍스트 입력 + 선택적 onSubmit), SelectBox(체크박스 + 라벨), LanguageSelector(select 드롭다운) 원자 컴포넌트 추가. 각 컴포넌트는 displayName 설정 및 props 타입 정의.
디스플레이 컴포넌트
Week10/endl24/mission0102/src/components/MovieCard.tsx, Week10/endl24/mission0102/src/components/MovieList.tsx, Week10/endl24/mission0102/src/components/MovieFilter.tsx
MovieCard는 포스터 이미지(fallback 지원), vote_average 배지, 제목/발매일/언어, overview(line-clamp-3) 렌더링. MovieList는 빈 결과 상태 또는 반응형 그리드로 MovieCard 매핑. MovieFilter는 query/includeAdult/language 입력 폼으로 useState 상태 관리 후 onChange로 상위 전달.
Modal 컴포넌트
Week10/endl24/mission0102/src/components/Modal.tsx
isOpen 불린값에 따라 렌더 여부 결정. useEffect로 Escape 키 리스너 등록/정리. 오버레이 클릭으로 닫기, 내부 컨텐츠에서는 stopPropagation. displayName 설정.
HomePage: 필터·모달 상태 및 API 통합
Week10/endl24/mission0102/src/pages/HomePage.tsx
filters(query/include_adult/language) 상태를 useState로 관리. useFetch로 query 유무에 따라 /search/movie 또는 /discover/movie 엔드포인트 동적 선택. 로딩·에러·결과 상태 렌더링. 영화 클릭 시 selectedMovie·isModalOpen 상태 설정 후 Modal 오픈. 모달 내부는 backdrop/poster 우선순위 이미지, 평점(소수 1자리), 개봉일, 줄거리(기본값 포함), IMDb 검색 링크 표시.
MovieDetailPage: 상세 페이지 스텁
Week10/endl24/mission0102/src/pages/MovieDetailPage.tsx
useParams로 movieId를 읽어 화면에 표시하는 기본 페이지 구조. 추후 API 호출 및 상세 정보 렌더링 예정.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant MovieFilter
  participant HomePage
  participant useFetch
  participant axiosClient
  participant TMDB_API
  participant MovieList
  participant Modal

  User->>MovieFilter: 검색어/성인포함/언어 입력 후 검색
  MovieFilter->>HomePage: onChange(MovieFilters)
  HomePage->>useFetch: useFetch("/search/movie", { params: filters })
  useFetch->>axiosClient: axiosClient.get(url, option)
  axiosClient->>TMDB_API: GET /search/movie (Bearer token)
  TMDB_API-->>axiosClient: MovieResponse JSON
  axiosClient-->>useFetch: data
  useFetch-->>HomePage: { data, isLoading, error }
  HomePage->>MovieList: movies={data.results}
  MovieList-->>User: 영화 그리드 렌더링

  User->>User: 영화 카드 클릭
  HomePage->>Modal: isModalOpen=true, selectedMovie 설정
  Modal-->>User: 상세 정보(이미지, 평점, 개봉일, 줄거리, IMDb 링크) 표시
  User->>Modal: Escape 키 또는 배경 클릭
  Modal->>HomePage: onClose()
  HomePage->>HomePage: isModalOpen=false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 두 개의 미션, 쏙 담아왔네
useMemo로 소수를 계산하고
useCallback으로 핸들러 고정했죠
TMDB 영화 검색도 API 불러왔고
컴포넌트와 훅, 한 PR로 완성! 🎬✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR 설명이 필수 섹션을 포함하지만 구현 사항이 매우 간략하고 상세하지 않습니다. 구현 사항 섹션을 더 상세하게 작성해주세요. 각 기능의 구현 방식, 사용된 기술, 최적화 방법 등을 구체적으로 설명해주기 바랍니다.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주어진 요구사항과 일치하며, Week10의 Mission 1과 2를 명확히 다루고 있습니다.
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 endl24/Week10

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@endl24
endl24 requested a review from wantkdd June 16, 2026 17:54
@endl24 endl24 self-assigned this Jun 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (5)
Week10/endl24/mission00/src/useMemo/utils/math.ts (1)

3-4: ⚡ Quick win

소수 판별 루프 범위를 sqrt(num)까지 줄이세요.

Line 3-4의 전수 검사(i < num)는 비용이 큽니다. i * i <= num로 줄이면 동일 정확도로 연산량을 크게 줄일 수 있습니다.

제안 diff
 export const isPrime = (num: number) => {
   if (num < 2) return false;
-  for (let i = 2; i < num; i++) {
+  for (let i = 2; i * i <= num; i++) {
     if (num % i === 0) return false;
   }

   return true;
 };
🤖 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 `@Week10/endl24/mission00/src/useMemo/utils/math.ts` around lines 3 - 4, The
prime number checking loop in the function on lines 3-4 currently iterates from
2 up to num, which is computationally expensive. Change the loop condition from
`i < num` to `i * i <= num` to optimize the prime checking algorithm. This works
because if a number has a divisor greater than its square root, it must also
have a corresponding divisor less than its square root, so checking only up to
the square root is mathematically sufficient while significantly reducing
computation.
Week10/endl24/mission00/src/App.css (1)

1-1: ⚡ Quick win

@import "tailwindcss"; 중복 제거.

이미 index.css에서 Tailwind를 import하고 있으므로, App.css에서는 제거하세요. 단일 엔트리포인트에서만 Tailwind를 import하는 것이 표준 패턴입니다.

💬 제안된 변경
-@import "tailwindcss";
🤖 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 `@Week10/endl24/mission00/src/App.css` at line 1, Remove the `@import`
"tailwindcss"; statement from App.css because Tailwind is already imported in
the index.css file, which serves as the single entry point for the application.
Following the standard pattern, Tailwind CSS should only be imported once from
the main CSS file, not duplicated across multiple CSS files. Delete this import
line entirely from App.css.
Week10/endl24/mission00/src/main.tsx (1)

1-1: 💤 Low value

사용하지 않는 StrictMode import 제거.

StrictMode가 import되었으나 렌더링에서 사용되지 않습니다. 필요한 경우 실제로 감싸거나, 불필요하면 제거하세요.

💬 제안된 변경
-import { StrictMode } from 'react'
 import { createRoot } from 'react-dom/client'
🤖 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 `@Week10/endl24/mission00/src/main.tsx` at line 1, The StrictMode component is
being imported from React but is not actually used anywhere in the rendering
code. Remove StrictMode from the import statement at the top of the file where
React imports are defined. If StrictMode is needed for development purposes
(such as detecting unsafe lifecycle methods), wrap the root component with it
during rendering; otherwise, simply delete it from the import to keep the code
clean.
Week10/endl24/mission0102/src/types/movie.ts (1)

3-7: ⚡ Quick win

MovieFilters.language을 더 강한 타입으로 정의하세요.

현재 MovieFilterslanguage 필드가 string으로 선언되어 있습니다. MovieLanguage 타입으로 제한하면 타입 안정성이 향상됩니다. 또한 LANGUAGE_OPTIONS의 value 타입과 일치해야 합니다.

♻️ 제안된 수정
 export type MovieFilters = {
   query: string;
   include_adult: boolean;
-  language: string;
+  language: MovieLanguage;
 };
🤖 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 `@Week10/endl24/mission0102/src/types/movie.ts` around lines 3 - 7, The
language field in the MovieFilters type is currently declared as a generic
string, which lacks type safety. Change the language field type from string to
MovieLanguage to restrict it to valid language values and ensure consistency
with LANGUAGE_OPTIONS. This will improve type safety and prevent invalid
language values from being passed as filters.
Week10/endl24/mission0102/src/App.css (1)

1-1: ⚡ Quick win

Tailwind 전역 import를 한 곳으로만 유지하세요.

Week10/endl24/mission0102/src/index.css에서 이미 Tailwind를 import하고 있어, 여기까지 중복 import하면 CSS 중복 생성/번들 비대화 위험이 있습니다. App.css에서는 해당 라인을 제거하는 쪽이 안전합니다.

제안 diff
-@import "tailwindcss";
🤖 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 `@Week10/endl24/mission0102/src/App.css` at line 1, Remove the duplicate
Tailwind import statement from App.css since Tailwind CSS is already being
imported globally in index.css. Having the same import in multiple files can
cause CSS duplication and increase bundle size. Delete the `@import`
"tailwindcss"; line from App.css and rely solely on the import in index.css to
provide Tailwind styles throughout the application.
🤖 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 `@Week10/endl24/mission00/src/App.tsx`:
- Line 2: The `UseCallbackPage` component is imported in App.tsx but is not
being rendered anywhere in the component. Either remove the unused import if
only `UseMemoPage` is needed for the PR goal, or add rendering logic to display
`UseCallbackPage` (such as through tabs, routing, or sequential page rendering)
if both pages should be demonstrated.

In `@Week10/endl24/mission00/src/useCallback-memo/useCallbackPage.tsx`:
- Around line 9-15: The dependency arrays in both handleText and
handleIncreaseCount callbacks are including state values that cause the callback
references to change frequently, which undermines the memo optimization. For
handleText (lines 9-11), remove the text dependency and use an empty dependency
array to keep the callback reference stable. For handleIncreaseCount (lines
13-15), convert to functional state update by changing setCount(count + number)
to setCount(prevCount => prevCount + number) and use an empty dependency array,
which allows the callback to access the current state without needing to list
count as a dependency.

In `@Week10/endl24/mission0102/src/apis/axiosClient.ts`:
- Around line 3-7: The TMDB Bearer token is being exposed in the client-side
bundle through the VITE_* environment variable in the axiosClient configuration,
creating a security vulnerability where users can see and misuse the token.
Remove the Authorization header from the axiosClient axios.create call and
instead implement a server-side proxy or Backend For Frontend (BFF) pattern
where TMDB API calls are made from the server with the token attached securely
there. Update the baseURL to point to your backend endpoint instead of the TMDB
API directly, and have the server forward requests to TMDB with the token added
server-side.

In `@Week10/endl24/mission0102/src/components/MovieCard.tsx`:
- Line 20: In the MovieCard.tsx file at the img element with the
transition-transform class, there is a typo in the className attribute where
"case-in-out" should be "ease-in-out". Replace the incorrect "case-in-out"
string with "ease-in-out" to properly apply the Tailwind easing function to the
transform transition effect.

In `@Week10/endl24/mission0102/src/components/MovieFilter.tsx`:
- Around line 17-24: The search action in the handleSubmit function is currently
only triggered by button clicks, which prevents Enter key submission. Wrap the
form inputs (query, includeAdult, language fields) in an HTML form element with
an onSubmit handler that calls handleSubmit, and update the button to
type="submit". This ensures both button clicks and Enter key presses trigger the
same submission logic. Apply this same structure change to both the primary form
location and the sibling form location at lines 62-68.

In `@Week10/endl24/mission0102/src/hooks/useFetch.ts`:
- Around line 10-23: The useFetch hook has two issues: race conditions where
previous requests can overwrite newer results, and error state stickiness where
error messages persist across new requests. To fix this, clear the error state
by calling setError with null or empty string at the start of each fetch in the
fetchData function, immediately after setIsLoading(true). Additionally,
implement request cancellation using AbortController to prevent late-arriving
responses from overwriting current results: create an AbortController instance,
pass its signal to the axiosClient.get call via the option parameter, and cancel
the previous request in the useEffect cleanup function if a new request is
initiated. This ensures that only the most recent request updates the component
state.

In `@Week10/endl24/mission0102/src/pages/HomePage.tsx`:
- Around line 18-20: The early return pattern in the error handling block (lines
18-20) prevents the filter UI from rendering when an error occurs, blocking user
retry attempts. Remove the early return that displays only the error message.
Instead, restructure the component to always render the filter UI and display
the error message inline (such as an alert or error banner) above or within the
filter section. This allows users to modify search conditions and retry without
the filter becoming inaccessible during error states.

In `@Week10/endl24/mission0102/src/types/movie.ts`:
- Line 1: The MovieLanguage type definition contains a typo where the Japanese
language code is specified as "ja=JP" instead of "ja-JP". Fix this by replacing
the equals sign with a hyphen in the MovieLanguage union type to ensure
consistency with the constant definitions in src/constants/movie.ts and prevent
runtime type mismatches.
- Around line 9-25: The `poster_path` field in the Movie type is defined as
non-nullable (string), but the TMDB API can return null for this field. Update
the `poster_path` field declaration in the Movie type to make it nullable by
changing it from `string` to `string | null` to accurately reflect the actual
API response structure.

---

Nitpick comments:
In `@Week10/endl24/mission00/src/App.css`:
- Line 1: Remove the `@import` "tailwindcss"; statement from App.css because
Tailwind is already imported in the index.css file, which serves as the single
entry point for the application. Following the standard pattern, Tailwind CSS
should only be imported once from the main CSS file, not duplicated across
multiple CSS files. Delete this import line entirely from App.css.

In `@Week10/endl24/mission00/src/main.tsx`:
- Line 1: The StrictMode component is being imported from React but is not
actually used anywhere in the rendering code. Remove StrictMode from the import
statement at the top of the file where React imports are defined. If StrictMode
is needed for development purposes (such as detecting unsafe lifecycle methods),
wrap the root component with it during rendering; otherwise, simply delete it
from the import to keep the code clean.

In `@Week10/endl24/mission00/src/useMemo/utils/math.ts`:
- Around line 3-4: The prime number checking loop in the function on lines 3-4
currently iterates from 2 up to num, which is computationally expensive. Change
the loop condition from `i < num` to `i * i <= num` to optimize the prime
checking algorithm. This works because if a number has a divisor greater than
its square root, it must also have a corresponding divisor less than its square
root, so checking only up to the square root is mathematically sufficient while
significantly reducing computation.

In `@Week10/endl24/mission0102/src/App.css`:
- Line 1: Remove the duplicate Tailwind import statement from App.css since
Tailwind CSS is already being imported globally in index.css. Having the same
import in multiple files can cause CSS duplication and increase bundle size.
Delete the `@import` "tailwindcss"; line from App.css and rely solely on the
import in index.css to provide Tailwind styles throughout the application.

In `@Week10/endl24/mission0102/src/types/movie.ts`:
- Around line 3-7: The language field in the MovieFilters type is currently
declared as a generic string, which lacks type safety. Change the language field
type from string to MovieLanguage to restrict it to valid language values and
ensure consistency with LANGUAGE_OPTIONS. This will improve type safety and
prevent invalid language values from being passed as filters.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e4d9fb2-3e51-4e3e-8959-e76273ff58dc

📥 Commits

Reviewing files that changed from the base of the PR and between 8a6238e and 38dd470.

⛔ Files ignored due to path filters (13)
  • Week10/endl24/mission00/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • Week10/endl24/mission00/public/favicon.svg is excluded by !**/*.svg
  • Week10/endl24/mission00/public/icons.svg is excluded by !**/*.svg
  • Week10/endl24/mission00/src/assets/hero.png is excluded by !**/*.png
  • Week10/endl24/mission00/src/assets/react.svg is excluded by !**/*.svg
  • Week10/endl24/mission00/src/assets/vite.svg is excluded by !**/*.svg
  • Week10/endl24/mission0102/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • Week10/endl24/mission0102/public/favicon.svg is excluded by !**/*.svg
  • Week10/endl24/mission0102/public/icons.svg is excluded by !**/*.svg
  • Week10/endl24/mission0102/src/assets/hero.png is excluded by !**/*.png
  • Week10/endl24/mission0102/src/assets/react.svg is excluded by !**/*.svg
  • Week10/endl24/mission0102/src/assets/vite.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • Week10/endl24/mission00/.gitignore
  • Week10/endl24/mission00/README.md
  • Week10/endl24/mission00/eslint.config.js
  • Week10/endl24/mission00/index.html
  • Week10/endl24/mission00/package.json
  • Week10/endl24/mission00/src/App.css
  • Week10/endl24/mission00/src/App.tsx
  • Week10/endl24/mission00/src/index.css
  • Week10/endl24/mission00/src/main.tsx
  • Week10/endl24/mission00/src/useCallback-memo/components/CountButton.tsx
  • Week10/endl24/mission00/src/useCallback-memo/components/TextInput.tsx
  • Week10/endl24/mission00/src/useCallback-memo/useCallbackPage.tsx
  • Week10/endl24/mission00/src/useMemo/UseMemoPage.tsx
  • Week10/endl24/mission00/src/useMemo/components/TextInput.tsx
  • Week10/endl24/mission00/src/useMemo/utils/math.ts
  • Week10/endl24/mission00/tsconfig.app.json
  • Week10/endl24/mission00/tsconfig.json
  • Week10/endl24/mission00/tsconfig.node.json
  • Week10/endl24/mission00/vite.config.ts
  • Week10/endl24/mission0102/.gitignore
  • Week10/endl24/mission0102/README.md
  • Week10/endl24/mission0102/eslint.config.js
  • Week10/endl24/mission0102/index.html
  • Week10/endl24/mission0102/package.json
  • Week10/endl24/mission0102/src/App.css
  • Week10/endl24/mission0102/src/App.tsx
  • Week10/endl24/mission0102/src/apis/axiosClient.ts
  • Week10/endl24/mission0102/src/components/LanguageSelector.tsx
  • Week10/endl24/mission0102/src/components/MovieCard.tsx
  • Week10/endl24/mission0102/src/components/MovieFilter.tsx
  • Week10/endl24/mission0102/src/components/MovieList.tsx
  • Week10/endl24/mission0102/src/components/SelectBox.tsx
  • Week10/endl24/mission0102/src/components/input.tsx
  • Week10/endl24/mission0102/src/constants/movie.ts
  • Week10/endl24/mission0102/src/hooks/useFetch.ts
  • Week10/endl24/mission0102/src/index.css
  • Week10/endl24/mission0102/src/main.tsx
  • Week10/endl24/mission0102/src/pages/HomePage.tsx
  • Week10/endl24/mission0102/src/types/movie.ts
  • Week10/endl24/mission0102/tsconfig.app.json
  • Week10/endl24/mission0102/tsconfig.json
  • Week10/endl24/mission0102/tsconfig.node.json
  • Week10/endl24/mission0102/vite.config.ts

@@ -0,0 +1,14 @@
import "./App.css";
import UseCallbackPage from "./useCallback-memo/useCallbackPage";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

사용하지 않는 UseCallbackPage import 제거 또는 렌더링 추가.

UseCallbackPage가 import되었으나 렌더링되지 않습니다. PR 목표에 따라:

  • 두 페이지를 모두 데모하려면, 렌더링 구조를 변경하여 추가하세요 (예: 탭, 라우팅, 또는 순차 페이지).
  • UseMemoPage만 필요하면, 불필요한 import를 제거하세요.
🤖 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 `@Week10/endl24/mission00/src/App.tsx` at line 2, The `UseCallbackPage`
component is imported in App.tsx but is not being rendered anywhere in the
component. Either remove the unused import if only `UseMemoPage` is needed for
the PR goal, or add rendering logic to display `UseCallbackPage` (such as
through tabs, routing, or sequential page rendering) if both pages should be
demonstrated.

Comment on lines +9 to +15
const handleText = useCallback((text: string) => {
setText(text);
}, [text]);

const handleIncreaseCount = useCallback((number: number) => {
setCount(count + number);
}, [count]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

useCallback 의존성 설정 때문에 데모 의도가 약해집니다.

Line 9-11과 Line 13-15에서 상태값을 직접 의존시키면 콜백 레퍼런스가 자주 바뀌어 memo 자식 최적화 효과가 흐려집니다. setCount는 함수형 업데이트를 사용하고 두 핸들러 모두 고정 deps로 두는 편이 안전합니다.

수정 제안
-  const handleText = useCallback((text: string) => {
-    setText(text);
-  }, [text]);
+  const handleText = useCallback((nextText: string) => {
+    setText(nextText);
+  }, []);

-  const handleIncreaseCount = useCallback((number: number) => {
-    setCount(count + number);
-  }, [count]);
+  const handleIncreaseCount = useCallback((number: number) => {
+    setCount((prev) => prev + number);
+  }, []);
🤖 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 `@Week10/endl24/mission00/src/useCallback-memo/useCallbackPage.tsx` around
lines 9 - 15, The dependency arrays in both handleText and handleIncreaseCount
callbacks are including state values that cause the callback references to
change frequently, which undermines the memo optimization. For handleText (lines
9-11), remove the text dependency and use an empty dependency array to keep the
callback reference stable. For handleIncreaseCount (lines 13-15), convert to
functional state update by changing setCount(count + number) to
setCount(prevCount => prevCount + number) and use an empty dependency array,
which allows the callback to access the current state without needing to list
count as a dependency.

Comment on lines +3 to +7
export const axiosClient = axios.create({
baseURL: "https://api.themoviedb.org/3",
headers:{
Authorization: `Bearer ${import.meta.env.VITE_TMDB_TOKEN}`,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

클라이언트 번들에 TMDB Bearer 토큰이 노출됩니다.

VITE_* 환경변수는 클라이언트에 주입되므로, 현재 방식은 토큰이 사용자에게 공개됩니다. 악의적 재사용으로 쿼터 소진/오남용이 가능합니다. TMDB 호출은 서버(또는 BFF)에서 토큰을 붙이는 구조로 분리하는 것이 안전합니다.

🔐 제안 수정안
 export const axiosClient = axios.create({
-    baseURL: "https://api.themoviedb.org/3",
-    headers:{
-        Authorization: `Bearer ${import.meta.env.VITE_TMDB_TOKEN}`,
-    }
+    // 서버 프록시(/api/tmdb) 뒤로 이동하고,
+    // 서버에서만 TMDB 토큰을 주입하세요.
+    baseURL: "/api/tmdb",
 })
📝 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.

Suggested change
export const axiosClient = axios.create({
baseURL: "https://api.themoviedb.org/3",
headers:{
Authorization: `Bearer ${import.meta.env.VITE_TMDB_TOKEN}`,
}
export const axiosClient = axios.create({
// 서버 프록시(/api/tmdb) 뒤로 이동하고,
// 서버에서만 TMDB 토큰을 주입하세요.
baseURL: "/api/tmdb",
})
🤖 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 `@Week10/endl24/mission0102/src/apis/axiosClient.ts` around lines 3 - 7, The
TMDB Bearer token is being exposed in the client-side bundle through the VITE_*
environment variable in the axiosClient configuration, creating a security
vulnerability where users can see and misuse the token. Remove the Authorization
header from the axiosClient axios.create call and instead implement a
server-side proxy or Backend For Frontend (BFF) pattern where TMDB API calls are
made from the server with the token attached securely there. Update the baseURL
to point to your backend endpoint instead of the TMDB API directly, and have the
server forward requests to TMDB with the token added server-side.

Comment thread Week10/endl24/mission0102/src/components/MovieCard.tsx Outdated
Comment on lines +17 to +24
const handleSubmit = () => {
const filters: MovieFilters = {
query,
include_adult: includeAdult,
language,
};
onChange(filters);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

검색 액션이 버튼 클릭에만 묶여 있어 Enter 키 제출이 동작하지 않습니다.

검색 UI는 <form onSubmit>로 구성해 키보드 제출(Enter)도 동일 동작 경로를 타도록 맞추는 것이 안전합니다.

⌨️ 제안 수정안
-import { useState } from "react";
+import { useState, type FormEvent } from "react";
@@
-  const handleSubmit = () => {
+  const handleSubmit = (e: FormEvent) => {
+    e.preventDefault();
     const filters: MovieFilters = {
       query,
       include_adult: includeAdult,
       language,
     };
     onChange(filters);
   };

   return (
-    <div className="transform rounded-2xl border border-gray-300 bg-white p-6 shadow-xl transition-all hover:shadow-2xl">
+    <form
+      onSubmit={handleSubmit}
+      className="transform rounded-2xl border border-gray-300 bg-white p-6 shadow-xl transition-all hover:shadow-2xl"
+    >
@@
-          <button
-            onClick={handleSubmit}
+          <button
+            type="submit"
             className="rounded-lg bg-blue-500 px-6 py-2 font-semibold text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
           >
             영화 검색
           </button>
@@
-    </div>
+    </form>
   );

Also applies to: 62-68

🤖 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 `@Week10/endl24/mission0102/src/components/MovieFilter.tsx` around lines 17 -
24, The search action in the handleSubmit function is currently only triggered
by button clicks, which prevents Enter key submission. Wrap the form inputs
(query, includeAdult, language fields) in an HTML form element with an onSubmit
handler that calls handleSubmit, and update the button to type="submit". This
ensures both button clicks and Enter key presses trigger the same submission
logic. Apply this same structure change to both the primary form location and
the sibling form location at lines 62-68.

Comment on lines +10 to +23
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const { data } = await axiosClient.get(url, {
...option,
});

setData(data);
} catch (err) {
setError("데이터를 가져오는데 에러가 발생했습니다.");
} finally {
setIsLoading(false);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

요청 경합과 에러 상태 고착으로 결과 화면이 틀어질 수 있습니다.

이전 요청이 늦게 완료되면 최신 검색 결과를 덮어쓸 수 있고, 새 요청 전에 error를 초기화하지 않아 에러 UI가 계속 남을 수 있습니다.

🛠 제안 수정안
   useEffect(() => {
+    let cancelled = false;
     const fetchData = async () => {
       setIsLoading(true);
+      setError(null);
       try {
         const { data } = await axiosClient.get(url, {
           ...option,
         });
-
-        setData(data);
+        if (!cancelled) setData(data);
       } catch (err) {
-        setError("데이터를 가져오는데 에러가 발생했습니다.");
+        if (!cancelled) {
+          setError("데이터를 가져오는데 에러가 발생했습니다.");
+        }
       } finally {
-        setIsLoading(false);
+        if (!cancelled) setIsLoading(false);
       }
     };

     fetchData();
+    return () => {
+      cancelled = true;
+    };

   }, [url, JSON.stringify(option)]);

Also applies to: 28-28

🤖 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 `@Week10/endl24/mission0102/src/hooks/useFetch.ts` around lines 10 - 23, The
useFetch hook has two issues: race conditions where previous requests can
overwrite newer results, and error state stickiness where error messages persist
across new requests. To fix this, clear the error state by calling setError with
null or empty string at the start of each fetch in the fetchData function,
immediately after setIsLoading(true). Additionally, implement request
cancellation using AbortController to prevent late-arriving responses from
overwriting current results: create an AbortController instance, pass its signal
to the axiosClient.get call via the option parameter, and cancel the previous
request in the useEffect cleanup function if a new request is initiated. This
ensures that only the most recent request updates the component state.

Comment on lines +18 to +20
if (error) {
return <div>{error}</div>;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

에러 발생 시 필터 UI가 사라져 사용자가 재시도할 수 없습니다.

현재 조기 반환 때문에 에러 상태에서 검색 조건 변경 자체가 막힙니다. 필터는 항상 렌더링하고, 에러는 인라인으로 표시해 재시도 가능 상태를 유지하세요.

🔁 제안 수정안
-  if (error) {
-    return <div>{error}</div>;
-  }
-
   return (
     <div className="container">
       <MovieFilter onChange={setFilters} />
+      {error && (
+        <div className="mt-4 rounded-md bg-red-50 p-3 text-sm text-red-700">
+          {error}
+        </div>
+      )}
       
       {isLoading ? (
         <div>로딩 중 입니다...</div>
       ) : (
         <MovieList movies={data?.results || []} />
       )}
     </div>
   );

Also applies to: 23-30

🤖 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 `@Week10/endl24/mission0102/src/pages/HomePage.tsx` around lines 18 - 20, The
early return pattern in the error handling block (lines 18-20) prevents the
filter UI from rendering when an error occurs, blocking user retry attempts.
Remove the early return that displays only the error message. Instead,
restructure the component to always render the filter UI and display the error
message inline (such as an alert or error banner) above or within the filter
section. This allows users to modify search conditions and retry without the
filter becoming inaccessible during error states.

@@ -0,0 +1,32 @@
export type MovieLanguage = "ko-KR" | "en-US" | "ja=JP";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

MovieLanguage 타입의 일본어 코드가 잘못되었습니다.

Line 1의 "ja=JP""ja-JP"로 수정되어야 합니다. 현재 src/constants/movie.ts에서는 올바르게 "ja-JP"로 정의되어 있으나, 타입 정의에서 다르게 되어 있어 런타임 타입 불일치가 발생할 수 있습니다.

🔧 수정 제안
-export type MovieLanguage = "ko-KR" | "en-US" | "ja=JP";
+export type MovieLanguage = "ko-KR" | "en-US" | "ja-JP";
📝 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.

Suggested change
export type MovieLanguage = "ko-KR" | "en-US" | "ja=JP";
export type MovieLanguage = "ko-KR" | "en-US" | "ja-JP";
🤖 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 `@Week10/endl24/mission0102/src/types/movie.ts` at line 1, The MovieLanguage
type definition contains a typo where the Japanese language code is specified as
"ja=JP" instead of "ja-JP". Fix this by replacing the equals sign with a hyphen
in the MovieLanguage union type to ensure consistency with the constant
definitions in src/constants/movie.ts and prevent runtime type mismatches.

Comment on lines +9 to +25
export type Movie = {
adult: boolean;
backdrop_path: string | null;
genre_ids: number[];
id: number;
title: string;
original_language: string;
original_title: string;
overview: string;
popularity: number;
poster_path: string;
release_date: string;
softcore: boolean;
video: boolean;
vote_average: number;
vote_count: number;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Movie 타입에서 poster_path 필드가 non-nullable로 정의되어 있습니다.

Line 19에서 poster_path: string으로 정의되었으나, TMDB API 응답에서는 이 필드가 null일 수 있습니다. MovieCard 컴포넌트에서 fallback 처리가 있더라도, 타입 정의는 실제 API 응답을 반영해야 합니다.

🔧 수정 제안
 export type Movie = {
   adult: boolean;
   backdrop_path: string | null;
   genre_ids: number[];
   id: number;
   title: string;
   original_language: string;
   original_title: string;
   overview: string;
   popularity: number;
-  poster_path: string;
+  poster_path: string | null;
   release_date: string;
   softcore: boolean;
   video: boolean;
   vote_average: number;
   vote_count: number;
 };
📝 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.

Suggested change
export type Movie = {
adult: boolean;
backdrop_path: string | null;
genre_ids: number[];
id: number;
title: string;
original_language: string;
original_title: string;
overview: string;
popularity: number;
poster_path: string;
release_date: string;
softcore: boolean;
video: boolean;
vote_average: number;
vote_count: number;
};
export type Movie = {
adult: boolean;
backdrop_path: string | null;
genre_ids: number[];
id: number;
title: string;
original_language: string;
original_title: string;
overview: string;
popularity: number;
poster_path: string | null;
release_date: string;
softcore: boolean;
video: boolean;
vote_average: number;
vote_count: number;
};
🤖 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 `@Week10/endl24/mission0102/src/types/movie.ts` around lines 9 - 25, The
`poster_path` field in the Movie type is defined as non-nullable (string), but
the TMDB API can return null for this field. Update the `poster_path` field
declaration in the Movie type to make it nullable by changing it from `string`
to `string | null` to accurately reflect the actual API response structure.

@endl24 endl24 changed the title Create Week10 Mission1 Create Week10 Mission1, 2 Jun 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
Week10/endl24/mission0102/src/App.tsx (1)

8-13: ⚡ Quick win

잘못된 경로 진입 시 빈 화면이 나올 수 있어 * 폴백 라우트를 추가해 주세요.

현재 라우트 테이블에는 미일치 경로 처리(*)가 없어, 오타 URL 접근 시 아무 콘텐츠도 렌더링되지 않습니다.

제안 diff
 import { BrowserRouter, Routes, Route } from "react-router-dom";
 import HomePage from "./pages/HomePage";
 import MovieDetailPage from "./pages/MovieDetailPage"; // 방금 만든 페이지
+import { Navigate } from "react-router-dom";

 function App() {
   return (
     <BrowserRouter>
       <Routes>
         {/* 메인 페이지 */}
         <Route path="/" element={<HomePage />} />
         {/* 영화 상세 페이지 동적 라우팅 */}
         <Route path="/movies/:movieId" element={<MovieDetailPage />} />
+        {/* 잘못된 경로 폴백 */}
+        <Route path="*" element={<Navigate to="/" replace />} />
       </Routes>
     </BrowserRouter>
   );
 }
🤖 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 `@Week10/endl24/mission0102/src/App.tsx` around lines 8 - 13, The Routes
component in App.tsx lacks a fallback route for handling unmatched paths,
resulting in a blank screen when users navigate to invalid URLs. Add a new Route
with path="*" after the existing routes (the HomePage and MovieDetailPage
routes) to catch all undefined paths and render an appropriate component such as
a NotFound or 404 page, providing proper user feedback instead of a blank
screen.
🤖 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 `@Week10/endl24/mission0102/src/components/Modal.tsx`:
- Around line 21-31: The modal component is missing critical accessibility
attributes that assistive technologies rely on to properly identify and describe
the dialog. Add `role="dialog"` and `aria-modal="true"` to the inner div element
(the one with the className containing "fixed inset-0 z-50 flex items-center
justify-center...") that wraps the children content. Additionally, add either an
`aria-label` attribute with a descriptive text or an `aria-labelledby` attribute
pointing to an element ID that labels the modal, so screen readers can properly
announce the purpose of the dialog to users.

In `@Week10/endl24/mission0102/src/components/MovieCard.tsx`:
- Around line 14-17: The div element in MovieCard component that has the onClick
handler is not keyboard accessible, preventing keyboard users from interacting
with it using Enter or Space keys. Either convert the div element to a button
element to inherit native keyboard accessibility, or if keeping it as a div, add
the accessibility attributes role="button", tabIndex="0", and an onKeyDown
handler that triggers the onClick callback when Enter or Space keys are pressed.
This ensures the card is fully accessible to keyboard users.

In `@Week10/endl24/mission0102/src/pages/MovieDetailPage.tsx`:
- Around line 3-11: The MovieDetailPage is unreachable because the onMovieClick
handler in HomePage.tsx only opens a modal and does not perform any navigation.
Modify the onMovieClick handler to navigate to the detail page route using the
pattern `/movies/${movie.id}` (using React Router's useNavigate hook), or
alternatively wrap the MovieCard component with a Link element that points to
this route. This will connect the user interaction flow to the detail page so
the movieId parameter extracted from the URL can be properly utilized.

---

Nitpick comments:
In `@Week10/endl24/mission0102/src/App.tsx`:
- Around line 8-13: The Routes component in App.tsx lacks a fallback route for
handling unmatched paths, resulting in a blank screen when users navigate to
invalid URLs. Add a new Route with path="*" after the existing routes (the
HomePage and MovieDetailPage routes) to catch all undefined paths and render an
appropriate component such as a NotFound or 404 page, providing proper user
feedback instead of a blank screen.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01c6ae8a-9d31-40e8-a48d-898ec0f84d80

📥 Commits

Reviewing files that changed from the base of the PR and between 38dd470 and d35221d.

⛔ Files ignored due to path filters (1)
  • Week10/endl24/mission0102/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • Week10/endl24/mission0102/.gitignore
  • Week10/endl24/mission0102/package.json
  • Week10/endl24/mission0102/src/App.tsx
  • Week10/endl24/mission0102/src/components/LanguageSelector.tsx
  • Week10/endl24/mission0102/src/components/Modal.tsx
  • Week10/endl24/mission0102/src/components/MovieCard.tsx
  • Week10/endl24/mission0102/src/components/MovieFilter.tsx
  • Week10/endl24/mission0102/src/components/MovieList.tsx
  • Week10/endl24/mission0102/src/components/SelectBox.tsx
  • Week10/endl24/mission0102/src/components/input.tsx
  • Week10/endl24/mission0102/src/pages/HomePage.tsx
  • Week10/endl24/mission0102/src/pages/MovieDetailPage.tsx
  • Week10/endl24/mission0102/vercel.json
✅ Files skipped from review due to trivial changes (2)
  • Week10/endl24/mission0102/vercel.json
  • Week10/endl24/mission0102/.gitignore
🚧 Files skipped from review as they are similar to previous changes (4)
  • Week10/endl24/mission0102/src/components/LanguageSelector.tsx
  • Week10/endl24/mission0102/src/components/SelectBox.tsx
  • Week10/endl24/mission0102/package.json
  • Week10/endl24/mission0102/src/components/MovieFilter.tsx

Comment on lines +21 to +31
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"
onClick={onClose}
>
<div
className="w-full max-w-md transform overflow-hidden rounded-3xl bg-white shadow-2xl transition-all"
onClick={(e) => e.stopPropagation()}
>
{children}
</div>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

모달 접근성 시맨틱이 빠져 있습니다.

현재 모달 컨테이너에 role="dialog"/aria-modal="true"가 없어 보조기기에서 대화상자로 인식되지 않습니다. 최소한 해당 속성과 라벨(aria-label 또는 aria-labelledby)을 추가해 주세요.

🤖 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 `@Week10/endl24/mission0102/src/components/Modal.tsx` around lines 21 - 31, The
modal component is missing critical accessibility attributes that assistive
technologies rely on to properly identify and describe the dialog. Add
`role="dialog"` and `aria-modal="true"` to the inner div element (the one with
the className containing "fixed inset-0 z-50 flex items-center
justify-center...") that wraps the children content. Additionally, add either an
`aria-label` attribute with a descriptive text or an `aria-labelledby` attribute
pointing to an element ID that labels the modal, so screen readers can properly
announce the purpose of the dialog to users.

Comment on lines +14 to +17
<div
onClick={onClick}
className="group flex cursor-pointer flex-col overflow-hidden rounded-3xl border-2 border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-2 hover:border-violet-400 hover:shadow-[0_20px_40px_-15px_rgba(139,92,246,0.25)]"
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

키보드 접근이 불가능한 클릭 카드입니다.

Line 14의 div 클릭 처리만으로는 키보드 사용자(Enter/Space) 경로가 막힙니다. 카드 루트를 button으로 바꾸거나 role="button", tabIndex, onKeyDown을 추가해 접근 경로를 보장해 주세요.

제안 diff
-    <div
-      onClick={onClick}
+    <button
+      type="button"
+      onClick={onClick}
       className="group flex cursor-pointer flex-col overflow-hidden rounded-3xl border-2 border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-2 hover:border-violet-400 hover:shadow-[0_20px_40px_-15px_rgba(139,92,246,0.25)]"
     >
@@
-    </div>
+    </button>
📝 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.

Suggested change
<div
onClick={onClick}
className="group flex cursor-pointer flex-col overflow-hidden rounded-3xl border-2 border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-2 hover:border-violet-400 hover:shadow-[0_20px_40px_-15px_rgba(139,92,246,0.25)]"
>
<button
type="button"
onClick={onClick}
className="group flex cursor-pointer flex-col overflow-hidden rounded-3xl border-2 border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-2 hover:border-violet-400 hover:shadow-[0_20px_40px_-15px_rgba(139,92,246,0.25)]"
>
{/* card content */}
</button>
🤖 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 `@Week10/endl24/mission0102/src/components/MovieCard.tsx` around lines 14 - 17,
The div element in MovieCard component that has the onClick handler is not
keyboard accessible, preventing keyboard users from interacting with it using
Enter or Space keys. Either convert the div element to a button element to
inherit native keyboard accessibility, or if keeping it as a div, add the
accessibility attributes role="button", tabIndex="0", and an onKeyDown handler
that triggers the onClick callback when Enter or Space keys are pressed. This
ensures the card is fully accessible to keyboard users.

Comment on lines +3 to +11
export default function MovieDetailPage() {
// URL에서 :movieId 파라미터를 뽑아옵니다.
const { movieId } = useParams();

return (
<div className="flex h-screen flex-col items-center justify-center gap-4">
<h1 className="text-3xl font-extrabold text-zinc-800">🎬 영화 상세 페이지</h1>
<p className="text-xl font-bold text-violet-600">선택된 영화 ID: {movieId}</p>
<p className="text-zinc-500">이곳에 향후 영화 상세 정보가 렌더링됩니다.</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

MovieDetailPage 진입 경로가 현재 UI 플로우에 연결되지 않아 사실상 도달 불가능합니다.

Week10/endl24/mission0102/src/pages/HomePage.tsx의 클릭 동작은 모달만 열고 라우팅을 하지 않아서, Line 5의 movieId 기반 상세 페이지가 사용자 플로우에서 사용되지 않습니다. onMovieClick에서 /movies/${movie.id}로 이동(또는 카드에 Link)하도록 연결이 필요합니다.

🤖 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 `@Week10/endl24/mission0102/src/pages/MovieDetailPage.tsx` around lines 3 - 11,
The MovieDetailPage is unreachable because the onMovieClick handler in
HomePage.tsx only opens a modal and does not perform any navigation. Modify the
onMovieClick handler to navigate to the detail page route using the pattern
`/movies/${movie.id}` (using React Router's useNavigate hook), or alternatively
wrap the MovieCard component with a Link element that points to this route. This
will connect the user interaction flow to the detail page so the movieId
parameter extracted from the URL can be properly utilized.

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.

1 participant