Skip to content

[6주차 과제] 영화 목록 사이트 만들기#9

Open
hdg0116 wants to merge 57 commits into
mainfrom
week6/homework
Open

[6주차 과제] 영화 목록 사이트 만들기#9
hdg0116 wants to merge 57 commits into
mainfrom
week6/homework

Conversation

@hdg0116

@hdg0116 hdg0116 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

💡 과제

  • React + TypeScript
  • TanStack Query 사용
  • HTTP 클라이언트 라이브러리 사용 (axios, ky, ...)
  • ThemeProvider, GlobalStyle 사용 (CSS 라이브러리 사용)
  • any 사용 금지
  1. 영화 목록 페이지(메인 페이지)
  • 리스트 형태로 영화 데이터 불러오기
  • 별점을 통한 필터링 기능 구현
  • 각 영화 카드에는 포스터, 영화 제목, 개봉일, 줄거리 표시
  • 영화 카드 hover시 효과(확대 or 그림자 or 색상 변경 등 자유)
  • 영화 카드 클릭 시, 영화 상세 페이지로 이동
  • 무한 스크롤 구현
  1. 영화 상세 페이지
  • movieId 로 라우팅
  • 영화 포스터, 배경 이미지, 제목, 개봉일, 장르, 평점, 상영 시간, 투표 수, 상태, 줄거리 표시
  • 기본 정보(원제, 원어, 제작 국가, 사용 언어, 예산, 수익) 표시
  • ‘<- 목록으로 돌아가기’ 클릭 시 영화 목록 페이지로 이동
  1. 별점 남기기/삭제하기
  • 영화 상세 페이지에 존재
  • 이전에 매긴 별점이 없을 경우 비어 있으며, 있을 경우 해당 값으로 채워짐
  • 범위 밖 숫자 입력 시 저장 x
  • 성공시, 성공 메시지 하단에 출력
  • ‘별점 삭제하기’ 클릭 시, 별점 삭제

✍️ 구현 요약 및 새로 배운 점

이번 과제에서는 합세를 하면서 배운 것들(폴더 구조, API 렌더링 구조 등)을 최대한 녹여내려고 했던 것 같습니다.

전체 구조

기존에 구현하던 방식은 폴더 최상위 부분에서 모든걸 공통으로 내려주려고 하는 방식이었는데, 합세 때 배운 폴더구조로 도메인별로 각 컴포넌트와 API 호출을 나눠서 구현했습니다. 그리고 Axios 인스턴스에 API 키를 인터셉터로 자동 주입하는 방식으로 API 통신 계층을 분리했습니다.
이렇게 구현하니 props가 훨씬 줄어들고, 파일별 책임도 가벼워져 코드의 뜻을 잘 파악할 수 있게 된 것 같습니다.

메인 페이지

  • useInfiniteQuery + IntersectionObserver로 무한 스크롤을 구현했습니다.
  • 별점 필터 선택 시 vote_average.gte / vote_average.lte 파라미터를 이용해 API를 호출했고, 빈 문자열이면 파라미터 자체를 생략해 전체 조회가 가능하도록 구현했습니다.
  • Card 컴포넌트는 Link로 상세 페이지로 이동하도록 했고, line-clamp-4로 overview 말줄임표를 적용하여 줄거리 내용이 길 때 높이 제한이 가능하도록 적용했습니다. 또한 호버 시 scale-102 애니메이션 적용을 통해 카드가 커지도록 했고, 줄거리가 없을 경우 '데이터가 없습니다.'라는 메세지가 뜨도록 했습니다.

영화 상세 페이지

  • HeroSection, InfoSection, OverviewSection, RatingSection으로 섹션 분리 후 각 섹션별로 사용되는 API만 각각 호출하도록 구현했습니다.
  • React Router의 useParams로 영화 ID를 추출하고 목록으로 돌아가기 버튼 클릭 시 useNavigate('/')로 영화 목록 페이지로 이동하도록 구현했습니다.

별점 기능

게스트 세션 발급

  • Guest Session ID 발급 API로 로그인 없이 별점을 저장/삭제할 수 있도록 구현했습니다.
  • getGuestSession 함수 호출 시 localStorage에 기존 세션 ID가 있으면 API를 호출하지 않고, 캐싱된 값을 바로 반환해 불필요한 재발급을 방지했습니다.

별점 저장 / 수정

  • handleSubmit에서 value가 비어있거나 0.5 미만 / 10.0 초과이면 early return으로 API 호출을 제한하도록 했습니다.
  • localStorage에 해당 영화의 별점(rating_{movieId} 키)이 이미 존재하는지 확인하고 신규 저장(RATING_SAVED)과 수정(RATING_UPDATED) 메시지를 구분해서 렌더링하도록 구현했습니다.
  • 저장 성공 후 localStorage에 별점 값을 기록하고 다음 진입 시 저장된 값을 input 초기값으로 복원하도록 구현했습니다.

별점 삭제

  • handleDelete 진입 시 localStorage에 해당 영화의 별점이 없으면 API 호출을 제한하도록 구현했습니다.
  • deleteMovieRating 함수는 동일 엔드포인트에 DELETE 요청을 보내고, 성공 시 localStorage 항목을 제거하고 input 값을 빈 문자열로 초기화하도록 했습니다.

💦 구현 과정에서 어려웠던 & 고민했던 부분

  • 영화 카드의 이미지를 렌더링할 때 처음에 테일윈드 클래스로 높이를 지정했는데, 이미지가 위쪽으로 올라가서 잘려보이는 문제가 있었습니다. 알고보니 부모 div의 flex 조건이 없어서 상단으로 올라가보이는 현상이 원인이었고, flex 적용 후 다시 높이를 적용하니 정상적으로 보이게 해결됐습니다. 이후에 고정 높이를 적용하기 위해 400px로 지정 후 클래스에 영향받지 않도록 수정했습니다.
  • 별점 상태를 API에서 가져올지, localStorage에서 가져올지 고민했던 것 같습니다. GET /guest_session/{id}/rated/movies 엔드포인트 API를 통해 저장한 별점을 불러오려고 했는데, 별점 상세 페이지 진입 시마다 이걸 호출해 초기값을 세팅하는 대신 localStorage를 사용해 영구적으로 저장하고 삭제하는 로직이 더 효율적이라고 생각해 localStorage 구조를 선택해 구현했습니다.

🙋‍♀️ 리뷰 요청 포인트 & 질문

  • 헤더를 공통 위치로 분리할지 main 도메인 폴더 아래로 분리할지 고민했는데, 사소한 부분이지만 어느 위치가 더 좋을지 궁금합니다.
  • 위에 고민했던 부분에 작성했던 별점 상태 값을 어떻게 가져오는게 더 나은 방법일지 궁금합니다.

📷 결과물

https://internal-beach-57c.notion.site/6-37271b55803980c59ed8c9d4a47cff50?source=copy_link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant