Skip to content

Conversation

@Wannys26
Copy link

@Wannys26 Wannys26 commented Nov 8, 2025

📎배포 링크

배포 링크

5->6주차 과제 진행하면서 중점적으로 고려한 사항

지난주 과제는 구현하면서 그동안 익숙했던 React 와 유사하게 했는데, 지난 과제 코드리뷰 등을 하면서 Next.js를 제대로 도입, 활용하지 못했다는 생각이 들었습니다. 따라서 Next.js에서 지향하는 방식을 따라서 다음을 리팩토링해보았습니다.

  • 폴더 구조
  • api 사용 방식

업무 분배

윤지

1. 프로젝트,코드 구조 리팩토링

(실제 폴더 구조)
src/
├── app/ # 페이지 라우트
├── components/ # UI 컴포넌트
├── lib/api/ # API 레이어
├── constants/ # 상수
├── types/ # TypeScript 타입
├── styles/ # CSS 파일 (globals.css, theme.css)
└── svgs/ # SVG 아이콘

  • 클라이언트 컴포넌트에서 useEffect + axios로 처리하던 API 호출을 서버 컴포넌트 + fetch 기반 데이터 패칭 구조로 변경
    → 초기 로딩 성능 개선, SEO 향상, 상태 관리 코드 감소(유지보수성 개선)

상세 페이지 구현 및 동적 라우팅 처리

  • Next.js의 동적 라우팅을 사용해서 detail/[mediaType]/[id] 로 접근 가능
  • 홈화면 내 각 아이템 클릭 시 상세페이지로 이동할 수 있도록 mediaType과 id를 넘기고, detail 페이지 내에서 detail.ts 관련 api 호출

주완

검색 페이지 구현

  • 검색어 입력 없을 시, Top Rated Movie 리스트 불러옴
  • 300ms debounce 처리

검색 페이지 무한 스크롤 구현(Tanstack Query 도입)

검색 페이지 스켈레톤 UI 적용

  • animate pulse 적용

검색 페이지 -> 상세 페이지 링크 적용

Merge 하는 과정에서 공통으로 사용되는 로직 util 함수로 분리

  • getMediaType등.

느낀 점

윤지

지난주 과제를 진행하면서 기능은 모두 구현하였지만, Next.js를 활용한 구현방식에 대해서 완벽하게 이해하고 적용하지 못한 것 같다는 생각이 들었습니다. 특히 지난 주 코드리뷰를 진행하며 다른 팀의 코드도 보고, 개선할 사항들을 많이 배울 수 있었고, 이를 실제로도 적용해보고자 많은 고민을 해보았던 것 같습니다. Next.js에서 지향하는 방식을 따르면서 과제를 진행하고 리팩토링하기 위해 이것저것 찾아보기도 하였고, 어떤 방식이 가장 좋을지, 다른 프로그래머들은 어떻게 하는지를 찾아보며 고민하고 배울 수 있는 과제였던 것 같습니다. 고민한 만큼 많이 배우기도 하였지만, 계속해서 업데이트되고 새로운 기술들이 나오는 만큼 배움을 꾸준히 해야겠다..고 느꼈습니다..

주완

Next.js에서 SSR은 어떻게 구현되는지, App Router 폴더 구조가 어떻게 작동하는지, 그리고 Next.js에서 제공하는 최적화 요소들은 무엇이 있는지 살펴볼 수 있는 주차였습니다. 고해성사를 하자면 1주차 때는 React 코드를 Next.js에 쓴 정도였는데, 윤지의 리팩토링을 보고 많이 반성했었습니다. CEOS 전기수 PR들을 읽어보며 어떻게 구현했는지 살펴보고, Next.js 공식 문서를 참조하며 최적화는 어떻게 하면 좋을지 고민을 해보았습니다.
특히 Skeleton UI는 언제 필요할지 읽어보면서 UX 개선이 이런 거구나…를 느낄 수 있었습니다.
next 공식문서
어느 정도의 latency에 스켈레톤 ui를 적용해야 되는가? (너무 좋은 글 같습니다)

