Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
863f9d8
docs: update README with common requirements and feature list
DongEun02 Mar 31, 2026
bdd020b
chore: .gitignore에 .env 추가
DongEun02 Mar 31, 2026
1edd5a7
feat: vite-env.d.ts 파일 추가 및 ImportMetaEnv 인터페이스 정의
DongEun02 Mar 31, 2026
751ca4e
feat: Movie 인터페이스 정의 추가
DongEun02 Mar 31, 2026
5ae1cf6
feat: TMDB API에서 인기 영화 목록을 가져오는 테스트 추가
DongEun02 Mar 31, 2026
5da28e5
feat: fetchMovies 함수 호출 시 인자 추가 및 fetch 모의 구현 개선
DongEun02 Mar 31, 2026
e85036a
feat: fetchMovies 함수 추가하여 인기 영화 목록을 가져오는 기능 구현
DongEun02 Mar 31, 2026
7d89949
feat: MovieCard 클래스 추가 및 영화 카드 렌더링 기능 구현
DongEun02 Mar 31, 2026
c709089
feat: index.html 및 main.ts에 영화 목록 렌더링을 위한 구조 추가
DongEun02 Mar 31, 2026
04f9bcc
feat: fetchMovies 함수의 반환 타입에 total_pages 추가 및 테스트 수정
DongEun02 Apr 1, 2026
14311d5
feat: fetchMovies 함수의 인자 추가 및 페이지네이션 기능 구현
DongEun02 Apr 1, 2026
a13e62f
test: 검색 API 성공 시 데이터 반환 테스트 추가
DongEun02 Apr 1, 2026
7bf042b
feat: 검색 기능 추가 및 검색 버튼 구현
DongEun02 Apr 1, 2026
5b9fb51
feat: Header 컴포넌트 추가 및 영화 정보 렌더링 기능 구현
DongEun02 Apr 1, 2026
f7fc623
feat: Header 컴포넌트 렌더링 및 스타일 수정, Movie 인터페이스에 backdrop_path 추가
DongEun02 Apr 1, 2026
b3fe852
feat: 스타일 수정 및 썸네일 그리드 레이아웃 반응형 추가
DongEun02 Apr 1, 2026
1c96936
feat: MovieSkeleton 컴포넌트 추가 및 렌더링 기능 구현
DongEun02 Apr 1, 2026
6e3893a
feat: 검색 결과 없음 상태 표시를 위한 UI 추가 및 스타일 수정
DongEun02 Apr 1, 2026
bf6f923
refactor: TMDB API 관련 상수 추가
DongEun02 Apr 1, 2026
f2cdb57
refactor: MovieList 컴포넌트 추가
DongEun02 Apr 1, 2026
9ee21e9
refactor: 경로 수정 및 상수 통합
DongEun02 Apr 1, 2026
38bf333
refactor: src 폴더에 images, styles 이동
DongEun02 Apr 1, 2026
98ef4c3
refactor: fetchMovies 함수 이름 변경 및 변수 네이밍 변경
DongEun02 Apr 1, 2026
f40fe61
style: main 요소에 min-height 추가 및 logo에 cursor 스타일 추가
DongEun02 Apr 2, 2026
208c6e7
fix: THUMB_NAIL_URL 이미지 URL 교체
DongEun02 Apr 2, 2026
a5c5cb2
feat: Header 컴포넌트에 renderEmpty 및 renderSearch 메서드 추가
DongEun02 Apr 2, 2026
5909e46
refactor: MovieList 및 main.ts에서 renderEmpty 메서드 통합 및 이벤트 리스너 수정
DongEun02 Apr 2, 2026
d902a1c
refactor: HomeButton, Header, MovieList 및 main.ts에서 메서드 이름 변경 및 구조 개선
DongEun02 Apr 2, 2026
ee71282
refactor: API 호출을 위한 핸들러 및 상수 추가, 메인.ts에서 초기 렌더링 로직 개선
DongEun02 Apr 2, 2026
ee19b29
test: 영화 리뷰 웹 E2E 테스트 추가
DongEun02 Apr 2, 2026
94bb8dc
docs: README.md의 기능 요구사항 체크리스트 완료 표시 추가
DongEun02 Apr 2, 2026
3ae5733
docs: 배포링크 추가
janghw0126 Apr 2, 2026
ddc3d19
docs: deploy 파일 추가
janghw0126 Apr 2, 2026
8a57bcd
dosc: 리뷰 요청 내용 작성
janghw0126 Apr 2, 2026
da8afc4
refactor: 상태 관리 로직 분리 및 영화 검색 기능 개선
janghw0126 Apr 6, 2026
54f7444
refactor: 더보기 버튼 렌더링 로직 모듈화
janghw0126 Apr 6, 2026
72c1a07
refactor: 로고 클릭과 더보기 버튼 클릭 이벤트 로직 분리 및 재귀 제거
janghw0126 Apr 6, 2026
314d094
refactor: 인기 영화 및 검색 쿼리를 위한 API 호출 함수와 핸들러 수정
janghw0126 Apr 7, 2026
a9ffc99
fix: 로고 클릭 시 홈 화면 이동 문제 해결 및 UI 초기화
janghw0126 Apr 7, 2026
4131b17
chore: 필요없는 코드 삭제
janghw0126 Apr 7, 2026
0a9a732
refactor: 스켈레톤 렌더링 로직을 데이터 핸들러에서 컨트롤러로 이동
janghw0126 Apr 7, 2026
2875712
fix: 컨트롤러 계층에 에러 핸들링 추가 및 UI 예외 처리
janghw0126 Apr 7, 2026
84fc2eb
refactor: UI 모듈 통합 및 renderHandlers로 DOM 조작 로직 위임
janghw0126 Apr 7, 2026
27fcc1d
test: TMDB API 테스트 코드 리팩토링 및 에러 케이스 추가
janghw0126 Apr 7, 2026
492ae91
refactor: 함수명 및 변수명을 역할에 맞게 명확하게 개선
janghw0126 Apr 7, 2026
b6b9abe
chore : 폴더명 UI에서 View로 변경
janghw0126 Apr 7, 2026
c64a793
refactor: View 컴포넌트가 직접 DOM 요소를 생성하도록 변경
janghw0126 Apr 7, 2026
f97eec0
refactor: DOM 조작을 handler에서 View로 이동
janghw0126 Apr 7, 2026
20b4a23
refactor: 폴더 구조 재구성
janghw0126 Apr 7, 2026
fb0c6b0
refactor: submit 이벤트를 main.ts에서 eventHandler로 이동
janghw0126 Apr 7, 2026
569865e
test: 코드 변경에 따른 테스트 수정
janghw0126 Apr 7, 2026
c0cbfe3
feat: 에러 발생 시 UI를 통한 에러 메시지 렌더링 구현
janghw0126 Apr 7, 2026
02c564a
fix: vote_average가 undefined일 때 toFixed 호출 오류 수정
janghw0126 Apr 8, 2026
57f2cc2
refactor: initEvents 내부 이벤트 핸들러를 함수로 분리
janghw0126 Apr 9, 2026
6217a9f
fix: step1 변경사항 stpe2로 병합
janghw0126 Apr 11, 2026
328b04f
docs: step2 기능목록 작성
janghw0126 Apr 12, 2026
dd26658
docs: 기존 README.md 삭제
janghw0126 Apr 12, 2026
923dc35
feat: 영화 상세 정보 렌더링 함수 추가
janghw0126 Apr 12, 2026
55111b8
feat: movie-card 클래스 및 data-id 속성 추가
janghw0126 Apr 12, 2026
e86d40f
feat: 영화 상세 정보 API 연동 및 카드 클릭 이벤트 연결
janghw0126 Apr 12, 2026
d4754f7
feat: 영화 상세 정보 타입 지정
janghw0126 Apr 12, 2026
d4a9412
feat: 영화 상세 정보 모달 클래스 인스턴스화 및 스타일 연결
janghw0126 Apr 12, 2026
c7086f7
feat: 영화 상세 모달 UI 구조 개선 및 css 구현
janghw0126 Apr 12, 2026
2d0d144
refactor: 모달 활성화 로직을 MovieDetailModal 클래스 내부로 이동
janghw0126 Apr 12, 2026
df37190
style: 모달 창 스타일 적용
janghw0126 Apr 12, 2026
08d6b9e
feat: 모달 닫기 기능 구현 (닫기 버튼, 배경 클릭, ESC 키)
janghw0126 Apr 12, 2026
047d138
feat: 영화 별점 부여 기능 구현
janghw0126 Apr 12, 2026
a9da5dc
fix: 모달 닫기 버튼에 직접 이벤트 리스너 등록
janghw0126 Apr 12, 2026
74b69ac
refactor: 더보기 버튼 관련 코드 제거 및 무한 스크롤 상태 추가
janghw0126 Apr 12, 2026
a2e85cf
feat: 더보기 버튼 제거 후 IntersectionObserver 기반 무한 스크롤 구현
janghw0126 Apr 12, 2026
997c4a9
refactor: 영화목록 썸네일 별 크기 조정
janghw0126 Apr 13, 2026
baac64f
fix: 썸네일 별점과 모달 별점 클래스명 분리
janghw0126 Apr 13, 2026
7e52b38
fix: 헤더 overlay보다 낮은 모달 z-index로 인한 모달 닫기 불가 버그 수정
janghw0126 Apr 13, 2026
d33904d
style: 영화 줄거리 스크롤 크기 조정
janghw0126 Apr 13, 2026
7c01694
style: 데스크톱/태블릿/모바일 반응형 레이아웃 적용
janghw0126 Apr 13, 2026
b7f223f
style: 태블릿 bottom sheet 및 모바일 모달 레이아웃 개선
janghw0126 Apr 13, 2026
29de8c2
test: E2E 테스트 케이스 모달/별점/무한 스크롤 기준으로 재작성
janghw0126 Apr 13, 2026
c75cab1
docs: 기능 구현 목록 체크
janghw0126 Apr 13, 2026
a1dd7a2
feat: 무한 스크롤 로딩 시 스켈레톤 UI 적용
janghw0126 Apr 13, 2026
e569dcc
fix: 모달 이미지 경로를 import로 변경하여 GitHub Pages 404 오류 수정
janghw0126 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
File renamed without changes.
53 changes: 53 additions & 0 deletions README-step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# javascript-movie-review

