Skip to content

Create Week08 Misson 1, 2, 3#110

Merged
guieuna3-lab merged 1 commit intomainfrom
hyunbin5921/week08
Dec 18, 2025
Merged

Create Week08 Misson 1, 2, 3#110
guieuna3-lab merged 1 commit intomainfrom
hyunbin5921/week08

Conversation

@guieuna3-lab
Copy link
Copy Markdown
Collaborator

📝 미션 번호

*주차 Misson 1, 2, 3

📋 구현 사항

  • 디바운스, 트로틀링으로 서버와 클라이언트의 네트워크 비용 저하
  • 사이드 바 로직 업데이트로 인한 사용자 UI 개선

📎 스크린샷

2025-11-24.190129.mp4
2025-11-24.194205.mp4
2025-11-24.194205.mp4

✅ 체크리스트

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

🤔 질문 사항

없음

@guieuna3-lab guieuna3-lab self-assigned this Nov 24, 2025
@github-actions
Copy link
Copy Markdown

🤖 Gemini 코드리뷰 결과

안녕하세요. 시니어 프론트엔드 개발자로서 제공해주신 모든 Pull Request 변경사항을 통합하여 하나의 완성된 리뷰로 정리했습니다. 초기에는 src 파일이 부재하여 설정 파일 위주로 검토했으나, 이후 제공된 src 디렉토리 내의 .ts.tsx 파일을 포함하여 전반적인 코드 품질, 성능, 타입스크립트 활용 및 유지보수성을 면밀히 분석했습니다.


전반적인 평가

코드 베이스가 잘 구성되어 있고, React Query, React Hook Form + Zod, 커스텀 훅 등 최신 프론트엔드 기술 스택을 적극적으로 활용하려는 노력이 돋보입니다. 특히 인증 관련 인터셉터 로직, 무한 스크롤 구현, 낙관적 업데이트 등 복잡한 기능을 구현하려는 시도는 훌륭합니다.

하지만 프로젝트의 기반 설정(의존성, ESLint)에서 잠재적 불안정성 및 유지보수성 문제가 발견되었으며, src 코드 내부에서도 React Hooks 규칙 위반, 사용자 경험 저해 요소, 타입 불일치, 중복 코드, 그리고 React Query의 이점을 충분히 활용하지 못하는 지점들이 확인되었습니다. 아래에서 각 항목별로 구체적인 피드백과 개선 제안을 드립니다.


리뷰 항목별 피드백 및 구체적 개선 제안

1. 프로젝트 설정 및 의존성 (package.json, eslint.config.js, README.md, tsconfig.*.json 부재)

  • Dependency 안정성 및 잠재적 성능 저하: react@19.2.0, react-router-dom@7.9.6, tailwindcss@4.1.17, eslint@9.39.1, typescript@5.9.3, vite@7.2.4 등 대부분의 주요 의존성이 Release Candidate (RC) 또는 베타 버전을 사용하고 있습니다. 이러한 버전들은 API 변경, 예상치 못한 버그, 성능 이슈 등을 내포할 가능성이 크므로, 특별한 실험 목적이 아니라면 안정화된 최신 버전을 사용하는 것이 장기적인 안정성과 유지보수 측면에서 훨씬 유리합니다.
  • 불필요한 Polyfill: intersection-observer 라이브러리가 포함되어 있습니다. Intersection Observer API는 최신 브라우저에서는 대부분 기본적으로 지원되는 기능이므로, 프로젝트의 최소 브라우저 지원 범위를 고려하여 이 polyfill이 정말 필요한지 검토하고 불필요하다면 제거하여 번들 사이즈를 줄일 수 있습니다.
  • ESLint 설정의 타입 인식 강화: eslint.config.js에서 tseslint.configs.recommended를 사용하고 있습니다. tseslint.configs.recommendedTypeChecked 또는 tseslint.configs.strictTypeChecked로 변경하고 languageOptions.parserOptions.project 설정을 추가하여, TypeScript의 타입 정보를 활용하는 linting 규칙을 활성화하는 것이 강력히 권장됩니다. 이는 런타임 오류를 줄이고 코드의 견고성을 높이는 데 크게 기여합니다. (예: 낙관적 업데이트 로직에서의 타입 불일치 등)
  • ESLint ECMAScript 버전 업데이트: eslint.config.jslanguageOptions.ecmaVersion2020에서 2022 또는 latest로 업데이트하여 최신 JavaScript 문법을 자유롭게 사용할 수 있도록 설정하는 것이 좋습니다.
  • Prettier 도입: 코드 일관성을 위해 Prettier를 도입하여 자동으로 코드 포맷팅을 강제하는 것을 제안합니다. ESLint와 통합하여 충돌 없이 작동하도록 설정하면 개발자 간의 불필요한 코드 스타일 논쟁을 줄이고 코드 리뷰 시간을 단축할 수 있습니다.
  • React-specific Linting 규칙 추가: README.md에 언급된 eslint-plugin-react-xeslint-plugin-react-dom 플러그인을 추가하여 React 컴포넌트 및 훅 사용에 대한 모범 사례를 강제하고 잠재적인 버그를 미리 방지할 수 있습니다.
  • tsconfig.json 엄격 모드 검토: tsconfig.json 파일이 아직 없지만, 향후 프로젝트의 견고성을 위해 strict: true를 포함한 엄격한 타입 체크 옵션을 적용할 것을 고려해주세요. 이는 초기에는 번거로울 수 있으나, 장기적으로 코드의 안정성과 가독성을 크게 향상시킵니다.

