Skip to content
Open
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
39dafc9
docs(readme): 기능 요구사항 초안 작성
bigcloud07 Mar 31, 2026
b3296d5
docs(readme): 서비스 전체 시나리오 작성
bigcloud07 Mar 31, 2026
ab1d8e0
docs(readme): 시나리오1 수정
bigcloud07 Mar 31, 2026
50cb4e5
test: 사용 시나리오 의사코드 작성
bigcloud07 Mar 31, 2026
6b8c140
feat: index.html 템플릿 파일 적용
bigcloud07 Mar 31, 2026
ea14686
test: E2E 테스트 작성완료
bigcloud07 Mar 31, 2026
a4d44aa
feat: 영화 목록 조회 기능 추가
bigcloud07 Apr 1, 2026
236c392
refactor: 영화 목록 조회 기능 1차 리팩토링
bel1c10ud Apr 1, 2026
4cee2c7
feat: 영화 검색 기능 추가
bel1c10ud Apr 2, 2026
aa8e270
test: e2e 테스트 문법 에러 수정
bel1c10ud Apr 2, 2026
71d65a9
docs(readme): 구현 현황 업데이트
bel1c10ud Apr 2, 2026
8d21654
feat: 토스트 기능 추가
bel1c10ud Apr 2, 2026
6efc6e7
feat: API 응답 시간이 길어지는 경우 예외를 발생시키는 동작 추가
bel1c10ud Apr 2, 2026
e9f4f06
docs(readme): API 요청에 실패하거나 응답이 길어져서 실패하는 경우에 대한 시나리오 추가
bel1c10ud Apr 2, 2026
80ee884
test: API 응답에 실패하거나 응답이 길어져 실패하는 경우에 대한 모킹 및 테스트 추가
bel1c10ud Apr 2, 2026
071708c
refactor: 하드코딩된 URL 환경 변수로 분리
bel1c10ud Apr 2, 2026
bbb2389
feat: 더보기 버튼을 짧은 주기로 누르는 경우 무시하도록 쓰로틀 추가
bel1c10ud Apr 2, 2026
60aebbe
chore: 파비콘 추가
bel1c10ud Apr 2, 2026
487ba7c
fix: 배포 환경 대응
bel1c10ud Apr 2, 2026
89a4dca
fix: 로컬 환경에서 평점 아이콘이 정상적으로 출력되지 않는 문제 해결
bel1c10ud Apr 2, 2026
8378310
fix: 배포 환경에 search 페이지가 포함되지 않는 문제 해결
bel1c10ud Apr 2, 2026
3cd1d57
fix: 이미지에 base url이 적용되어있지 않았던 부분 수정
bel1c10ud Apr 2, 2026
3aa31f9
fix: 잘못된 mock 반환값 수정
bel1c10ud Apr 5, 2026
379526d
fix: 누락된 img alt 추가
bel1c10ud Apr 5, 2026
259e7bb
fix: 잘못된 마크업 구조 개선
bel1c10ud Apr 5, 2026
110e656
fix: 오타 수정
bel1c10ud Apr 5, 2026
0e05eeb
feat: 커스텀 에러 클래스 도입 및 에러 핸들링 개선
bel1c10ud Apr 5, 2026
4072641
test: 변경된 에러 형식에 대응하도록 테스트 수정
bel1c10ud Apr 5, 2026
a25542c
docs(readme): 구현 현황 업데이트
bel1c10ud Apr 5, 2026
425ace6
fix: API 요청 실패시 skeleton이 사라지지 않는 문제 해결
bel1c10ud Apr 5, 2026
fbca497
test: 더보기 버튼과 Skeleton UI 테스트 추가
bel1c10ud Apr 5, 2026
f519882
refactor: render 관련 유틸 함수 분리
bel1c10ud Apr 5, 2026
391401b
chore: 함수 이름과 일치하지 않는 파일 이름 수정
bel1c10ud Apr 5, 2026
07ced67
refactor: 에러 핸들링 개선
bel1c10ud Apr 10, 2026
99b0ae3
docs(readme): 2단계 기능 요구사항 작성
bel1c10ud Apr 11, 2026
f740937
feat: 영화 상세 정보 모달 구현
bel1c10ud Apr 11, 2026
b23b175
feat: 별점 매기기 기능 구현
bel1c10ud Apr 11, 2026
4b01e75
feat: 최상단 영화 클릭시 모달이 띄워지는 기능 추가
bel1c10ud Apr 11, 2026
ed8013b
feat: 영화 목록 무한 스크롤 적용
bel1c10ud Apr 11, 2026
b4667b5
feat: 반응형 디자인 적용
bel1c10ud Apr 11, 2026
543aa6a
feat: 양방향 무한 스크롤 구현
bel1c10ud Apr 12, 2026
c9f8c24
feat: 영화 상세 정보 모달 비동기 로딩 스피너 추가
bel1c10ud Apr 12, 2026
5294f49
feat: 평점 메시지 추가
bel1c10ud Apr 12, 2026
86d6791
fix: 최상단 영화가 정상적으로 갱신되지않는 문제 해결
bel1c10ud Apr 12, 2026
ab7c31a
docs(readme): 구현 현황 업데이트
bel1c10ud Apr 12, 2026
feb0712
feat: 상단 무한 스크롤 적용
bel1c10ud Apr 12, 2026
2884bd6
refactor: 반응형 디자인 개선
bel1c10ud Apr 12, 2026
e5ed376
fix: 잘못된 에러 핸들링 및 비동기 처리 수정
bel1c10ud Apr 12, 2026
bf31206
docs(readme): 사용 시나리오 업데이트
bel1c10ud Apr 13, 2026
7373d6c
test: 사용 시나리오 기반으로 E2E 테스트 업데이트
bel1c10ud Apr 13, 2026
567c312
feat: 테스트 환경에서 ESC키를 사용해 모달을 닫을 수 있도록 이벤트 핸들러 추가
bel1c10ud Apr 13, 2026
6c50efd
feat: 모달이 열려있는 경우 스크롤할 수 없도록 처리
bel1c10ud Apr 13, 2026
6aaa165
refactor: 에러 핸들링 개선
bel1c10ud Apr 13, 2026
8995f5e
fix: 모달이 열릴때 스크롤이 상단으로 이동하는 문제 해결
bel1c10ud Apr 13, 2026
af04faf
Merge remote-tracking branch 'upstream/bel1c10ud' into bel1c10ud
bel1c10ud Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.env
120 changes: 120 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,123 @@
# javascript-movie-review

