Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Re-op]KDT5_윤금엽_YoonGeumYeop_영화 검색 사이트 #70

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"plugins": ["prettier"]
}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.parcel-cache
dist
node_modules
.vercel
.env
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": false,
"singleQuote": true,
"endOfLine": "lf",
"singleAttributePerLine": true,
"bracketSameLine": true,
"trailingComma": "none",
"arrowParens": "avoid"
}
221 changes: 27 additions & 194 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,217 +3,50 @@
주어진 API를 활용해 '[완성 예시](https://stupefied-hodgkin-d9d350.netlify.app/)' 처럼 자유롭게 영화 검색 기능을 구현해보세요!
과제 수행 및 리뷰 기간은 별도 공지를 참고하세요!

## 과제 수행 및 제출 방법
## 배포페이지

```
KDT기수번호_이름 | E.g, KDT0_ParkYoungWoong
```

1. 현재 저장소를 로컬에 클론(Clone)합니다.
1. 자신의 본명으로 브랜치를 생성합니다.(구분 가능하도록 본명을 꼭 파스칼케이스로 표시하세요, `git branch KDT0_ParkYoungWoong`)
1. 자신의 본명 브랜치에서 과제를 수행합니다.
1. 과제 수행이 완료되면, 자신의 본명 브랜치를 원격 저장소에 푸시(Push)합니다.(`main` 브랜치에 푸시하지 않도록 꼭 주의하세요, `git push origin KDT0_ParkYoungWoong`)
1. 저장소에서 `main` 브랜치를 대상으로 Pull Request 생성하면, 과제 제출이 완료됩니다!(E.g, `main` <== `KDT0_ParkYoungWoong`)

