Skip to content

[6주차] JobDri 구민교 & 이윤서 과제 제출합니다.#14

Open
yiyoonseo wants to merge 29 commits into
CEOS-Developers:masterfrom
CEOS-week5-Netflix:chore/init-project
Open

[6주차] JobDri 구민교 & 이윤서 과제 제출합니다.#14
yiyoonseo wants to merge 29 commits into
CEOS-Developers:masterfrom
CEOS-week5-Netflix:chore/init-project

Conversation

@yiyoonseo

@yiyoonseo yiyoonseo commented May 9, 2026

Copy link
Copy Markdown

링크

Next 넷플릭스
협업 노션

Migration

npm → pnpm

  • pnpm-lock.yaml, pnpm-workspace.yaml 추가
  • 패키지 매니저를 pnpm으로 전환하여 설치 속도 및 디스크 효율 개선

Webpack → TurboPack

  • next dev --turbo 적용
  • 개발 서버 빌드 속도 개선

Refactoring

로딩 UX 개선

  • search/loading.tsx, detail/[id]/loading.tsx 추가
  • Next.js 빌트인 로딩 UI를 활용한 Suspense 경계 정리

상세페이지 포스터 로딩 속도 개선

  • /api/detail/[id] Route Handler 신설로 상세 데이터 서버 사이드 pre-fetch
  • DetailPageContent, DetailLink 컴포넌트 분리하여 페이지 컴포넌트 슬림화
  • MovieRow, MyListRow, TopTenSection의 중복 데이터 fetch 제거

공통 컴포넌트 추출

  • TopTenCarousel, MainBar 제거
  • MovieCarousel 신설 — MovieRow, MyListRow의 공통 캐러셀 로직 통합

6주차 과제 요건

무한 스크롤

  • Intersection Observer 기반 무한 스크롤 구현 (SearchPageContent)
  • tmdb.ts API에 페이지네이션 파라미터 추가
  • /api/search Route Handler에 page 쿼리 처리

검색 로딩 스켈레톤

  • SearchResultsSkeleton 컴포넌트 신설
  • 검색 결과 로딩 중 스켈레톤 UI 표시

느낀점 및 배운점 (민교)

이번 일주일 동안 과제와 프로젝트를 동시에 진행하며 GitHub를 사용한 협업 방식에 많이 익숙해질 수 있었습니다. 브랜치를 나누어 작업하고, PR을 통해 변경 사항을 공유하고, merge 이후의 흐름까지 확인하면서 실제 협업에서 GitHub가 어떻게 활용되는지 더 잘 이해할 수 있었던 시간이었습니다.

또한 코드 리뷰의 중요성도 느꼈습니다. 내가 작성한 코드를 다른 팀원이 확인해주고, 반대로 다른 팀원의 코드를 보면서 구현 방식이나 놓친 부분을 함께 점검하는 과정이 프로젝트의 완성도를 높이는 데 큰 도움이 된다는 것을 느꼈습니다. 그리고 이를 위해서는 PR을 꼼꼼히 적는 습관을 가지는게 좋을 것 같다는 생각이 들었습니다.

더불어 피그마에 있는 요소들을 실제 코드로 옮기는 방식에도 점점 익숙해졌습니다. 디자인에 있는 레이아웃, 간격, 색상, 컴포넌트 구조를 확인하고 이를 화면에 최대한 가깝게 구현하면서, 디자인을 코드로 변환하는 경험을 쌓을 수 있었습니다.

Research Question

민교

  • 상세 페이지 동적 라우팅
    • 콘텐츠마다 다른 상세 정보를 보여주기 위해 /detail/[id] 구조로 구현
    • 하나의 상세 페이지 컴포넌트를 여러 콘텐츠에 재사용할 수 있도록 고려
  • 실시간 키워드 검색
    • 사용자가 검색어를 입력하면 바로 결과를 확인할 수 있도록 구현
    • 불필요한 API 호출을 줄이기 위해 debounce 적용
  • 검색 결과 무한 스크롤
    • 모든 결과를 한 번에 불러오지 않고, 스크롤 시 다음 페이지를 추가 요청
    • 초기 로딩 부담을 줄이고 자연스럽게 탐색할 수 있도록 고려
  • 검색 로딩 스켈레톤
    • 검색 중 단순 텍스트 대신 결과 리스트 형태의 스켈레톤 UI 표시
    • 로딩 중에도 화면 구조가 유지되어 더 안정적인 사용자 경험 제공

윤서

