Skip to content

[5주차] Jobdri 구민교 & 이윤서 과제 제출합니다.#4

Open
minnngo wants to merge 18 commits intoCEOS-Developers:masterfrom
CEOS-week5-Netflix:chore/init-project
Open

[5주차] Jobdri 구민교 & 이윤서 과제 제출합니다.#4
minnngo wants to merge 18 commits intoCEOS-Developers:masterfrom
CEOS-week5-Netflix:chore/init-project

Conversation

@minnngo
Copy link
Copy Markdown

@minnngo minnngo commented May 1, 2026

🔗: 베포링크

느낀점 및 배운점 (민교)

이번 프로젝트를 진행하면서 Next.js를 처음 사용해 보았고, 외부 API를 연결해 데이터를 가져오는 작업도 처음 경험해 보았습니다. 이전에는 주로 정적인 화면을 만들거나 한 파일 안에서 기능을 구현하는 방식에 익숙했는데, 이번에는 페이지 구조, 컴포넌트 분리, 데이터 요청, 응답 처리 등 전체적인 흐름을 함께 고려해야 했습니다.

그러나 이번 과제에서 가장 크게 느낀 점은 협업 개발이 단순히 페이지를 나누어 작성하는 것이 아니라는 점이었습니다. 혼자 작업할 때는 하나의 브랜치에서 계속 코드를 작성하면 되었지만, 협업에서는 기능별로 브랜치를 나누고 각자의 작업 내용을 커밋한 뒤 다시 합치는 과정이 필요했습니다. 실제 코드를 작성하는 것만큼이나 브랜치 관리, 커밋 컨벤션, PR 작성, 머지 과정 등 협업을 위한 절차가 중요하다는 것을 알게 되었습니다. 처음에는 이런 과정들이 익숙하지 않아 단순 개발보다 더 어렵게 느껴졌습니다.

특히 여러 브랜치를 오가며 작업하는 것에 익숙하지 않아 어려웠습니다. 이전에는 한 브랜치에서 쭉 작업하면 되었기 때문에 브랜치를 나누는 이유를 깊게 생각해본 적이 없었지만, 이번 협업을 통해 기능 단위로 작업을 분리하는 것의 중요성을 이해하게 되었습니다.

main 브랜치로 머지하는 과정에서 충돌이 발생한 적이 있는데, 충돌이 발생한 파일을 확인하고 어떤 코드가 필요한 코드인지 판단한 뒤 정리하는 과정에서 많이 헤맸습니다. 충돌 해결은 단순히 표시된 부분을 지우면 되는 것이 아니라, 전체 코드를 이해하고 조심스럽게 처리해야 하는 작업이라는 점을 배웠습니다.

프로젝트 초기 세팅과 파일 구조를 파트너와 함께 정하는 과정도 처음 경험해 보았습니다. 어떤 폴더에 어떤 파일을 둘지, 컴포넌트를 어떻게 나눌지, API 관련 코드는 어디에서 관리할지 등을 정리해야 했는데, 혼자라면 제 방식대로 빠르게 정하고 넘어갔을 부분도, 서로가 이해할 수 있는 구조로 맞춰야 했습니다. 이 과정에서 일관된 컨벤션 규칙이 협업의 효율에 큰 영향을 준다는 것을 느꼈습니다.

또한 상대방의 작업에 피해를 주지 않기 위해 더 신중하게 작업하려고 했습니다. 커밋을 하기 전에는 불필요한 변경 사항이 포함되지 않았는지 확인하고, 브랜치를 이동할 때는 현재 작업 상태를 정리하려고 노력했습니다. 아직 익숙하지 않아 실수도 많았지만, 그만큼 협업에서는 작은 습관들이 중요하다는 것을 배웠습니다. 다음 프로젝트에서는 작업을 시작하기 전에 브랜치 전략, 커밋 메시지 규칙, 폴더 구조 등을 더 명확히 정하고 진행해야겠다고 느꼈습니다.

느낀점 및 배운점 (윤서)

제한된 일정 속에서 팀원과 호흡을 맞추며 협업의 물리적·심리적 밀도를 조율하는 과정이 쉽지만은 않았습니다. 특히 효율적인 커뮤니케이션을 위한 PR(Pull Request) 및 Issue 템플릿의 부재가 큰 아쉬움으로 남습니다.
초기에 컨벤션을 명확히 정의하는 '시스템 구축'의 중요성을 체감했으며, 이번 경험을 계기로 다음 프로젝트에서는 작업의 표준화를 선행하여 리소스 낭비를 줄이고 코드 퀄리티를 유지하는 데 집중할 계획입니다

Research Question

  • 컨벤션 정리: 컨벤션은 필수적으로 필요한 것이라 생각해서 자주 쓰던 컨벤션을 컨플루언스를 통해 공유했습니다.