- `main` 혹은 다른 사람의 브랜치로 절대 병합하지 않도록 주의하세요!
- Pull Request에서 보이는 설명을 다른 사람들이 이해하기 쉽도록 꼼꼼하게 작성하세요!
- Pull Request에서 과제 제출 후 절대 병합(Merge)하지 않도록 주의하세요!
- 과제 수행 및 제출 과정에서 문제가 발생한 경우, 바로 담당 멘토나 강사에서 얘기하세요!
**[영화 검색 사이트](https://omdb-api-filmise.vercel.app)**

## 요구사항

필수 요구사항은 꼭 달성해야 하는 목표로, 수정/삭제는 불가하고 추가는 가능합니다.
필수 요구사항은 꼭 달성해야 하는 목표로, 수정/삭제는 불가하고 추가는 가능합니다.
선택 요구사항은 단순 예시로, 자유롭게 추가/수정/삭제해서 구현해보세요.
각 요구사항은 달성 후 마크다운에서 `- [x]`로 표시하세요.
각 요구사항은 달성 후 마크다운에서 `- [x]`로 표시하세요.

### ❗ 필수

- [ ] 영화 제목으로 검색이 가능해야 합니다!
- [ ] 검색된 결과의 영화 목록이 출력돼야 합니다!
- [ ] 단일 영화의 상세정보(제목, 개봉연도, 평점, 장르, 감독, 배우, 줄거리, 포스터 등)를 볼 수 있어야 합니다!
- [ ] 실제 서비스로 배포하고 접근 가능한 링크를 추가해야 합니다.
- [x] 영화 제목으로 검색이 가능해야 합니다!
- [x] 검색된 결과의 영화 목록이 출력돼야 합니다!
- [x] 단일 영화의 상세정보(제목, 개봉연도, 평점, 장르, 감독, 배우, 줄거리, 포스터 등)를 볼 수 있어야 합니다!
- [x] 실제 서비스로 배포하고 접근 가능한 링크를 추가해야 합니다.

### ❔ 선택

- [ ] 한 번의 검색으로 영화 목록이 20개 이상 검색되도록 만들어보세요.
- [ ] 영화 개봉연도로 검색할 수 있도록 만들어보세요.
- [ ] 영화 목록을 검색하는 동안 로딩 애니메이션이 보이도록 만들어보세요.
- [x] 영화 목록을 검색하는 동안 로딩 애니메이션이 보이도록 만들어보세요.
- [ ] 무한 스크롤 기능을 추가해서 추가 영화 목록을 볼 수 있도록 만들어보세요.
- [ ] 영화 포스터가 없을 경우 대체 이미지를 출력하도록 만들어보세요.
- [ ] 영화 상세정보가 출력되기 전에 로딩 애니메이션이 보이도록 만들어보세요.
- [ ] 영화 상세정보 포스터를 고해상도로 출력해보세요. (실시간 이미지 리사이징)
- [ ] 차별화가 가능하도록 프로젝트를 최대한 예쁘게 만들어보세요.
- [x] 영화 상세정보가 출력되기 전에 로딩 애니메이션이 보이도록 만들어보세요.
- [x] 영화 상세정보 포스터를 고해상도로 출력해보세요. (실시간 이미지 리사이징)
- [] 차별화가 가능하도록 프로젝트를 최대한 예쁘게 만들어보세요.
- [ ] 영화와 관련된 기타 기능도 고려해보세요.

## API 기본 사용법

```curl
curl https://omdbapi.com/?apikey=7035c60c
\ -X 'GET'
```

## 영화 목록 검색

영화 목록은 한 번에 최대 10개까지 검색할 수 있습니다.

파라미터 | 설명 | 기본값
---|----------------------|---
`s` | 검색할 영화 제목(필수!) | -
`y` | 검색할 개봉연도, 빈 값은 전체 검색 | -
`page` | 검색할 페이지 번호 | `1`

요청 코드 예시:

```js
async function getMovies(title, year = '', page = 1) {
const s = `&s=${title}`
const y = `&y=${year}`
const p = `&page=${page}`
try {
const res = await fetch(`https://omdbapi.com/?apikey=7035c60c${s}${y}${p}`)
const json = await res.json()
if (json.Response === 'True') {
const { Search: movies, totalResults } = json
return {
movies,
totalResults
}
}
return json.Error
} catch (error) {
console.log(error)
}
}
```

응답 데이터 타입 및 예시:

```ts
interface ResponseValue {
Search: Movie[] // 검색된 영화 목록, 최대 10개
totalResults: string // 검색된 영화 개수
Response: 'True' | 'False' // 요청 성공 여부
}
interface Movie {
Title: string // 영화 제목
Year: string // 영화 개봉연도
imdbID: string // 영화 고유 ID
Type: string // 영화 타입
Poster: string // 영화 포스터 이미지 URL
}
```

```json
{
"Search": [
{
"Title": "Frozen",
"Year": "2013",
"imdbID": "tt2294629",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BMTQ1MjQwMTE5OF5BMl5BanBnXkFtZTgwNjk3MTcyMDE@._V1_SX300.jpg"
},
{
"Title": "Frozen II",
"Year": "2019",
"imdbID": "tt4520988",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BMjA0YjYyZGMtN2U0Ni00YmY4LWJkZTItYTMyMjY3NGYyMTJkXkEyXkFqcGdeQXVyNDg4NjY5OTQ@._V1_SX300.jpg"
}
],
"totalResults": "338",
"Response": "True"
}
```

## 영화 상제정보 검색

단일 영화의 상제정보를 검색합니다.

파라미터 | 설명 | 기본값
---|---|---
`i` | 검색할 영화 ID(필수!) |
`plot` | 줄거리 길이 | `short`

요청 코드 예시:

```js
async function getMovie(id) {
const res = await fetch(`https://omdbapi.com/?apikey=7035c60c&i=${id}&plot=full`)
const json = await res.json()
if (json.Response === 'True') {
return json
}
return json.Error
}
```
## 고찰

응답 데이터 타입 및 예시:
- 2-3주간 개인 건강문제로 JavaScript 공부가 많이 부족한 상태
- 아직 지식이 부족하여 스스로 개발하기는 어려움
- 제공된 강의를 보며 최대한 이해하면서 작업해내는 것이 목표였음
- 온전히 다 이해하지 못했기 때문에 아쉬움이 많이 남음
- 강의를 들으며 어려웠던 내용, 시도했지만 실패한 선택사항들을 리스트업 => 추가 학습+적용
- 무한 스크롤 기능을 추가하고 싶었지만 실패
=> Scroll event와 IntersectionObserver API의 차이점 정리 + 다음에 사용해보기
- SCSS에서 변수 적용, 메인·변수 파일 분리 X => 다음 프로젝트에서 사용해보기
- Component, Router, Store 및 디렉토리 구조에 대해 정리하고 조원들과 나눠보기

```ts
interface ResponseValue {
Title: string // 영화 제목
Year: string // 영화 개봉연도
Rated: string // 영화 등급
Released: string // 영화 개봉일
Runtime: string // 영화 상영시간
Genre: string // 영화 장르
Director: string // 영화 감독
Writer: string // 영화 작가
Actors: string // 영화 출연진
Plot: string // 영화 줄거리
Language: string // 영화 언어
Country: string // 영화 제작 국가
Awards: string // 영화 수상 내역
Poster: string // 영화 포스터 이미지 URL
Ratings: Rating[] // 영화 평점 정보
Metascore: string // 영화 메타스코어
imdbRating: string // 영화 IMDB 평점
imdbVotes: string // 영화 IMDB 투표 수
imdbID: string // 영화 고유 ID
Type: string // 영화 타입
DVD: string // 영화 DVD 출시일
BoxOffice: string // 영화 박스오피스
Production: string // 영화 제작사
Website: string // 영화 공식 웹사이트
Response: string // 요청 성공 여부
}
interface Rating { // 영화 평점 정보
Source: string // 평점 제공 사이트
Value: string // 평점
}
```
# 1차 리팩토링(23.05.10)

```json
{
"Title": "Frozen",
"Year": "2013",
"Rated": "PG",
"Released": "27 Nov 2013",
"Runtime": "102 min",
"Genre": "Animation, Adventure, Comedy",
"Director": "Chris Buck, Jennifer Lee",
"Writer": "Jennifer Lee, Hans Christian Andersen, Chris Buck",
"Actors": "Kristen Bell, Idina Menzel, Jonathan Groff",
"Plot": "When the newly crowned Queen Elsa accidentally uses her power to turn things into ice to curse her home in infinite winter, her sister Anna teams up with a mountain man, his playful reindeer, and a snowman to change the weather co...",
"Language": "English, Norwegian",
"Country": "United States",
"Awards": "Won 2 Oscars. 82 wins & 60 nominations total",
"Poster": "https://m.media-amazon.com/images/M/MV5BMTQ1MjQwMTE5OF5BMl5BanBnXkFtZTgwNjk3MTcyMDE@._V1_SX300.jpg",
"Ratings": [
{ "Source": "Internet Movie Database", "Value": "7.4/10" },
{ "Source": "Rotten Tomatoes", "Value": "90%" },
{ "Source": "Metacritic", "Value": "75/100" }
],
"Metascore": "75",
"imdbRating": "7.4",
"imdbVotes": "620,489",
"imdbID": "tt2294629",
"Type": "movie",
"DVD": "18 Mar 2014",
"BoxOffice": "$400,953,009",
"Production": "N/A",
"Website": "N/A",
"Response": "True"
}
```
- 영화 검색 시 개봉년도, 타이틀을 출력하는 container 높이
- container의 height를 설정, 타이틀이 두 줄을 넘어갈 경우 한 줄로 출력
- MovieItem, TheFooter, About, NotFound 등 전체적인 html 태그 수정
- TheHeader에 있는 메뉴 중 Recommended가 영화 상세정보 출력 시 활성화되지 않도록 수정
13 changes: 13 additions & 0 deletions api/movie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fetch from 'node-fetch';

const { APIKEY } = process.env;

Choose a reason for hiding this comment

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

process.env의 경우, 빌드 파라미터로 들어가는 것으로 알고 있는데 어떠한 방식으로 해당값을 넣는지 궁금합니다.


export default async function handler(request, response) {
const { title, page, id } = JSON.parse(request.body);

Choose a reason for hiding this comment

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

handler를 통해서 요청 - 응답 객체를 변경하는 작업을 진행하신 것은 매우 좋은 시도라고 생각합니다. 주로 handler를 통한 APP의 요청 - 응답 객체에 대한 변경은 로그인 체크나, 쿠키를 심는 것과 같은 모든 요청(APP 전역)에 대해서 필요한 작업을 위해 사용합니다. 아마 APIKEY를 심는 공통의 작업을 위해서 handler를 사용하신 것으로 보입니다. 하지만 여기서 조금 고려해볼 만한 것이 있습니다.

  1. 현재 요청을 진행하는 URL이 두개이기에 분기처리를 통해서 가능하지만, URL이 20-30개의 프로젝트일 경우에는 분기처리로는 구분이 어려울 것 같습니다. 이를 위해서는 APIKEY를 심는 작업만 handler 내에 구현하고, URL에 따라서 메서드를 각각 구현해야할 것으로 보입니다.

  2. 모든 요청의 상태코드가 200(response.status(200))으로 고정되어 있습니다. 이는 모든 요청에 대한 응답이 심지어 서버에서 전달하는 값이 오류가 발생하였다고 하더라도 API의 응답은 200, 정상이라는 의미를 전달하고 있습니다. HTTP에 따르면 응답의 상태에 따라서 200이 아닌, 400- 500번의 상태값을 반환하고 있는데요. API 서버에서 전달하는 상태값에 맞게 대응할 수 있도록 구분하여 구현해야합니다. 이를 위해서 HTTP 상태코드에 대해서 한 번 알아보도록 합시다!
    HTTP response status codes

const url = id
? `https://omdbapi.com?apikey=${APIKEY}&i=${id}&plot=full`
: `https://omdbapi.com?apikey=${APIKEY}&s=${title}&page=${page}`;
const res = await fetch(url);
const json = await res.json();
response.status(200).json(json);
}
42 changes: 42 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>FILMISE</title>

<!-- cdn reset.css -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/reset.min.css" />

<!-- Google fonts - Oswald, Roboto -->
<link
rel="preconnect"

Choose a reason for hiding this comment

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

preconnect를 사용하여 font 파일을 들고오고 있네요:) 해당 속성이 어떠한 의미로 사용되고 있는지 또 어떤 효과가 있어서 추가한 것인지 설명해주실 수 있을까요?

href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Oswald:wght@500&family=Roboto:wght@400;700&display=swap"
rel="stylesheet" />

<link
rel="stylesheet"
href="./src/main.scss" />
<script
type="module"
defer
src="./src/main.js"></script>
</head>

<body>
<div id="root"></div>
</body>
</html>
Loading