2. src 디렉토리 내부 파일 (.ts, .tsx)

src/apis/axios.ts
  • ⚠️ CRITICAL: useLocalStorage 훅의 잘못된 사용: axiosInstance.interceptors.request.useresponse.use 내부에서 useLocalStorage 훅을 호출하는 것은 React Hooks 규칙 위반입니다. 훅은 React 컴포넌트나 다른 커스텀 훅 내부에서만 호출되어야 합니다. 이는 런타임 오류를 일으킬 수 있습니다.
    • 개선 제안: window.localStorage API를 직접 사용하는 유틸리티 함수(예: getStoredAccessToken, setStoredAccessToken)를 생성하여 axios.ts에서 import 하여 사용하도록 변경합니다.
  • refreshPromise 타입: refreshPromise: Promise<string> | null = null;로 되어 있으나, 실제 반환 값은 data.data.accessToken이므로, accessTokenstring 타입이라면 올바릅니다.
src/pages/HomePage.tsx
  • ⚠️ CRITICAL: handleWheel 이벤트 핸들러의 스크롤 차단 로직: 휠 이벤트를 3초 이내에 반복할 경우 e.preventDefault()e.stopPropagation()을 호출하여 스크롤을 막는 로직은 사용자 경험에 심각한 부정적 영향을 미치고 접근성을 저해할 수 있습니다. 무한 스크롤 최적화는 스크롤 이벤트 자체를 막기보다, 데이터 페칭 로직에 스로틀/디바운스를 적용하는 방식으로 접근해야 합니다.
    • 개선 제안: 이 useEffect 내의 휠 이벤트 리스너 로직을 제거하고, throttledScrollY를 활용한 isNearBottom 체크와 fetchNextPage 호출만으로 무한 스크롤을 구현합니다. 필요하다면 fetchNextPage 호출 자체에 디바운스/스로틀을 적용할 수 있습니다.
src/pages/MyPage.tsx
  • 데이터 페칭 방식의 불일치: useGetMyInfo 훅이 이미 정의되어 있음에도 불구하고, MyPage.tsx에서는 useEffect 내에서 getMyInfo API 함수를 직접 호출하고 있습니다. 이는 React Query의 캐싱, 로딩 상태 관리 등의 이점을 활용하지 못하게 하며 코드 일관성을 해칩니다.
    • 개선 제안: MyPage.tsx에서도 useAuth 훅과 함께 useGetMyInfo 훅을 사용하여 사용자 정보를 가져오도록 수정합니다.
  • 함수명 구체화: getData 함수명은 조금 더 구체적인 이름(fetchMyInfo 등)을 사용하면 좋을 것 같습니다.
src/pages/LoginPage.tsx
  • API URL 환경 변수 사용 일관성: Google 로그인 시 window.location.href = "http://localhost:8000/v1/auth/google/login"와 같이 하드코딩된 URL을 사용하고, 주석 처리된 부분에서는 import.meta.env.VITE_SERVER_API_URL을 사용하고 있습니다. 모든 서버 API URL에 환경 변수를 일관되게 적용하여 로컬 및 배포 환경에서 유연하게 대응하도록 합니다.
