You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
안녕하세요, 시니어 프론트엔드 개발자입니다. 제출해주신 Pull Request의 변경 사항을 면밀히 검토했습니다. 전반적으로 React의 핵심 최적화 기법인 memo, useCallback, useMemo를 적절히 활용하고 계시며, TypeScript와 TanStack Query를 이용한 데이터 관리 능력 또한 인상 깊습니다. 이는 코드 품질 향상과 성능 개선에 크게 기여하고 있습니다.
아래는 각 파일 및 기능별 구체적인 리뷰 의견과 개선 제안입니다.
통합 리뷰
1. 전반적인 성능 최적화 노력 (매우 긍정적)
memo, useCallback, useMemo의 효과적인 사용: LpCard.tsx 컴포넌트의 memo화, HomePage.tsx의 useMemo를 사용한 파생 데이터 메모이제이션 및 useCallback을 사용한 handleSearch 최적화, LpComments.tsx의 모든 이벤트 핸들러를 useCallback으로 래핑한 점은 매우 훌륭합니다. 특히 리스트 아이템이나 하위 컴포넌트에 props로 전달되는 함수/값이 불필요하게 재생성되는 것을 방지하여 리렌더링 최적화에 큰 도움이 됩니다.
useGetInfiniteLpList API 변경: cursor 파라미터가 제거된 것은 react-query의 useInfiniteQuery 패턴에 더 잘 부합합니다. 훅 내부에서 queryFn의 pageParam을 올바르게 사용하고 있는지 확인이 필요합니다.
2. 파일별 상세 리뷰
src/components/lp/LpCard.tsx
export default memo(LpCard) 적용은 리스트 내 아이템의 불필요한 리렌더링을 방지하여 성능에 큰 도움이 됩니다.
src/pages/HomePage.tsx
lps, isFetching 등 파생 데이터를 useMemo로 래핑한 것은 좋습니다. 이러한 값들이 memo화된 하위 컴포넌트의 prop으로 전달될 경우 안정적인 레퍼런스를 제공하여 리렌더링을 줄일 수 있습니다.
handleSearch 함수를 useCallback으로 래핑한 것 또한 하위 컴포넌트의 불필요한 리렌더링을 방지하는 좋은 최적화입니다.
useGetInfiniteLpList 호출 시 0 인자가 제거된 것은 훅 변경과 일관됩니다.
src/pages/LpComments.tsx
handleAddCommentClick, handleEditClick 등 모든 이벤트 핸들러를 useCallback으로 래핑한 것은 훌륭한 최적화 전략입니다.
useCallback 의존성 배열(deps)이 각 함수의 클로저 변수를 정확히 포착하고 있어 타입스크립트 활용도 좋습니다.
두 컴포넌트 모두 memo를 사용하여 불필요한 리렌더링을 방지하려는 의도가 좋습니다. onClick, onChange prop의 타입 정의도 명확합니다.
src/pages/UseCallbackPage.tsx
useState와 useCallback 훅을 적절히 사용하고 있습니다.
handleText는 의존성 배열을 비워 memo 효과를 잘 활용하고 있습니다.
개선 필요: handleIncreaseCount의 useCallback 의존성 배열에 count가 포함되어 있어 count가 변경될 때마다 함수가 재생성되고, 이로 인해 CountButton의 memo 효과가 상쇄됩니다. setCount의 함수형 업데이트를 통해 count 의존성을 제거해야 합니다.
src/utils/math.ts
isPrime 함수는 num이 커질수록 연산량이 기하급수적으로 증가하는 비효율적인 방식입니다. sqrt(num) 이하에서만 약수를 확인하고, 짝수를 미리 걸러내는 최적화가 필요합니다.
findPrimeNumbers는 개별 isPrime 호출에 의존하는데, 여러 소수를 찾을 때는 "에라토스테네스의 체"와 같은 알고리즘이 훨씬 효율적입니다.
src/pages/UseMemoPage.tsx
소수 계산과 같이 비용이 많이 드는 연산을 useMemo로 캐싱하여 불필요한 리렌더링 시 재계산을 방지하는 점이 인상 깊습니다.
개선 필요: handleChangeText 함수가 TextInput에서 전달되는 실제 입력 값을 받지 못하고 있으며, TextInput 또한 value prop이 없어 제어 컴포넌트(controlled component)로 동작하지 못하고 있습니다.
src/components/MovieSearchPage/MovieList.tsx
useMovie 훅에 include_adult: adultChecked를 전달하고 있음에도 불구하고, filteredMovies에서 searchData?.results?.filter((movie) => !movie.adult)를 다시 수행하는 것은 중복 필터링입니다. include_adult 파라미터를 API에 정확히 전달하여 서버 측에서 필터링하도록 하고, 클라이언트 측 필터링을 제거하는 것이 효율적입니다.
src/components/MovieSearchPage/MovieModal.tsx
popularity 진행 바 계산 시 (movie.popularity / 1000) * 100은 popularity가 1000을 넘을 경우 100%를 초과할 수 있습니다. Math.min으로 제한하고 있지만, 1000이라는 고정된 최대값은 popularity의 실제 분포를 반영하기 어려울 수 있습니다.
src/components/MovieSearchPage/Searchbar.tsx
language prop과 handleLanguageChange 함수에서 문자열 리터럴을 사용하는 대신 유니온 타입을 정의하여 타입 안전성을 높일 수 있습니다.
3. 타입스크립트 문법 / 타입 개선점
Non-null Assertion Operator 안전성 강화: src/main.tsx의 document.getElementById("root")!와 같은 non-null assertion operator 사용은 해당 요소가 존재하지 않을 경우 런타임 에러를 발생시킬 수 있습니다. 존재 여부를 확인하는 방어 로직을 추가하는 것이 안전합니다.
인터페이스 명명 규칙: ICountButton, ITextInput과 같이 인터페이스에 I 접두사를 붙이는 것은 과거의 컨벤션이며, 현대 TypeScript에서는 CountButtonProps, TextInputProps와 같이 접미사를 사용하는 것이 일반적입니다.
TextInput 제어 컴포넌트화: ITextInput 인터페이스에 value: string;을 추가하고, 컴포넌트에서 <input value={value}>를 바인딩하여 React의 제어 컴포넌트 패턴을 따르도록 합니다.
HomePage.tsx의 order 상태 타입: PAGINATION_ORDER 타입을 임포트한다면, useState<PAGINATION_ORDER>를 사용하여 타입 정의의 일관성을 높이는 것이 좋습니다.
언어 선택의 타입 안전성: src/types에 type LanguageCode = 'ko-KR' | 'en-US' | 'ja-JP';와 같은 유니온 타입을 정의하고, language 상태 및 관련 prop에 적용하여 타입 안전성을 강화합니다.
4. 변수명, 함수명, 주석 품질 및 코드 리팩토링
변수명/함수명: 전반적으로 명확하고 목적에 부합하게 잘 명명되어 있습니다.
개발용 console.log 제거: CountButton.tsx, TextInput.tsx, UseMemoPage.tsx, MovieSearchPage.tsx 등에 있는 디버깅용 console.log는 프로덕션 빌드에서 제거하거나 환경 변수에 따라 조건부로 활성화되도록 변경하는 것이 좋습니다.
TMDB 이미지 기본 URL 중앙화: MovieCard.tsx와 MovieModal.tsx에서 TMDB 이미지의 기본 URL(https://image.tmdb.org/t/p/)이 하드코딩되어 있습니다. 이를 유틸리티 함수나 상수로 중앙에서 관리하면 유지보수성이 향상됩니다.
Searchbar의 기본 검색어 상수화: inputValue.trim() || "코난"과 같이 사용되는 기본 검색어를 상수로 추출하여 중복을 줄이고 명확성을 높입니다.
컴포넌트의 레이아웃 스타일 분리: InputNumber와 TextInput 컴포넌트 내부에 ml-3와 같은 레이아웃 관련 스타일이 포함되어 있습니다. 이러한 스타일은 컴포넌트 자체보다는 부모 컴포넌트와의 배치 관계에 더 가깝으므로, 컴포넌트의 재사용성을 위해 부모 컴포넌트에서 제어하는 것이 좋습니다.
소수 리스트 간격 처리 개선: UseMemoPage.tsx에서 로 간격을 주는 대신, Tailwind CSS의 flex flex-wrap gap-x-2와 같은 유틸리티 클래스를 사용하여 더 유연하고 현대적인 간격 제어 방식을 적용합니다.
구체적 개선 제안 (총 12가지)
useCallback 의존성 배열 최적화 (src/pages/UseCallbackPage.tsx): handleIncreaseCount 함수가 count 상태에 의존하지 않도록 setCount의 **함수형 업데이트(functional update)**를 사용합니다.
// src/pages/UseCallbackPage.tsxconsthandleIncreaseCount=useCallback((number: number)=>{setCount(prevCount=>prevCount+number);},[]// 이제 외부 count 상태에 의존하지 않으므로 의존성 배열을 비울 수 있습니다.);
src/main.tsx의 TypeScript 안전성 강화: document.getElementById("root")! 대신 루트 엘리먼트 존재 여부를 확인하여 런타임 에러 가능성을 줄입니다.
// src/main.tsxconstrootElement=document.getElementById("root");if(!rootElement){thrownewError("Root element with ID 'root' not found in the DOM.");}createRoot(rootElement).render(<App/>);
src/utils/math.ts의 isPrime 함수 로직 최적화: i * i <= num 조건을 사용하여 불필요한 반복을 줄이고, 짝수 및 2에 대한 예외 처리를 추가하여 효율성을 높입니다.
(선택적) src/utils/math.ts의 findPrimeNumbers 함수를 에라토스테네스의 체로 개선: max가 큰 경우를 대비하여 에라토스테네스의 체와 같은 더 효율적인 소수 찾기 알고리즘을 적용하는 것을 고려합니다. (위 통합 리뷰 섹션에 예시 코드가 있습니다.)
src/components/TextInput.tsx를 제어 컴포넌트(controlled component)로 전환 및 src/pages/UseMemoPage.tsx 수정: TextInput 컴포넌트에 value prop을 추가하고, UseMemoPage의 handleChangeText 함수 시그니처를 수정하여 TextInput의 실제 입력 값을 정확히 반영하도록 합니다.
언어 선택 select의 타입 안전성 강화: src/types/common.ts 또는 src/types/movie.ts에 언어 코드를 위한 유니온 타입을 정의하고 MovieSearchPage.tsx 및 Searchbar.tsx에서 사용합니다.
// src/types/common.tsexporttypeLanguageCode='ko-KR'|'en-US'|'ja-JP';// ... 사용처에서 string 대신 LanguageCode 사용
HomePage.tsx 에러 UI 처리 및 useMovie 훅 로딩/에러 상태 활용: isError 상태에 대한 사용자 피드백 UI (에러 메시지 표시, 다시 시도 버튼 등)를 추가하고, useQuery 훅이 제공하는 isLoading, isError, error 상태를 활용하여 로딩 스피너나 에러 메시지를 표시함으로써 사용자 경험을 개선합니다.
개발용 console.log 제거 또는 조건부 사용:
모든 console.log 호출을 프로덕션 빌드에서 제거하거나, if (import.meta.env.DEV)와 같이 개발 환경에서만 활성화되도록 수정합니다.
접근성 개선 / TextInput에 label 추가: <input> 요소에는 시각적으로 보이지 않더라도 접근성을 위해 <label> 요소가 함께 제공되는 것이 좋습니다. aria-label이나 sr-only 클래스를 사용하여 시각적으로 숨겨진 <label>을 추가합니다.
스타일 및 레이아웃 관리 개선:
InputNumber 및 TextInput 컴포넌트에서 ml-3와 같은 레이아웃 관련 클래스를 제거하고, 상위 컴포넌트(UseMemoPage)에서 flex 및 gap 속성을 사용하여 간격을 제어합니다.
UseMemoPage.tsx의 소수 리스트 간격 처리도 대신 Tailwind CSS의 gap-x-2 등을 활용하여 더 깔끔하고 유연한 방식을 적용합니다.
전반적으로 매우 깔끔하고 효율적인 코드입니다. 위 제안들은 주로 코드의 성능, 안정성, 가독성, 유지보수성, 그리고 사용자 경험을 더욱 향상시키기 위한 보완 사항으로 검토해주시면 좋을 것 같습니다.
The reason will be displayed to describe this comment to others. Learn more.
10주차도 고생하셨습니다. 초반과 비교했을때 전반적으로 많이 성장한 것 같아 뿌듯한 마음입니다. 이후로는 컴포넌트 분리 및 확장성도 고려해보며 리팩토링 해보면 좋을 것 같아요
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 미션 번호
10주차 Misson 0, 1, 2
📋 구현 사항
📎 스크린샷
🔽 mission00-1

🔽 mission00-2

🔽 mission01
week10-mission01.mp4
🔽 mission02
https://minseeeeo-week10-mission02.vercel.app/
✅ 체크리스트
🤔 질문 사항