image
  • 화면 분할: 시간적 여유가 없던 상태라 간단하게 랜딩과 메인, 검색과 상세 페이지로 나누어 작업한 후 한 명이 라우팅을 연결, 배포하였습니다.

Review Questions

  • React 18 버전의 변경점
  1. Server Component와 Client Component
    • Server Component: 클라이언트 앱이나 SSR 서버와 분리된 환경에서, 번들링 전에 미리 렌더링되는 새로운 종류의 컴포넌트
      • 서버에서 실행: 브라우저가 아니라 서버 환경에서 실행됨
      • 클라이언트 번들 감소: 컴포넌트 코드가 브라우저 JS 번들에 포함되지 않을 수 있음
      • 서버 리소스 접근 가능: DB, 파일 시스템, 서버 API 등에 접근하기 좋음
      • 상태/이벤트 사용 불가: useState, useEffect, onClick 같은 브라우저 상호작용 기능은 사용할 수 없음
      • 데이터 조회나 정적인 UI 렌더링에 적합
    • Client Component: 브라우저에서 실행되는 컴포넌트
      • 브라우저에서 실행: 사용자 화면에서 JS로 동작
      • 상태 사용 가능: useState, useReucer 등 사용 가능
      • 이벤트 사용 가능: onClick, onChange 등 사용 가능
      • 브라우저 API 사용 가능: window, localStorage 등 사용 가능
      • js 번들에 포함: 클라이언트로 전달되는 JavaScript 양이 늘어날 수 있음
      • 버튼 클릭, 입력값 관리, 모달 열기, 상태 변경처럼 사용자와 상호작용하는 UI에 적합
image
  1. lazy loading과 Suspence

    • lazy loading: 처음부터 모든 코드를 불러오지 않고, 필요한 시점에 특정 컴포넌트 코드를 불러오는 방식

      • React.lazy(): 컴포넌트를 필요할 때 비동기적으로 불러옴
      • : 로딩 중일 때 보여줄 대체 UI를 지정
      • fallback: 로딩 중 표시할 UI

      lazy로 불러온 컴포넌트가 아직 로딩 중이면 렌더링이 suspend되고, 이때 Suspense가 fallback UI를 보여줍니다.

  2. Automatic Batching

    • Automatic Batching: 여러 개의 상태 업데이트를 하나의 렌더링으로 묶어 처리하는 기능

      setTimeout(() => {
        setCount(count + 1);
        setName('React');
      }, 1000);
      • setTimeout, Promise, native event handler 등에서도 상태 업데이트가 자동으로 묶임
      • 위 코드에서 setCount, setName이 각각 실행되지만 렌더링은 한 번만 발생
image
  1. Cocurrent Rendering

    • Cocurrent Rendering: React가 여러 렌더링 작업을 더 유연하게 처리할 수 있도록 해주는 내부 렌더링 방식

      → React가 화면을 그리는 중에도 더 급한 작업이 들어오면 기존 작업을 잠시 멈추고 중요한 작업을 먼저 처리할 수 있음

      const [input, setInput] = useState('');
      const [list, setList] = useState([]);
      
      function handleChange(e) {
        setInput(e.target.value);
        setList(filterLargeList(e.target.value));
      }
      • 검색어 입력과 큰 리스트 필터링이 동시에 일어나면 화면이 버벅일 수 있기 때문에, 입력 반응성을 우선적으로 유지하고, 무거운 렌더링 작업은 뒤로 미루는 방식으로 최적화 가능

    ** 단, Concurrent Rendering은 개발자가 직접 동시성 렌더링을 실행하는 것이 아니라 React 내부에서 작동하는 기반이다. 개발자는 useTransition, Suspense, startTransition 같은 API를 통해 이를 활용해야 한다.

  2. useTransition과 startTransition

    • seTransitionstartTransition을 통해 업데이트 우선순위를 바꿀 수 있음

    • 예를 들어, 검색창에서 입력값은 즉시 반영되어야 하지만, 검색 결과 목록은 조금 늦게 바뀌어도 됨

      import { useState, useTransition } from 'react';
      
      function SearchPage() {
        const [input, setInput] = useState('');
        const [keyword, setKeyword] = useState('');
        const [isPending, startTransition] = useTransition();
      
        function handleChange(e) {
          const value = e.target.value;
          setInput(value);
      
          startTransition(() => {
            setKeyword(value);
          });
        }
      
        return (
          <>
            <input value={input} onChange={handleChange} />
            {isPending && <p>검색 결과 업데이트 중...</p>}
            <SearchResults keyword={keyword} />
          </>
        );
      }

    → 여기서 setInput(value)는 즉시 처리하고, setKeyword(value)는 조금 늦게 업데이트 가능

    → 사용자가 입력할 때 화면이 덜 버벅이고, 무거운 렌더링 작업은 뒤로 미룰 수 있음

  3. Streaming Server Rendering

    • React 18에서는 서버 렌더링에서도 Suspense를 더 잘 활용할 수 있음

    • 기존 SSR은 서버에서 전체 HTML을 다 만든 뒤 클라이언트로 보내는 방식에 가까워 일부 데이터가 늦게 준비되면 전체 페이지 응답이 지연될 수 있었으나, React 18의 streaming server rendering은 준비된 부분부터 먼저 브라우저로 보내고, 늦게 준비되는 부분은 나중에 이어서 보낼 수 있게 해줌

    • 예를 들어, 페이지 상단의 헤더와 기본 콘텐츠는 먼저 보여주고, 댓글 목록이나 추천 목록처럼 늦게 불러와도 되는 부분은 Suspense fallback으로 처리할 수 있음

      <Suspense fallback={<div>댓글을 불러오는 중...</div>}>
        <Comments />
      </Suspense>

      → 사용자는 전체 페이지가 완전히 준비될 때까지 기다리지 않고, 먼저 준비된 UI를 볼 수 있음