src/layouts/HomeLayout.tsx, src/layouts/ProtectedLayout.tsx
  • 구조적 중복: 두 레이아웃 컴포넌트가 NavBar, Sidebar, main, Footer, CreateLpModal, SearchProvider 등 거의 동일한 구조를 가지고 있습니다.
    • 개선 제안: 공통적인 UI 요소를 포함하는 BaseLayout.tsx 컴포넌트를 생성하여 중복을 제거하고, 각 레이아웃은 BaseLayout을 감싸고 고유한 로직(예: 인증 체크)만 추가하도록 합니다.
  • <Sidebar /> 컴포넌트 사용 오류: 이 두 레이아웃에서 lucide-react<Sidebar /> 아이콘 컴포넌트를 실제 사이드바 패널인 src/components/Sidebar.tsx 대신 렌더링하고 있습니다.
    • 개선 제안: NavBar에서 useSidebar 훅을 통해 isOpen 상태를 관리하고, NavBar 컴포넌트 자체에서 src/components/Sidebar.tsx를 렌더링하도록 유지하는 것이 좋습니다. 레이아웃 내의 <Sidebar /> 아이콘 렌더링은 제거합니다.
src/hooks/mutations/usePostLike.ts, src/hooks/mutations/useDeleteLike.ts
  • 낙관적 업데이트 로직에 Deep Copy 적용: onMutate에서 const newLpPost = { ...previousLpPost };는 얕은 복사입니다. previousLpPost?.data.likes 배열을 직접 수정하면 원본 previousLpPost 객체의 내부 likes 배열도 함께 변형되어, onError 시 정확한 이전 상태로 복구하기 어려워집니다.
    • 개선 제안: onMutate에서 previousLpPost를 수정하기 전에 JSON.parse(JSON.stringify(previousLpPost))와 같이 깊은 복사를 수행하여 원본 데이터를 안전하게 보존합니다.
  • onError 캐시 복구 로직 수정: onError에서 queryClient.setQueryData([QUERY_KEY.lps, newLp.lpid], context?.previousLpPost?.data.id); 이 부분은 id만으로 데이터를 복구하려는 것으로 보입니다. 캐시된 LP 상세 정보는 ResponseLpDto 전체 객체여야 하므로, queryClient.setQueryData([QUERY_KEY.lps, newLp.lpid], context?.previousLpPost); 와 같이, context에 저장된 원본 previousLpPost 객체 전체를 사용하여 캐시를 복구하도록 수정합니다.
  • as Likes 캐스팅 위험: const newLike = { userId, lpId: lp.lpid } as Likes; 부분에서 id 속성이 없는 객체를 Likes 타입으로 캐스팅하고 있습니다. 백엔드에서 id를 자동 생성한다면 문제가 없지만, 명시적으로 id가 필요한 경우 타입 불일치가 발생할 수 있습니다. 이 부분은 tseslint.configs.strictTypeChecked 활성화 시 경고를 발생시킬 수 있습니다.
  • 낙관적 업데이트 로직 중복: onMutate 콜백 내부의 캐시 취소, 이전 데이터 가져오기, 좋아요 목록 수정 로직이 거의 동일하므로, 재사용 가능한 헬퍼 함수로 추출하여 중복을 줄일 수 있습니다.
src/hooks/useForm.ts
  • 타입 정의 개선: validate 함수의 반환 타입과 errors, touched 상태의 타입이 Record<keyof T, string> 등으로 정의되어 모든 필드에 대한 상태가 항상 존재한다고 가정합니다.
    • 개선 제안: validate 함수의 반환 타입과 errors 상태의 타입을 Partial<Record<keyof T, string>>으로, touched 상태의 타입을 Partial<Record<keyof T, boolean>>으로 변경하여, 필요에 따라 일부 필드에만 해당 상태가 있을 수 있음을 정확하게 나타내고, useState 초기값을 {}로 설정합니다.
