Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
af2725f
docs: 2단계 진행을 위한 README 작성
binggwa Apr 9, 2026
25cc0ca
feat: 무한 스크롤 기능 추가
binggwa Apr 9, 2026
02111d2
feat: 영화 상세보기용 타입 추가
binggwa Apr 12, 2026
cb848ff
chore: 로컬작업
binggwa Apr 12, 2026
44799f5
style: html 파일들에 상세보기용 modal 컨테이너 추가
binggwa Apr 12, 2026
3ef9487
feat: 모달 창 여닫기 기능 추가
binggwa Apr 12, 2026
829c790
feat: 모달 창 닫기 기능 3가지 구현 - 닫기버튼, 모달창바깥, ESC키
binggwa Apr 12, 2026
474080c
feat: 모달 이벤트 추가
binggwa Apr 12, 2026
df1b6f7
feat: 의존성 주입을 통한 리뷰스토리지 인터페이스 작성
binggwa Apr 12, 2026
ac03cfd
feat: 별점 매기기 기능 추가
binggwa Apr 12, 2026
386fd4c
style: 모달창 별점 매기기 스타일 추가
binggwa Apr 12, 2026
fb503d0
feat: 화면 크기에 따라 한줄에 출력되는 포스터 개수 조절
binggwa Apr 12, 2026
b887498
style: 반응형 적용시작
binggwa Apr 12, 2026
938854b
style: 헤더 스타일 2차미션에 맞게 변경
binggwa Apr 12, 2026
f29ae3d
style: 반응형에 맞게 herobanner, 모달 수정
binggwa Apr 12, 2026
ee56002
feat: herobanner 자세히 보기 버튼 이벤트 추가
binggwa Apr 12, 2026
df39bc0
style: 요구사항에 맞게 스타일 수정
binggwa Apr 12, 2026
b8fefd5
docs: README2 업데이트
binggwa Apr 12, 2026
dfccc32
style: 모바일 환경 여백 수정
binggwa Apr 12, 2026
ace5826
test: step2 e2e 테스트 작성
binggwa Apr 12, 2026
e38ef79
style: resultData 파스칼케이스로 변경
binggwa Apr 13, 2026
2dbaad0
refactor: render, view 통합 및 중복 코드 제거, fetch흐름 controller로 이관
binggwa Apr 13, 2026
2c21d57
refactor: modal에서 render관련 view요소 분리
binggwa Apr 13, 2026
5e2e7c4
feat: isError추가로 무한스크롤중 오류 발생시 문제 해결
binggwa Apr 13, 2026
b959778
chore: 로고클릭 경로 상대경로로 변경
binggwa Apr 13, 2026
e19f9ec
test: cypress 테스트 api > mock데이터로 변경
binggwa Apr 13, 2026
12e605d
rename: movieController > movieListApp으로 변경
binggwa Apr 13, 2026
9468daa
refactor: renderModalContent 추상화레벨 증가
binggwa Apr 13, 2026
05cd3e7
refactor: updateStarsUI 추상화레벨 증가
binggwa Apr 13, 2026
8dcab73
refactor: initModalEvents로 모달 이벤트 집약 및 각 이벤트 분리
binggwa Apr 13, 2026
3266676
refactor: 타입단언 추가
binggwa Apr 13, 2026
c3cdab8
refactor: 히어로배너 및 영화상세보기 클릭이벤트 리팩토링
binggwa 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
33 changes: 33 additions & 0 deletions .github/pull_request_template2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## 🎯 미션 소개

- API 연동을 통한 비동기 통신 학습
- 사용자 시나리오 기반 E2E 테스트

---

## 🕵️ 셀프 리뷰(Self-Review)

### 제출 전 체크 리스트

- [x] 기능 요구 사항을 모두 구현했고, 정상적으로 동작하는지 확인했나요?
- [x] 기본적인 프로그래밍 요구 사항(코드 컨벤션, 에러 핸들링 등)을 준수했나요?
- [ ] 테스트 코드는 모두 정상적으로 실행되나요? (필요 시, API Mocking 등)
- [ ] (해당하는 경우) 배포한 데모 페이지에 정상적으로 접근할 수 있나요?
- (해당하는 경우) 배포 링크 기입: \***\*\_\_\*\***

### 리뷰 요청 & 논의하고 싶은 내용