FE 레벨1 영화 리뷰 미션

## 공통 요구사항

- [x] 영화 포스터나 제목을 클릭하면 자세한 예고편이나 줄거리 등의 정보를 보여준다.
- [x] 별점을 남길 수 있는 기능을 만든다.
- [x] 반응형 웹을 구상하여 디바이스의 너비에 따라 유동적으로 레이아웃이 조절되는 멋진 UI를 구현한다.

## 기능 목록

### 1. 영화 상세정보 조회 모달

- [x] 영화 제목이나 포스터 클릭 시 모달을 표시한다.
- [x] 모달에서 보여줄 상세 정보
- 영화 포스터
- 영화 제목
- 영화 장르
- 평균 별점
- 줄거리
- [x] 모달 닫기
- ESC 키를 누르는 경우
- 모달 외부 배경을 클릭하는 경우
- 닫기 버튼을 클릭하는 경우

### 2. 별점 매기기

- [x] 2점 단위로 구성 (2 / 4 / 6 / 8 / 10점)
- 2점 : 최악이예요
- 4점 : 별로예요
- 6점 : 보통이예요
- 8점 : 재미있어요
- 10점 : 명작이예요
- [x] 별점 선택 시 localStorage에 저장한다.
- [x] 새로고침 후에도 사용자가 남긴 별점을 유지한다.