src/types/lp.tssrc/types.ts
  • Lp 타입의 likes 속성: Lp 타입 정의에서 likes: Like로 되어 있으나, Like 타입은 정의되어 있지 않고, 코드에서는 likes를 배열처럼(lpData.likes.some) 사용하고 있습니다.
    • 개선 제안: Lp 타입의 likes 속성을 likes: Likes[]로 수정하여 배열 형태임을 명확히 하고, Likes 타입을 올바르게 참조하도록 합니다.
  • RequestLpDto의 목적 명확화: RequestLpDtolpid: number;만 포함하고 있어, 이것이 실제로 요청 바디에 사용되는 DTO인지, 아니면 경로/쿼리 파라미터를 위한 것인지 혼란을 줄 수 있습니다. 만약 경로/쿼리 파라미터라면 별도의 DTO가 필요 없을 수 있으며, 요청 바디라면 이름에 대한 의도를 더 명확히 (예: LpIdRequestPayload) 하거나 추가 필드가 없는 이유를 주석으로 명시하는 것을 고려해볼 수 있습니다.
  • ResponseLikeLpDto 내부 타입 추출: ResponseLikeLpDto 내부에 정의된 익명 객체 타입 { id: number; userId:number; lpId: number; }type LikeDetail = { id: number; userId: number; lpId: number; };와 같이 별도의 이름 있는 타입으로 추출하는 것을 제안합니다. 이는 코드 재사용성 및 가독성을 높여줍니다.
src/utils/validate.ts
  • validateUservalidateSignin 함수 통폐합: validateSignin 함수가 validateUser 함수를 아무런 추가 로직 없이 단순히 호출만 하고 있습니다. 이는 불필요한 추상화이며 코드 중복으로 간주될 수 있습니다.
    • 개선 제안: validateUser 함수를 validateSignin으로 이름을 변경하고 이 함수를 직접 export하는 것이 더 간결하고 명확합니다.
  • 유효성 검사 반환 타입 명시: validateSignin 함수의 반환 타입을 type SigninValidationErrors = Partial<Record<keyof UserSigninInformation, string>>;와 같이 명시적으로 지정하여 함수의 시그니처를 더 명확히 할 수 있습니다.
  • 이메일 정규식 확장성 고려: 현재 이메일 정규식의 TLD(최상위 도메인) 부분이 [a-zA-Z]{2,3}$로 제한되어 있습니다. .info, .name, .travel 등 3자를 초과하는 TLD를 가진 이메일 주소를 허용하지 않을 수 있으므로, 일반적인 웹 서비스에서는 [a-zA-Z]{2,}$ 또는 [a-zA-Z]{2,6}$ 등으로 수정하여 더 다양한 이메일 형식을 지원하도록 하는 것을 고려해볼 수 있습니다.
  • 정규식 상수로 추출: 이메일 유효성 검사에 사용되는 정규 표현식을 파일 상단이나 관련 유틸리티 파일에 const EMAIL_REGEX = /.../;와 같이 상수로 추출하여 정의하는 것을 제안합니다. 이는 코드 가독성을 높이고, 동일한 정규식이 여러 곳에서 사용될 때 중복을 피할 수 있게 해줍니다.
  • 파일 마지막에 빈 줄 추가: validate.ts 파일 끝에 빈 줄이 없습니다. 코드 스타일 일관성을 위해 파일 끝에 빈 줄을 추가하는 것이 좋습니다.
src/vite-env.d.ts
  • Vite 환경 변수 타입 정의는 올바르게 작성되었습니다. ImportMetaEnvImportMeta의 선언 병합(declaration merging)을 통한 확장은 표준적이고 모범적인 사용법입니다.

주요 개선 제안 (우선순위별)