#### 1) 이번 단계에서 가장 많이 고민했던 문제와 해결 과정에서 배운 점

#### 2) 이번 리뷰를 통해 논의하고 싶은 부분

---

## ✅ 리뷰어 체크 포인트

<!-- 리뷰어가 이 PR을 검토할 때 중점적으로 확인할 사항입니다.
코드의 완성도만이 아니라, 리뷰이가 구현 과정에서 어떤 고민과 결정을 하며 학습했는지도 함께 고려해 주세요. -->

- [ ] 비동기 통신 과정에서 발생할 수 있는 예외(네트워크, 서버 오류 등)를 고려했는가?
- [ ] 비동기 로직에서 콜백 지옥 없이, 적절히 async/await 또는 Promise를 활용했는가?
- [ ] 역할과 책임에 따라 파일/모듈을 분리했는가? (UI, 비즈니스 로직, API 호출 등)
43 changes: 43 additions & 0 deletions README2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# javascript-movie-review

FE 레벨1 영화 리뷰 미션 2단계

## 기능 요구 사항
### 더보기 버튼 무한스크롤로 변경
- [x] 기존 더보기 버튼 클릭 시 영화목록을 호출하던 조건을 화면 맨 아래로 내려왔을 때로 변경

### 영화 상세정보 조회
- [x] 자세히 보기 클릭 시 출력
- [x] 영화 섬네일 클릭 시 출력
- [x] 상세 정보 로딩 중 스켈레톤 처리
- [x] 모달 창 닫기 기능
- [x] X 키
- [x] esc 키
- [x] 모달 창 바깥 클릭
- 출력할 내용
- [x] 영화 포스터
- [x] 영화 제목
- [x] 연도
- [x] 장르
- [x] 평균 별점
- [x] 내 별점 (별점 그림 + 별점에 따른 설명 + (8/10)과 같은 별점 수치화)
- [x] 줄거리
- [x] API 에러 처리

### UI/UX 개선
- [x] 반응형 웹을 구상하여 디바이스의 너비에 따라 레이아웃이 조절되도록 변경
- [x] 너비에 따라 영화 출력 갯수 반응형으로 변경
- [x] 모달 창 데스크톰은 중앙 출력, 태블릿이나 모바일은 하단 출력으로 제작
- [x] 자세히 보기 창에서 화면이 너무 작으면 포스터 출력 x

### 별점 매기기 기능
- [x] 2점 단위의 별점 매기기 기능
- [x] 새로고침해도 별점 유지
- [x] 별점 UI 호버, 클릭 이벤트 처리
- [x] 새로 입력받은 별점 데이터를 상세화면에 반영
- [x] 로컬 스토리지 및 서버API로 쉽고 안전하게 갈아끼울 수 있는 구조로 제작