FE 레벨1 영화 리뷰 미션

## 페어

@bigcloud07
@bel1c10ud

# 서비스 전체 시나리오 작성

## [시나리오 1] Movie List 탐색 - 무한 스크롤

User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달하면 다음 영화 목록을 불러온다

## [시나리오 1-1] Movie List 탐색 - 로딩 중 스켈레톤 UI 표시

User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달하면 다음 영화 목록을 불러온다 -> 스켈레톤 UI가 표시된다.

## [시나리오 1-2] Movie List 탐색 - 마지막 페이지 도달시 추가 로드 없음

User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달한다 -> 마지막 페이지 도달 -> 더 이상 영화 목록을 불러오지 않는다

## [시나리오 2] 검색 - 결과 있음

User -> Main Page -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다

## [시나리오 2-1] 검색 - 결과 있는 경우의 재검색

User -> Main Page -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다 -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다

## [시나리오 3] 검색 - 결과 없음

User -> Main Page -> 검색창에 "$$%@@" 입력 -> 검색 버튼 클릭 -> "결과가 없습니다" 메시지가 보인다

## [시나리오 3-1] 검색 - 결과 없는 경우의 재검색

User -> Main Page -> 검색창에 "$$%@@" 입력 -> 검색 버튼 클릭 -> "결과가 없습니다" 메시지가 보인다 -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다