@minnngo minnngo changed the title [5주차] Jobdri 과제 제출합니다. [5주차] Jobdri 구민교 & 이윤서 과제 제출합니다. May 2, 2026
Comment thread src/app/detail/[id]/page.tsx Outdated
detail.original_name ??
detail.title ??
detail.name ??
"Previews"
Copy link
Copy Markdown

@YJ0623 YJ0623 May 3, 2026

Choose a reason for hiding this comment

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

체이닝으로 제목 설정하는 방식 좋네요

Comment thread src/app/detail/[id]/page.tsx Outdated
<div className="absolute top-[19.022px] left-1/2 h-[4.529px] w-[121.377px] -translate-x-1/2 rounded-[90.58px] bg-white" />
</div>
</section>
);
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 on lines 3 to 11
import React, { useState, useEffect } from "react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { DotLottieReact, DotLottie } from "@lottiefiles/dotlottie-react";

export default function NetflixLogoHandler() {
const router = useRouter();
const [dotLottie, setDotLottie] = useState<DotLottie | null>(null);
const [logoState, setLogoState] = useState<"image" | "lottie" | "finished">(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

혹시 여기 상태가 finished로 바뀌어도 로고 이미지 자체에는 아무런 변화가 없는 걸까요? 만약 맞다면, 아래 setTimeout함수 내부에 setLogoState는 사용하지 않으셔도 되는 것으로 보입니다.

Copy link
Copy Markdown

@waldls waldls left a comment

Choose a reason for hiding this comment

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

5주차 과제 하시느라 수고 많으셨습니다! 👍🏻👍🏻

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 public/icons/topten.svg
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 아이콘도 ic_topten.svg로 아이콘 네이밍 컨벤션 맞춰주시면 일관성 있을 것 같습니당

Comment on lines +6 to +8
const IMAGE_BASE =
process.env.NEXT_PUBLIC_TMDB_IMAGE_BASE_URL ?? "https://image.tmdb.org/t/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.

현재 주소 값이 MyListRow 등 여러 곳에서 중복 정의되어 있는 것 같습니다. 이 부분을 constants 파일로 분리하여 import해 사용하면 나중에 주소가 변경되더라도 한 곳에서만 수정하면 되니 훨씬 효율적일 것 같아용

Comment thread src/apis/tmdb.ts
Comment on lines +24 to +31

export async function getTopSearches(): Promise<TmdbTrendingItem[]> {
const headers = getTmdbHeaders();

if (!headers) {
return [];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

모든 API 함수에서 getTmdbHeaders를 호출하고 if (!headers) return [] 체크를 반복하고 있는데, 이를 커스텀 fetch 래퍼 함수 하나로 묶어보는 건 어떨까요? 공통 헤더 주입과 에러 처리를 한곳에서 관리하면 코드가 훨씬 간결해질 것 같습니다.

Comment thread src/components/NetflixLogoHandler.tsx Outdated
Comment on lines +35 to +43
// ⭐ 로고 이미지 클릭 핸들러
const handleImageClick = () => {
setIsImageFading(true); // 이미지 페이드아웃 시작

// 이미지 페이드아웃(500ms)이 끝난 후 로티로 교체
setTimeout(() => {
setLogoState("lottie");
}, 500);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

로티 애니메이션 실행 전에 페이드아웃을 적용 전환이 자연스럽게 느껴지게 한 점이 좋았습니다! setIsImageFading 상태와 setTimeout 을 활용해 전환 타이밍을 분리한 구조도 명확해서 읽기 좋았습니다.

YJ0623 referenced this pull request in YJ0623/next-netflix-23rd May 6, 2026
…/landingPage

Feat: 랜딩페이지 구현
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