아래는 위에서 언급된 많은 제안 중 가장 중요하고 즉각적인 개선이 필요한 항목들입니다.

  1. src/apis/axios.ts에서 useLocalStorage 훅 호출 방식 개선 (Critical Bug)
    • 문제: Axios 인터셉터 내에서 React Hooks 규칙을 위반하여 useLocalStorage를 호출합니다.
    • 제안: window.localStorage API를 직접 사용하는 유틸리티 함수(예: getStoredAccessToken, setStoredAccessToken)를 생성하여 인터셉터에서 사용하도록 변경합니다.
  2. src/pages/HomePage.tsx의 휠 이벤트 preventDefault 로직 제거 (Critical UX/Accessibility)
    • 문제: 휠 이벤트를 강제로 막는 로직은 사용자 경험을 심각하게 저해하고 접근성을 떨어뜨립니다.
    • 제안: 해당 휠 이벤트 리스너를 제거하고, throttledScrollYisNearBottom 체크만으로 무한 스크롤을 구현합니다.
  3. 낙관적 업데이트 로직에 Deep Copy 적용 및 onError 복구 로직 수정
    • 문제: usePostLike/useDeleteLikeonMutate에서 얕은 복사로 인해 onError 시 원본 상태 복구가 어렵고, onError에서 id만으로 복구하는 것은 잘못된 접근입니다.
    • 제안: onMutate에서 깊은 복사를 수행하여 원본 데이터를 안전하게 보존하고, onError에서는 context에 저장된 원본 previousLpPost 객체 전체를 사용하여 캐시를 복구하도록 수정합니다.
  4. 레이아웃 컴포넌트(HomeLayout, ProtectedLayout) 중복 제거 및 Sidebar 컴포넌트 올바른 사용
    • 문제: 두 레이아웃의 구조적 중복이 많으며, <Sidebar /> 아이콘을 실제 사이드바 컴포넌트 대신 렌더링하고 있습니다.
    • 제안: 공통 요소를 BaseLayout.tsx로 추출하여 중복을 제거하고, NavBar에서 src/components/Sidebar.tsx를 렌더링하도록 유지하며 레이아웃 내의 아이콘 렌더링은 제거합니다.
  5. src/pages/MyPage.tsx에서 useGetMyInfo 훅 사용으로 통일
    • 문제: useEffect 내에서 API 함수를 직접 호출하여 React Query의 이점을 활용하지 못하고 코드 일관성이 떨어집니다.
    • 제안: MyPage.tsx에서도 useGetMyInfo 훅을 사용하여 사용자 정보를 가져오도록 수정합니다.
  6. ESLint 타입 인식 규칙 활성화 및 주요 의존성 버전 검토
    • 문제: 타입스크립트의 타입 정보를 활용하는 Linting 규칙이 활성화되어 있지 않고, 핵심 의존성들이 불안정한 RC/베타 버전입니다.
    • 제안: eslint.config.js에서 tseslint.configs.recommendedTypeChecked를 활성화하고 languageOptions.parserOptions.project를 추가합니다. 또한, 모든 주요 의존성을 안정화된 최신 버전으로 업데이트하는 것을 강력히 권장합니다.
  7. src/utils/validate.ts 함수 통폐합 및 타입 명시 개선
    • 문제: validateUservalidateSignin 함수가 중복되고, 유효성 검사 함수의 반환 타입이 명시적이지 않아 가독성과 유지보수성이 저하됩니다.
    • 제안: validateUser 함수를 validateSignin으로 이름을 변경하고 직접 export하여 중복을 제거합니다. 또한, SigninValidationErrors와 같은 타입을 정의하여 함수의 반환 타입을 명시적으로 지정합니다. 이메일 정규식도 상수로 추출하고 확장성을 고려하여 수정합니다.

이번 PR은 전반적으로 잘 작성되었지만, 위에 언급된 몇 가지 중요한 개선점들이 있습니다. 특히 useLocalStorage 훅의 잘못된 사용과 HomePage.tsx의 휠 이벤트 핸들링은 기능적 오류 및 사용자 경험 저하를 유발할 수 있으므로 우선적으로 수정되어야 합니다. 나머지 개선 사항들도 코드의 견고성, 유지보수성, 가독성을 높이는 데 크게 기여할 것입니다.

수고 많으셨습니다. 다음 작업에서 개선된 코드를 볼 수 있기를 기대합니다.

Copy link
Copy Markdown
Collaborator

@woojo230 woojo230 left a comment

Choose a reason for hiding this comment

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

8주차도 고생하셨습니다! 최근에 업데이트 된 React-Compiler 라는 자동 최적화 도구가 나왔으니 같이 공부해보면 좋을 것 같아요!

@guieuna3-lab guieuna3-lab merged commit f75a0c0 into main Dec 18, 2025
1 check passed
@guieuna3-lab guieuna3-lab deleted the hyunbin5921/week08 branch December 18, 2025 22:04
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