리팩토링 관련 PR 사진

image image image

jungyungee and others added 30 commits October 29, 2025 17:17
[FEAT] 초기 세팅 및 랜딩페이지 구현
#3 [CHORE] 폰트, 색상 디자인 시스템 세팅
[FEAT] axios 함수, imageURL 유틸함수 구현 및 경로 수정
…nt-ui

[FEAT] 홈페이지 하단 컴포넌트 구현
Wannys26 and others added 22 commits November 8, 2025 13:28
[FEAT] 상세 페이지 구현 및 API 연동
[FEAT] 검색 페이지 UI 구현 및 API 연결
…gation

[FEAT] 검색 페이지->상세 페이지 Link 연결
Copy link

@hammoooo hammoooo left a comment

Choose a reason for hiding this comment

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

전반적으로 폴더 구조가 직관적으로 잘 설계되어 있다고 느꼈고
또한 PR올리신 거 봤는데 형식적인 pr이 아닌 서로 피드백해 주시는 것이 대단했습니다!! + 컨벤션도 좋았어요
많이 배울 수 있는 과제였습니다 고생하셨습니다!!

Choose a reason for hiding this comment

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

상세 정보 api까지 구현하셔서 더 실감나는 과제가 된 거 같아요!

import Image from 'next/image';
import Link from 'next/link';
import { FreeMode, Navigation } from 'swiper/modules';
import { Swiper, SwiperSlide } from 'swiper/react';

Choose a reason for hiding this comment

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

가로 스크롤 동작에서 라이브러리를 사용하셔서 그런지 더 매끄럽게 구현된 거 같아요

Choose a reason for hiding this comment

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

텍스트부터 swipe, 반응형까지 디테일하게 설정하신 점 너무 좋은 거 같습니다!!

enabled: !!query,
});

if (isLoading) {
Copy link
Author

@Wannys26 Wannys26 Nov 10, 2025

Choose a reason for hiding this comment

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

isLoading -> isPending으로 대체되었습니다. (Tanstack query v5)
추가로 isFetching 쓰는게 더 적절합니다.

이미 Suspense로 Skeleton 컴포넌트 fallback 하도록 설정해두었으므로, 필요없는 코드입니다.

: undefined,
});

if (isLoading) {
Copy link
Author

@Wannys26 Wannys26 Nov 10, 2025

Choose a reason for hiding this comment

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

isLoading -> isPending으로 대체되었습니다. (Tanstack query v5)
추가로 isFetching 쓰는게 더 적절합니다.

이미 Suspense로 Skeleton 컴포넌트 fallback 하도록 설정해두었으므로, 필요없는 코드입니다.

Copy link
Author

@Wannys26 Wannys26 Nov 10, 2025

Choose a reason for hiding this comment

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

검색어 입력에 따라 매번 데이터를 다르게 받아오는데 SSR 방식으로 만들어서 검색어 입력마다 서버에서 리렌더링을 하게 하는것 보다, 검색 결과는 CSR로 구성하는게 낫습니다.
TopSearches는 초기 페이지 SSR 유지.

new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1분
Copy link
Author

Choose a reason for hiding this comment

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

revalitdate Time은 1시간으로 설정이 되어있는데,
staleTime, gcTime은 각각 1분, 5분으로 짧게 설정되어있습니다.

서버 캐시가 1시간 마다 새롭게 API를 호출하는데, 클라이언트는 Tanstack query에서 매우 짧은 시간으로 재확인하고 refetch 해오는 상황입니다.

staleTime : 1시간
gcTime : 2시간
으로 수정하는 방향이 좋을 거 같습니다

Copy link

@Jy000n Jy000n left a comment

Choose a reason for hiding this comment

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

전반적으로 client 와 server 부분으로 폴더 나누어서 작업 하신 점이 인상깊었습니다! 과제 수고하셨습니당👍

Comment on lines +1 to +4
/**
* 홈 페이지
* 넷플릭스 클론의 메인 페이지로, 다양한 영화 및 TV 프로그램 섹션을 표시합니다.
*/
Copy link

Choose a reason for hiding this comment

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

페이지에 대한 설명을 간단하게 멘션해두는 것 좋네용

{overviewText ? (
<p className="text-caption3">{overviewText}</p>
) : (
<p className="text-caption3">줄거리가 따로 제공되지 않습니다.</p>
Copy link

Choose a reason for hiding this comment

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

previews가 없는 영화들을 따로 처리하신 점 인상 깊습니다

<div className="flex items-center justify-center w-[15px] h-[15px] border border-white translate-y-[3px]">
<span className="text-[4px] font-extrabold leading-none text-white text-center">
TOP
<br />
Copy link

Choose a reason for hiding this comment

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

아무생각 없이 div 태그로 처리했었는데 br 태그 방법도 있었네여

Copy link

Choose a reason for hiding this comment

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

각 타입에 대해 상세하게 설명까지 주석을 달아놔서 이해하기 더 편해요 !!

Comment on lines +1 to +3
import { searchMulti } from '@/lib/api/tmdb/search';

import SearchResults from '../client/SearchResults';
Copy link

Choose a reason for hiding this comment

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

연관 있는 폴더 내 한 폴더 정도면 상대경로로 잡는거는 가독성이나 유지보수 면에서 상관 없으려나요? 항상 고민하다가 그냥 다 절대경로로 통일했었는데 궁금해서 여쭤봅니담,,

Copy link

@dragunshin dragunshin left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! 코드가 전반적으로 깔끔해서 보기 좋았습니다

fetchNextPage();
}
},
{ threshold: 0.5 },

Choose a reason for hiding this comment

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

절반이 보여야 트리거되는 부분이 스크롤을 천천히 하다보면 정말 무한 스크롤같아 보입니다 좋은 것 같아요.

pageParams: [1],
}
: undefined,
enabled: !!query,

Choose a reason for hiding this comment

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

사소하지만 query.trim()으로 수정하면 더 안전할 듯 합니다

Comment on lines +14 to +21
useEffect(() => {
const handler = setTimeout(() => {
if (query) {
router.push(`/search?q=${encodeURIComponent(query)}`);
} else {
router.push('/search');
}
}, 300);

Choose a reason for hiding this comment

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

push를 계속 진행하고 있는데 replace로 해서 push를 최종때 한번만 진행해봐도 좋을 것 같아요

Choose a reason for hiding this comment

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

검색에 캐시를 쓰면 UX에 더 효과적일듯 합니다

Choose a reason for hiding this comment

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

또한, 빈 검색어일 ㄱ경우 trim()을 활용해서 빈 결과를 내보는 것도 검색에 있어 하나의 방법같아요!

Copy link

@Tutankhannun Tutankhannun left a comment

Choose a reason for hiding this comment

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

이번 주차도 멋진 코드 감사합니다. 저희가 많이 신경쓰지 못한 api관련 설정과 최적화와 관련된 내용 보면서 많이 배웠습니다. 과제 완성하시느라 수고하셨습니다!

Choose a reason for hiding this comment

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

components를 client와 server로 나눈 점이 인상 깊네요! 최적화에 신경쓰신 것 같아 좋네요.

className="movie-swiper"
>
{items.map((item) => (
<SwiperSlide key={item.id} style={{ width: itemWidth, height: itemHeight }} className="w-auto!">

Choose a reason for hiding this comment

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

className="w-auto!"는 tailwind에서 인식이 안될 것 같고 인라인 스타일로 정의되고 있기도 해서 빼도 될 것 같습니다.

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.

6 participants