이번 작업에서는 프로젝트의 개발 환경을 전반적으로 개선하는 데 집중했습니다.
기존에 사용하던 npm을 pnpm으로 마이그레이션하고, 번들러를 Turbopack으로 전환하면서 패키지 설치와 빌드 양쪽 모두에서 속도 개선을 이끌어냈습니다. 저번주 코드 리뷰에서 지적해 주셨던 비효율적인 구조들을 함께 리팩토링하며 코드베이스의 유지보수성과 확장성을 높이는 데 공을 들였습니다.
협업 측면에서도 개선이 필요하다고 판단해, Notion 페이지를 새로 개설하여 작업 현황과 프로젝트 컨텍스트를 한 곳에서 관리할 수 있는 구조를 만들었습니다

minnngo and others added 29 commits April 30, 2026 01:54
Comment on lines +114 to +118
return () => {
window.clearTimeout(resetTimer);
window.clearTimeout(timer);
controller.abort();
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

검색 요청에서 debounceAbortController를 함께 사용하여, 입력이 빠르게 변경될 때 불필요한 요청과 stale response가 최신 상태를 덮어쓰는 문제를 함께 방지할 수 있는 점이 좋았습니다!

React 공식 문서에서도 Effect에서 비동기 요청을 다룰 때 이전 요청 결과가 뒤늦게 상태에 반영되는 race condition을 주의해야 한다고 설명하고 있는데, 현재 구현이 그 흐름에 잘 맞는 것 같습니다!

Comment thread package-lock.json
@@ -0,0 +1,8908 @@
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

pnpm으로 패키지 매니저를 통일한 상태라면 package-lock.json은 제거하는 방향이 좋아 보입니다!

pnpm-lock.yaml과 package-lock.json은 각각 pnpm과 npm이 사용하는 lockfile이라, 의존성 설치 기준을 명확히 하기 위해 pnpm-lock.yaml만 남기는 방향을 고려해보면 좋을 것 같습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

제가 migration이 처음이라 이 부분을 놓쳤네요!! 피드백 감사드립니다🙇‍♀️🙇‍♀️

@GirimNam GirimNam 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.

이번 주차에 함께 코드리뷰를 하게 되어서 영광입니다!

const { searchParams } = new URL(request.url);
const query = searchParams.get("query") ?? "";
const pageParam = Number(searchParams.get("page") ?? "1");
const page = Number.isFinite(pageParam) && pageParam > 0 ? pageParam : 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Number.isFinite()이나 pageParam > 0 에서 문자나 음수가 들어가는걸 막는 방어로직이 좋은거 같아요!

title={title}
className="flex h-[76px] w-full items-center bg-grey-800"
ariaLabel={`${title} 상세 페이지로 이동`}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

를 로 세분화해서 컴포넌트 관리하는 부분이 좋은 거 같아요!

Comment thread src/store/myList.ts
@@ -0,0 +1,58 @@
"use client";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

파일에 UI가 없어서 use client를 안써도 될 거 같아요

@minsxo minsxo 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.

6주차 과제 수고 많으셨습니다!☺️

Comment thread src/apis/tmdb.ts
Comment on lines +35 to +39
function filterValidMedia(items: TmdbTrendingItem[]): TmdbTrendingItem[] {
return items
.filter((item) => item.media_type !== "person")
.filter((item) => getTmdbImagePath(item));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

검색 결과 렌더링 전에 person 타입과 이미지가 없는 데이터를 필터링해둔 점 좋네요! 덕분에 빈 카드가 생기거나 영화/TV 목록에 인물 데이터가 섞이는 경우를 방지할 수 있어서 UX가 더 안정적일 것 같습니다.

Comment thread src/app/not-found.tsx
</Link>
<p className="mt-6 text-xs text-gray-400">
<span className="text-red-600">|</span> Error Code{" "}
<span className="font-semibold">NSES-404</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

에러 코드까지 Netflix 원본 UI처럼 반영한 디테일이 좋은 것 같아요!

Comment on lines +55 to +88
useEffect(() => {
const controller = new AbortController();
const searchParams = new URLSearchParams();

if (mediaType) {
searchParams.set("mediaType", mediaType);
}

async function loadDetail() {
setIsLoading(true);

try {
const response = await fetch(
`/api/detail/${id}?${searchParams.toString()}`,
{ signal: controller.signal },
);

if (!response.ok) {
setDetail(null);
return;
}

const data = (await response.json()) as DetailResponse;
setDetail(data.detail);
} catch {
if (!controller.signal.aborted) {
setDetail(null);
}
} finally {
if (!controller.signal.aborted) {
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.

이 상세 데이터 fetch는 page.tsx에서 처리해서 props로 내려줘도 좋을 것 같아요!

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.

5 participants