### E2E 테스트
- [x] 무한 스크롤 동작 테스트
- [x] 상세 정보 모달 여닫기 테스트
- [x] 별점 평가, 반영, 새로고침 시 데이터 유지 테스트
117 changes: 117 additions & 0 deletions cypress/e2e/movie.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
describe('영화 리뷰 step2 테스트', () => {
beforeEach(() => {
// 인기 영화 목록 API 가로채기
cy.intercept('GET', '**/movie/popular*', {
fixture: 'popularMovies.json',
}).as('getPopularMovies');

// 영화 상세 정보 API를 가로채기
cy.intercept('GET', /\/movie\/\d+/, {
fixture: 'movieDetail.json',
}).as('getMovieDetail');

cy.visit('/');

cy.wait('@getPopularMovies');
});

describe('무한 스크롤 동작 테스트', () => {
it('화면을 끝까지 스크롤하면 새로운 영화 목록이 추가로 로드된다.', () => {
// 렌더링된 영화 아이템 개수 저장
cy.get('.thumbnail-list .movie-item').then(($items) => {
const initialCount = $items.length;

// 바닥으로 스크롤하여 더 보기 트리거
cy.scrollTo('bottom');

// 렌더링 대기
cy.wait('@getPopularMovies');

// 영화 아이템 개수가 늘어났는지 검증
cy.get('.thumbnail-list .movie-item')
.should('have.length.greaterThan', initialCount);
});
});
});

describe('상세 정보 모달 여닫기 테스트', () => {
// 클릭 확인
it('영화 클릭 시 모달이 열리고, 닫기 버튼을 누르면 모달이 닫힌다.', () => {
// 첫 번째 영화 아이템 클릭
cy.get('.thumbnail-list .movie-item').first().click();

// 모달 데이터 응답 대기
cy.wait('@getMovieDetail');

// 모달이 활성화되었는지 확인 (active 클래스 및 화면노출)
cy.get('#modalBackground').should('have.class', 'active');
cy.get('#modalContainer').should('be.visible');

// 닫기 버튼 클릭
cy.get('#closeModal').click();

// 모달이 정상적으로 닫혔는지 확인
cy.get('#modalBackground').should('not.have.class', 'active');
});

// 배경클릭 확인
it('모달 배경을 클릭해도 모달이 닫힌다.', () => {
cy.get('.thumbnail-list .movie-item').first().click();

// 배경 영역 클릭
cy.get('#modalBackground').click('topLeft', { force: true });

cy.get('#modalBackground').should('not.have.class', 'active');
});

// ESC키 확인
it('ESC 키를 누르면 모달이 닫힌다.', () => {
cy.get('.thumbnail-list .movie-item').first().click();
cy.get('#modalBackground').should('have.class', 'active');

// body 태그에 대고 ESC 키 입력 이벤트 발생
cy.get('body').type('{esc}');

// 모달이 정상적으로 닫혔는지 확인
cy.get('#modalBackground').should('not.have.class', 'active');
});
});

describe('별점 평가 및 데이터 유지 테스트', () => {
it('별점을 선택하면 반영되고, 새로고침 후에도 해당 점수가 유지된다.', () => {
const TARGET_SCORE = 8;
const RATING_TEXT = '재미있어요';

// 모달 열기
cy.get('.thumbnail-list .movie-item').first().click();

// 8점 클릭
cy.get(`.rate-star-img[data-score="${TARGET_SCORE}"]`).click();

// UI에 평가 문구와 점수가 반영되었는지 확인
cy.get('#ratingDescription')
.should('contain.text', RATING_TEXT)
.and('contain.text', `(${TARGET_SCORE}/10)`);

// 모달 닫기
cy.get('#closeModal').click();

// 페이지 새로고침
cy.reload();
cy.wait('@getPopularMovies');

// 동일한 영화의 모달을 다시 열기
cy.get('.thumbnail-list .movie-item').first().click();

// LocalStorage에서 데이터를 잘 가져와서 이전 점수가 그대로 표시되는지 확인
cy.get('#ratingDescription')
.should('contain.text', RATING_TEXT)
.and('contain.text', `(${TARGET_SCORE}/10)`);

// 별 이미지 소스도 채워진 별(star_filled)인지 확인 (첫 번째 별점 예시)
cy.get(`.rate-star-img[data-score="2"]`)
.should('have.attr', 'src')
.and('include', 'star_filled');
});
});
});
16 changes: 8 additions & 8 deletions cypress/e2e/spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ describe('영화 리뷰 앱 E2E 테스트', () => {
cy.get('.thumbnail-list > li').should('have.length', 20);
});

it('더보기 버튼을 누르면 다음 페이지의 영화 20개를 추가로 렌더링한다.', () => {
cy.visit('/');
cy.wait('@getPopularMovies');
// it('더보기 버튼을 누르면 다음 페이지의 영화 20개를 추가로 렌더링한다.', () => {
// cy.visit('/');
// cy.wait('@getPopularMovies');

cy.get('#more-page-button').click();
cy.wait('@getNextPopularMovies');
// cy.get('#more-page-button').click();
// cy.wait('@getNextPopularMovies');

// 기존 20개 + 추가 20개 = 40개
cy.get('.thumbnail-list > li').should('have.length', 40);
});
// // 기존 20개 + 추가 20개 = 40개
// cy.get('.thumbnail-list > li').should('have.length', 40);
// });
});