## [시나리오 4] 에러 - 영화 조회 API 요청이 실패했을 때

User -> Main Page -> API 요청 실패 -> 화면 하단에 "API 에러" 타이틀과 에러 메세지를 담은 토스트를 띄운다.

## [시나리오 5] 에러 - 영화 조회 API 요청에 대한 응답이 길어졌을 때

User -> Main Page -> API 요청 -> 응답 대기 시간이 10초를 넘어간다 -> 화면 하단에 "API 에러" 타이틀과 "API 응답 시간이 10000ms를 초과했습니다." 메세지를 담은 토스트를 띄운다.

## [시나리오 6] 영화 상세 정보 조회 - 모달 열기

User -> Main Page -> 영화 카드 클릭 -> 영화 상세 정보가 모달로 표시된다

## [시나리오 6-1] 영화 상세 정보 조회 - 닫기 버튼으로 모달 닫기

User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> 닫기 버튼 클릭 -> 모달이 닫힌다

## [시나리오 6-2] 영화 상세 정보 조회 - ESC 키로 모달 닫기

User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> ESC 키 입력 -> 모달이 닫힌다

## [시나리오 7] 별점 매기기

User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> 별점 선택 -> 별점이 저장된다

## [시나리오 7-1] 별점 유지 - 새로고침 후

User -> Main Page -> 영화 카드 클릭 -> 별점 선택 -> 페이지 새로고침 -> 같은 영화 클릭 -> 이전에 선택한 별점이 유지된다

## [시나리오 8] URL 페이지 파라미터로 접속 - 해당 페이지 로드

User -> URL에 page=2를 포함하여 접속 -> 2페이지의 영화 목록이 표시된다

## [시나리오 8-1] URL 페이지 파라미터 접속 후 상단 무한 스크롤

User -> URL에 page=2를 포함하여 접속 -> 2페이지 영화 목록이 표시된다 -> 상단으로 스크롤 -> 1페이지 영화 목록이 추가로 표시된다

## [시나리오 8-2] URL 페이지 파라미터 접속 후 하단 무한 스크롤 - 마지막 페이지

User -> URL에 page=2(마지막 페이지)를 포함하여 접속 -> 2페이지 영화 목록이 표시된다 -> 하단으로 스크롤 -> 더 이상 영화 목록을 불러오지 않는다

## [시나리오 9] 새로고침 후 스크롤 위치 복원

User -> Main Page -> 영화 목록 탐색 -> 특정 영화가 화면에 들어옴 -> URL에 해당 영화의 ID와 페이지가 기록된다 -> 새로고침 -> 이전에 화면에 보였던 영화 위치로 자동 스크롤된다

# 기능 요구 사항

## 🎬 영화 목록 조회

- [x] 영화 목록의 1페이지를 불러오며 페이지끝에 도달하면 그 다음의 영화 목록을 불러 올 수 있다.
- [x] 영화는 한 번의 요청당 20개씩 영화 목록을 보여준다.
- [x] 영화 목록을 불러오는 동안 Skeleton UI 표시

## 🔎 검색

- [x] 영화 검색 API를 이용하여 내가 보고 싶은 영화를 검색할 수 있다. 엔터키를 눌러 검색할 수 있다.
- [x] 검색 버튼을 클릭하여 검색할 수 있다
- [x] 영화 목록 조회와 같이 검색한 결과에 한해 정보를 보여주는 화면의 요구사항은 동일하다

## 📺 영화 상세 정보 조회

- [x] 영화 포스터나 제목 또는 메인 화면의 자세히 보기를 누른경우 영화 상세 정보를 모달로 출력한다.
- [x] 닫기 버튼 또는 ESC를 누르면 모달이 닫힌다.
- [x] 모달창을 반응형 레이아웃으로 구현한다.
- [x] 사용자는 영화에 대해 별점을 남길 수 있다
- [x] 별점은 5개로 구성되어있으며 한개장 2점이다, 1점 단위는 고려하지 않는다.
- [x] 새로고침하더라도 별점은 유지되어야 한다
- [x] localStorag로 구현하돼 서버 API로 쉽고 안전하게 갈아끼울 수 있는 구조로 개발하여야한다.