### 3. 영화 목록 무한 스크롤

- [x] IntersectionObserver를 사용하여 무한 스크롤을 구현한다.
- [x] 브라우저 화면 끝에 도달하면 다음 20개 영화를 요청한다.

### 4. 반응형 웹

- [x] 웹 / 모바일 환경에 따른 반응형 레이아웃을 구성한다.

### 5. E2E 테스트 작성

- [x] 영화 포스터 또는 제목 클릭 시 모달 창 표시
- [x] 닫기 버튼 클릭 시 모달 닫기
- [x] ESC 키로 모달 닫기
- [x] 별점 부여 후 새로고침 시 별점 유지
- [x] 무한 스크롤 동작
63 changes: 58 additions & 5 deletions cypress/e2e/spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,75 @@ describe("영화 리뷰 웹 E2E 테스트", () => {
beforeEach(() => {
cy.visit("localhost:5173");
cy.get(".thumbnail-list li").should("have.length.greaterThan", 0);
cy.get(".search-input").should("not.be.disabled");
});

it("초기 진입 시 인기영화 목록이 표시된다", () => {
cy.get(".main-title").should("have.text", "지금 인기 있는 영화");
cy.get(".btn-more").should("be.visible");
// 1. 모달 표시
it("영화 포스터 클릭 시 모달 창이 표시된다", () => {
cy.get(".thumbnail-list li").first().find(".thumbnail").click();
cy.get(".modal-background").should("have.class", "active");
cy.get(".modal-title").should("be.visible");
});

it("영화 제목 클릭 시 모달 창이 표시된다", () => {
cy.get(".thumbnail-list li").first().find("strong").click();
cy.get(".modal-background").should("have.class", "active");
cy.get(".modal-title").should("be.visible");
});

// 2. 닫기 버튼으로 모달 닫기
it("닫기 버튼 클릭 시 모달이 닫힌다", () => {
cy.get(".thumbnail-list li").first().find(".thumbnail").click();
cy.get(".modal-background").should("have.class", "active");
cy.get(".close-modal").click();
cy.get(".modal-background").should("not.have.class", "active");
});

// 3. ESC 키로 모달 닫기
it("ESC 키 입력 시 모달이 닫힌다", () => {
cy.get(".thumbnail-list li").first().find(".thumbnail").click();
cy.get(".modal-background").should("have.class", "active");
cy.get("body").type("{esc}");
cy.get(".modal-background").should("not.have.class", "active");
});

// 별점 테스트
it("별점 부여 후 새로고침 시 별점이 유지된다", () => {
cy.get(".thumbnail-list li").first().find(".thumbnail").click();
cy.get(".modal-background").should("have.class", "active");

cy.get(".modal-star[data-index='4']").click();
cy.get(".rating-label").should("have.text", "재미있어요");
cy.get(".rating-score").should("have.text", "(8/10)");

cy.get(".close-modal").click();
cy.reload();
cy.get(".thumbnail-list li").should("have.length.greaterThan", 0);

cy.get(".thumbnail-list li").first().find(".thumbnail").click();
cy.get(".modal-background").should("have.class", "active");
cy.get(".rating-label").should("have.text", "재미있어요");
cy.get(".rating-score").should("have.text", "(8/10)");
Comment on lines +38 to +53
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 | 🟠 Major

새로고침 뒤에 같은 영화를 다시 열었다는 보장이 없습니다.

평점은 movieId로 저장되는데, 검증은 다시 “첫 번째 카드”를 여는 방식이라 인기순 응답 순서가 바뀌면 기능이 정상이어도 테스트가 깨집니다. 처음 선택한 카드의 data-id를 저장해서 같은 영화를 다시 열도록 맞춰 두면 훨씬 안정적입니다.

🤖 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 38 - 53, The test opens the first movie
card then reloads but re-opens whatever is first after reload, which can be a
different movie; capture the initially opened movie's identifier (e.g., read the
first `.thumbnail-list li`'s `data-id` or the clicked `.thumbnail`'s `data-id`
into a variable/alias) before clicking or reloading, then after cy.reload()
locate and click the same movie by selecting the element with that `data-id`
(e.g., `[data-id="${savedId}"]`) instead of blindly clicking the first
`.thumbnail-list li`, and then assert `.rating-label` and `.rating-score` as
before to ensure you reopened the same movie.

});

it("더보기 클릭 시 영화 카드가 추가된다", () => {
// 무한 스크롤 테스트
it("화면 끝에 도달하면 추가 영화가 로드된다", () => {
cy.intercept("GET", /movie\/popular/).as("loadMore");

cy.get(".thumbnail-list li")
.its("length")
.then((before) => {
cy.get(".btn-more").click();
cy.get(".scroll-sentinel").scrollIntoView();
cy.wait("@loadMore", { timeout: 8000 });
cy.get(".thumbnail-list li").should("have.length.greaterThan", before);
});
});

it("초기 진입 시 인기영화 목록이 표시된다", () => {
cy.get(".main-title").should("have.text", "지금 인기 있는 영화");
cy.get(".thumbnail-list li").should("have.length.greaterThan", 0);
});

it("검색어 입력 시 검색 결과가 표시된다", () => {
cy.get(".search-input").type("아이언맨");
cy.get(".search-form").submit();
Expand Down
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="stylesheet" href="./src/styles/main.css" />
<link rel="stylesheet" href="./src/styles/tab.css" />
<link rel="stylesheet" href="./src/styles/thumbnail.css" />
<link rel="stylesheet" href="./src/styles/modal.css" />
<title>영화 리뷰</title>
</head>
<body>
Expand All @@ -20,7 +21,7 @@
<h2 class="main-title">지금 인기 있는 영화</h2>
<div class="main-result"></div>
<ul class="thumbnail-list"></ul>
<button class="btn-more" style="display: none">더 보기</button>
<div class="scroll-sentinel"></div>
</section>
</main>
</div>
Expand Down
77 changes: 0 additions & 77 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/features/View/Header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const Header = {
<div class="top-rated-movie">
<div class="rate">
<img src="${starImg}" class="star" />
<span class="rate-value">${movie.vote_average.toFixed(1)}</span>
<span class="rate-value">${(movie.vote_average ?? 0).toFixed(1)}</span>
</div>
<div class="title">${movie.title}</div>
<button class="primary detail">자세히 보기</button>
Expand Down Expand Up @@ -82,4 +82,4 @@ export const Header = {
</div>
</div>`;
},
};
};
4 changes: 3 additions & 1 deletion src/features/View/MovieCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export default class MovieCard {

render(): HTMLLIElement {
const li = document.createElement("li");
li.className = "movie-card";
li.dataset.id = this.movie.id.toString();
li.innerHTML = `<div class="item">
<img
class="thumbnail"
Expand All @@ -20,7 +22,7 @@ export default class MovieCard {
<div class="item-desc">
<p class="rate">
<img src="${starImg}" class="star" />
<span>${this.movie.vote_average.toFixed(1)}</span>
<span>${(this.movie.vote_average ?? 0).toFixed(1)}</span>
</p>
<strong>${this.movie.title}</strong>
</div>
Expand Down
Loading