context('2. 검색 기능', () => {
Expand Down
26 changes: 26 additions & 0 deletions cypress/fixtures/movieDetail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"id": 1022789,
"title": "인사이드 아웃 2",
"poster_path": "/vpnVM9B6NMmQpWeZvzRxHXlNpzT.jpg",
"release_date": "2024-06-12",
"genres": [
{
"id": 16,
"name": "애니메이션"
},
{
"id": 10751,
"name": "가족"
},
{
"id": 12,
"name": "모험"
},
{
"id": 35,
"name": "코미디"
}
],
"vote_average": 8.5,
"overview": "13살이 된 라일리의 머릿속 감정 컨트롤 본부에 불안, 당황, 따분, 부럽의 낯선 감정들이 새롭게 등장하면서 평화롭던 일상이 깨지고 다시 시작된 위기와 모험을 다룬 애니메이션 영화"
}
41 changes: 41 additions & 0 deletions cypress/fixtures/popularMovies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"page": 1,
"results": [
{
"id": 1022789,
"title": "인사이드 아웃 2",
"poster_path": "/vpnVM9B6NMmQpWeZvzRxHXlNpzT.jpg",
"backdrop_path": "/stKGOm8UyhuLPR9sLsGAXo0I94w.jpg",
"vote_average": 8.5
},
{
"id": 533535,
"title": "데드풀과 울버린",
"poster_path": "/8cdWjvZQUExUUTzyp4t6EDMubfO.jpg",
"backdrop_path": "/yDHYTfA3R0jFYba16ZBRWUP1lNl.jpg",
"vote_average": 7.9
},
{
"id": 1029528,
"title": "슈퍼배드 4",
"poster_path": "/3w84hCFJATpiCO5g8hpdWVPBcbF.jpg",
"backdrop_path": "/lgkPzcOSnTvjeMnuFzozRO5HHw1.jpg",
"vote_average": 7.4
},
{
"id": 653346,
"title": "혹성탈출: 새로운 시대",
"poster_path": "/fQZ5o2eY9KusVq6qO2o1EudV9Iq.jpg",
"backdrop_path": "/fqv8v6AycXKsivp1T5yKtLbGXce.jpg",
"vote_average": 6.9
},
{
"id": 748783,
"title": "가필드 더 무비",
"poster_path": "/a0A9YlRhaV7Z1rA0N6P4qF8w3rO.jpg",
"backdrop_path": "/xg27NrZaAKk4pM1r7K531Qk7u0D.jpg",
"vote_average": 7.1
}
],
"total_pages": 100
}
Binary file added images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/modal_button_close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/star_empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/star_filled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/woowacourse_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 28 additions & 13 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,41 @@
<link rel="stylesheet" href="./styles/main.css" />
<link rel="stylesheet" href="./styles/tab.css" />
<link rel="stylesheet" href="./styles/thumbnail.css" />
<link rel="stylesheet" href="./styles/modal.css" />
</head>

<body>
<div id="app"></div>
<header style="position: relative; width: 100%;">
<div class="background-container">
<div class="overlay" aria-hidden="true"></div>
<div class="top-rated-container">

<div class="header-top-wrapper">
<h1 class="logo">
<a href="/"><img src="./images/logo.png" alt="MovieList" /></a>
<a href="./"><img src="./images/logo.png" alt="MovieList" /></a>
</h1>
<form class="pretty-search-bar" action="./search.html" method="GET">
<input type="text" class="search-input" name="q" placeholder="검색어를 입력하세요" required/>
<button type="submit" class="search-icon-btn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</button>
</form>
</div>

<div class="top-rated-container">
<div class="top-rated-movie">
<div class="rate">
<img src="./images/star_empty.png" class="star" alt="별점" />
<img src="./images/star_filled.png" class="star" alt="별점" />
<span class="rate-value">9.5</span>
</div>
<div class="title">인사이드 아웃2</div>
<button class="primary detail">자세히 보기</button>
</div>
</div>
</div>

<form class="pretty-search-bar" action="./search.html" method="GET" style="position: absolute; top: 48px; left: 50%; transform: translateX(-50%); width: 480px; z-index: 10;">
<input type="text" class="search-input" name="q" placeholder="검색어를 입력하세요" required/>
<button type="submit" class="search-icon-btn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</button>
</form>
</header>

<main>
Expand All @@ -51,6 +55,17 @@ <h2>지금 인기 있는 영화</h2>
</section>
</main>

<div class="modal-background" id="modalBackground">
<div class="modal">
<button class="close-modal" id="closeModal">
<img src="./images/modal_button_close.png" alt="닫기" />
</button>
<div class="modal-container" id="modalContainer">

</div>
</div>
</div>

<footer class="footer">
<p>&copy; 우아한테크코스 All Rights Reserved.</p>
<p><img src="./images/woowacourse_logo.png" width="180" /></p>
Expand Down
Loading