## 공통

### 예외처리

> 에러가 발생했을 때, 해당 내용에 해당하는 에러 메세지를 하단 toast UI를 통해 보여준다.

- [x] 영화 조회 API 요청이 실패 했을 때
- [x] 영화 조회 API 요청에 대한 응답이 길어졌을 때

### 프로그래밍 요구사항

- 테스트 전략을 세우고, 단위 테스트(vitest), E2E 테스트(cypress)를 진행한다.
- 핵심이 되는 기능이라고 생각하는 기능 플로우를 선정하고 그에 대한 E2E 테스트를 추가한다.
- E2E 테스트 도구를 이용하여 UI Test를 진행해 본다.
- 비동기 통신에서 일어날 수 있는 다양한 상황을 고려해 본다.
- 예외 처리가 발생하는 경우에 사용자를 위한 메세지를 띄워 사용자에게 알려준다.
201 changes: 199 additions & 2 deletions cypress/e2e/spec.cy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,202 @@
describe("template spec", () => {
it("passes", () => {
const TMDB_POPULAR_URL_PATTERN = "**/movie/popular**";
const TMDB_SEARCH_URL_PATTERN = "**/search/movie**";
const TMDB_MOVIE_DETAIL_URL_PATTERN = /\/movie\/\d+/;

describe("Movie App", () => {
beforeEach(() => {
cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (req) => {
const url = new URL(req.url);
const page = url.searchParams.get("page");
req.reply({ fixture: page === "1" ? "popular-movies-p1.json" : "popular-movies-p2.json" });
}).as("popularMovies");

cy.intercept("GET", TMDB_SEARCH_URL_PATTERN, (req) => {
const url = new URL(req.url);
const query = url.searchParams.get("query");
req.reply({ fixture: query === "$$%@@" ? "empty-search.json" : "search-results.json" });
}).as("searchMovies");

cy.visit("localhost:5173");
cy.wait("@popularMovies");
Comment on lines 19 to +20
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI 'cypress\.config\.(ts|js|mjs|cjs)$' | while read -r file; do
  echo "### $file"
  sed -n '1,200p' "$file"
done

echo
echo "### cy.visit 사용 위치"
rg -n 'cy\.visit\(' cypress

Repository: woowacourse/javascript-movie-review

Length of output: 840


cy.visit()에 프로토콜이 없는 URL을 넘기고 있습니다.

cypress.config.tsbaseUrl이 정의되지 않았는데, cy.visit("localhost:5173")처럼 프로토콜 없이 호출하면 상대 경로로 해석돼 테스트가 실패합니다.

두 가지 방법으로 수정할 수 있습니다:

  1. cypress.config.tsbaseUrl: "http://localhost:5173"을 추가하고 cy.visit("/")cy.visit("/?page=2")처럼 호출
  2. 모든 cy.visit() 호출을 cy.visit("http://localhost:5173")처럼 절대 URL(프로토콜 포함)로 변경

지금 패턴이 이 파일 전반에 반복되고 있으니, 어느 접근이 프로젝트에 맞는지 판단한 후 일관되게 적용해 주세요.

Also applies to: 96-97, 107-107, 162-163, 172-173, 177-178, 183-184, 193-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypress/e2e/spec.cy.ts` around lines 19 - 20, The cy.visit calls in this spec
use URLs without a protocol (e.g., cy.visit("localhost:5173")), which are
treated as relative because cypress.config.ts has no baseUrl; either add
baseUrl: "http://localhost:5173" to cypress.config.ts and change calls like
cy.visit("localhost:5173") to cy.visit("/") or cy.visit("/?page=2"), or update
every cy.visit(...) in this file (including the occurrences paired with
cy.wait("@popularMovies") and other visits at the lines cited) to use absolute
URLs with protocol (e.g., "http://localhost:5173"); choose one approach and
apply it consistently across all cy.visit calls in this file.

});

context("Movie List 탐색 - 무한 스크롤", () => {
it("초기 목록 20개 표시 후 페이지 끝 스크롤 시 40개 표시", () => {
cy.get(".item").should("have.length", 20);
cy.get(".scroll-area").scrollIntoView();
cy.wait("@popularMovies");
cy.get(".item").should("have.length", 40);
});

it("페이지 끝 스크롤 시 로딩 중 스켈레톤 UI 표시", () => {
cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (req) => {
req.reply({ fixture: "popular-movies-p2.json", delay: 1000 });
}).as("delayedPopularMovies");
cy.get(".scroll-area").scrollIntoView();
cy.get(".skeleton-item").should("exist");
cy.wait("@delayedPopularMovies");
cy.get(".skeleton-item").should("not.exist");
});

it("마지막 페이지 도달 시 하단 스크롤 트리거 요소가 제거된다", () => {
cy.get(".scroll-area").scrollIntoView();
cy.wait("@popularMovies");
cy.get(".scroll-area").should("not.exist");
});
});

context("검색 - 결과 있음", () => {
it("검색 결과가 1개 이상 표시된다", () => {
cy.get(".search-input").type("Inception");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.get(".item").its("length").should("be.gte", 1);
});
});

context("검색 - 결과 있는 경우의 재검색", () => {
it("재검색 시에도 결과가 1개 이상 표시된다", () => {
cy.get(".search-input").type("Inception");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.get(".item").its("length").should("be.gte", 1);
cy.get(".search-input").clear().type("Inception");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.get(".item").its("length").should("be.gte", 1);
});
});

context("검색 - 결과 없음", () => {
it("결과 없음 메시지가 표시된다", () => {
cy.get(".search-input").type("$$%@@");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.contains("검색 결과가 없습니다");
});
});

context("검색 - 결과 없는 경우의 재검색", () => {
it("결과 없음 후 재검색 시 결과가 표시된다", () => {
cy.get(".search-input").type("$$%@@");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.contains("검색 결과가 없습니다");

cy.get(".search-input").clear().type("Inception");
cy.get(".search-submit").click();
cy.wait("@searchMovies");
cy.get(".item").its("length").should("be.gte", 1);
});
});

context("API 요청 실패", () => {
it("영화 API 실패 시 토스트 에러가 표시된다", () => {
cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, { statusCode: 500 }).as("failedPopularMovies");
cy.visit("localhost:5173");
cy.wait("@failedPopularMovies");
cy.get(".toast").should("be.visible");
});
});

context("API 요청 타임아웃", () => {
it("응답이 지연되면 타임아웃 토스트 에러가 표시된다", () => {
cy.clock();
cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (_req) => {
}).as("pendingRequest");
cy.visit("localhost:5173");
cy.tick(10001);
cy.get(".toast").should("be.visible");
});
});

context("영화 상세 정보 조회 - 모달", () => {
beforeEach(() => {
cy.intercept("GET", TMDB_MOVIE_DETAIL_URL_PATTERN, { fixture: "movie-detail.json" }).as("movieDetail");
});

it("영화 클릭 시 상세 정보 모달이 표시된다", () => {
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".modal[open]").should("exist");
cy.get(".modal-movie-title").should("exist");
});

it("닫기 버튼 클릭 시 모달이 닫힌다", () => {
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".modal[open]").should("exist");
cy.get(".modal-close-button").click();
cy.get(".modal[open]").should("not.exist");
});

it("ESC 키 입력 시 모달이 닫힌다", () => {
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".modal[open]").should("exist");
cy.get(".modal[open]").trigger("keydown", { key: "Escape", keyCode: 27, which: 27 });
cy.get(".modal[open]").should("not.exist");
});
});

context("별점 매기기", () => {
beforeEach(() => {
cy.intercept("GET", TMDB_MOVIE_DETAIL_URL_PATTERN, { fixture: "movie-detail.json" }).as("movieDetail");
});

it("별점 선택 시 localStorage에 별점이 저장된다", () => {
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".star-button[data-rating='8']").click();
cy.window().then((win) => {
const ratings = JSON.parse(win.localStorage.getItem("my-ratings") ?? "{}");
expect(ratings[1523145]).to.equal(8);
});
});

it("새로고침 후에도 선택한 별점이 유지된다", () => {
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".star-button[data-rating='8']").click();
cy.get(".modal-close-button").click();
cy.visit("localhost:5173");
cy.wait("@popularMovies");
cy.get(".item:not(.skeleton-item)").first().click();
cy.wait("@movieDetail");
cy.get(".modal-movie-my-rating-selector").should("have.attr", "data-rating", "8");
});
});

context("URL 페이지 파라미터 기반 페이지 로드", () => {
it("page=2 URL로 접속 시 첫 번째 API 요청이 2페이지 데이터를 요청한다", () => {
cy.visit("localhost:5173?page=2");
cy.wait("@popularMovies").its("request.url").should("include", "page=2");
});

it("page=2(마지막 페이지) 접속 시 하단 무한 스크롤 트리거 요소가 존재하지 않는다", () => {
cy.visit("localhost:5173?page=2");
cy.wait("@popularMovies");
cy.get(".scroll-area").should("not.exist");
});

it("page=2 접속 후 상단 스크롤 시 1페이지 영화 목록이 추가로 불러와진다", () => {
cy.visit("localhost:5173?page=2");
cy.wait("@popularMovies");
cy.wait("@popularMovies");
cy.get(".item").should("have.length", 40);
});
});

context("스크롤 위치 복원", () => {
it("viewed-movie-id URL 파라미터로 접속 시 해당 영화가 뷰포트에 표시된다", () => {
const targetMovieId = "83533";
cy.visit(`localhost:5173?page=1&viewed-movie-id=${targetMovieId}`);
cy.wait("@popularMovies");
cy.get(`[data-movie-id="${targetMovieId}"]`).should(($el) => {
const rect = $el[0].getBoundingClientRect();
expect(rect.top, "영화가 뷰포트 상단 아래에 있어야 함").to.be.lt(Cypress.config("viewportHeight") as number);
expect(rect.bottom, "영화가 뷰포트 상단 위에 있어야 함").to.be.gt(0);
});
});
});
});
6 changes: 6 additions & 0 deletions cypress/fixtures/empty-search.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"page": 1,
"total_pages": 1,
"total_results": 0,
"results": []
}
31 changes: 31 additions & 0 deletions cypress/fixtures/movie-detail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"id": 1523145,
"title": "Твоё сердце будет разбито",
"original_title": "Твоё сердце будет разбито",
"overview": "테스트용 줄거리입니다. 이 영화에 대한 상세 설명이 여기에 표시됩니다.",
"release_date": "2026-03-26",
"genres": [
{ "id": 10749, "name": "로맨스" },
{ "id": 18, "name": "드라마" }
],
"popularity": 1037.0135,
"vote_average": 6.6,
"vote_count": 27,
"poster_path": "/iGpMm603GUKH2SiXB2S5m4sZ17t.jpg",
"backdrop_path": "/1x9e0qWonw634NhIsRdvnneeqvN.jpg",
"adult": false,
"video": false,
"original_language": "ru",
"belongs_to_collection": null,
"budget": 0,
"homepage": "",
"imdb_id": "",
"origin_country": ["RU"],
"production_companies": [],
"production_countries": [],
"revenue": 0,
"runtime": 90,
"spoken_languages": [],
"status": "Released",
"tagline": ""
}
Loading