결과물
어디 뭘 설명해야할지.... 우선 완성작부터 보자.
그냥 보기엔 소소해보이겠지만, 난 이걸 하루하고 반나절동안 계속 붙잡고 있었다.
무려 거의 2일이나!!!
진행과정
우선 api를 불러서 데이터를 출력하는 것부터 막혔다. 대체 어떤 것이 데이터를 담고 있는 변수인 것인가...?
우선 options를 console.log로 출력하였고, undifined가 나왔다. 이 변수 안에는 배열이 없다.
const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization:
"어쩌구저쩌구~ 긴 영어"
},
};
fetch('https://api.themoviedb.org/3/movie/now_playing?language=ko-KR&page=1', options)
.then((response) => response.json()) //읽어온 데이터를 json으로 변환
.then((response) => { ~~ })
.catch((err) => console.error(err));
그 다음으론 아마 response 을 출력했던 것 같다.(json 아니면 response) console.log( response .reault[0])을 하였고, 예상했던 데이터가 나왔다! 그래서 이 데이터에 있는 result가 필요했기에, 배열에 객체를 담아주었다.
.then((response) => {
if (typeof window !== 'object') return;
const movieLists = response['results'];
이제 데이터가 어디있는지 아는데, 그 다음으로 동적으로 doc메서드를 이용해 출력을 해야하는데, 어떻게 할지 감이 잘 안 와서 앞서 정리했던 JSON공식 문서를 확인하였다. 역시 예제가 있었고, 예제를 토대로 천천히 이해하며 진행하였다.
let movieTitle = movieList.title;
let h2 = document.createElement('h2');
h2.textContent = movieTitle;
우선 첫 번째 줄은 movieTitle이라는 변수에 배열 안의 객체 데이터를 할당하는 것이다.
그리고 두 번째는 createElement를 선언해서 h2라는 변수를 입력할 때마다 h2의 html태그가 나오도록 하는 것이다.
그리고 마지막은 그것을 사용해 movieTitle을 생성한는 것이었다.
이제 동적으로 할당까지 할 수 있으니, 데이터를 반복문을 통해 카드로 나타내야했는데, 조건에 forEach가 있었다.
movieLists.forEach((movieList) => {
그리하여 위에 있는 createElement를 forEach문에 넣어줌으로써 돌아가면서 카드를 하나씩 생성했고,
이와 같은 화면이 나오게 되었다.
여기까지는 나름 할 만했으나, 그 다음이 고비였다. 바로 검색 기능이다. filer와 includes, toLowerCase를 사용했어야했다.
title값을 이용해 필터처리를 해야했기에 다음과 같은 코드가 나왔다.
/* 검색 */
const searchInput = document.getElementById('input');
function showSearchResult() {
section.innerHTML = '';
console.log(movieLists);
let searchWord = searchInput.value;
newDataList = movieLists.filter((object) => {
return object.title.toLowerCase().includes(searchWord);
});
return newDataList.forEach((searchMovie) => {
console.log('실행이 됐습니까????????????');
let movieArticle = document.createElement('article');
movieArticle.className = 'modal_btn';
movieArticle.id = searchMovie.id;
let movieDiv = document.createElement('div');
...
let h2 = document.createElement('h2');
...
img.src = moviePoster;
h2.textContent = movieTitle;
...
/* 이미지, 제목, 별점 */
movieArticle.appendChild(img);
...
});
}
const enterKey = (e) => {
if (e.code === 'Enter') {
showSearchResult();
}
};
searchInput.addEventListener('keypress', (event) => {
enterKey(event);
});
뭐라고 설명을 해야할지 잘 모르겠는데, html에서 input에서 enter을 누르면 showSearchReault함수가 실행되도록 하던에 화살표 함수를 만들어주었다.
우선 함수가 실행되면 화면을 초기화 해줌으로서 검색된 영화들을 리스트로 나열할 수 있게 하였다.
그리고 인풋값을 받아줄 변수를 하나 지정한 뒤, 함수가 실행되면 입력값을 받아 필터링을 해줄 새로운 배열을 만든다.
여기서 object가 무엇인가 헷갈릴 수도 있는데, 이는 movieLists를 가리키는 것이다. searchWord를 가지고 있는 배열 생성.
그렇게 받아온 배열을 forEach문을 돌려서 카드를 만드는 그런 것이었다.
쓰면서도 map, filter가 너무너무 긴가민가하다. option이 받아오는 것이 ~.filter 에서 ~배열이 맞는지.
내일 다시 공부해봐야 할 것 같다. 아래는 코드 전문이다.
JS
const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization:
'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkMzcxNjA2ZDhjZjFhNzExMGM3NDA4NDgyMzRkYTI5OCIsIm5iZiI6MTcyMTg5NTIxOS42Mzc4OTQsInN1YiI6IjY2OWRhNDQxZjE3YTkxMjZkMjRjMzE2ZSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.ee981DIwG9c7sMsfzEQ20JrbYWmpJyD8_TcWcO0NXYM',
},
};
fetch('https://api.themoviedb.org/3/movie/now_playing?language=ko-KR&page=1', options)
.then((response) => response.json()) //읽어온 데이터를 json으로 변환
.then((response) => {
if (typeof window !== 'object') return;
const movieLists = response['results'];
const section = document.querySelector('section');
section.className = 'movies';
const divTitle = document.getElementsByClassName('titleVote');
movieLists.forEach((movieList) => {
let movieArticle = document.createElement('article');
movieArticle.className = 'modal_btn';
movieArticle.id = movieList.id;
movieArticle.onclick = () => {
alert(movieList.id);
console.log('hello');
};
let movieDiv = document.createElement('div');
let moviePoster = `https://image.tmdb.org/t/p/w500/${movieList.poster_path}`;
let movieTitle = movieList.title;
let movieOverview = movieList.overview;
let movieVoteAverage = movieList.vote_average;
let div = movieDiv;
movieDiv.className = 'myClass';
let article = document.createElement('article');
let img = document.createElement('img');
let h2 = document.createElement('h2');
let p1 = document.createElement('p');
let p2 = document.createElement('p');
img.src = moviePoster;
h2.textContent = movieTitle;
p1.textContent = movieOverview;
p2.textContent = movieVoteAverage;
/* 이미지, 제목, 별점 */
movieArticle.appendChild(img);
movieDiv.appendChild(h2);
movieDiv.appendChild(p2);
movieDiv.appendChild(p1);
movieArticle.appendChild(movieDiv);
section.appendChild(movieArticle);
});
/* 검색 */
const searchInput = document.getElementById('input');
function showSearchResult() {
section.innerHTML = '';
console.log(movieLists);
let searchWord = searchInput.value;
newDataList = movieLists.filter((object) => {
return object.title.toLowerCase().includes(searchWord);
});
console.log(newDataList);
console.log('실행이 됐습니까????????????');
return newDataList.forEach((searchMovie) => {
console.log('실행이 됐습니까????????????');
let movieArticle = document.createElement('article');
movieArticle.className = 'modal_btn';
movieArticle.id = searchMovie.id;
let movieDiv = document.createElement('div');
let moviePoster = `https://image.tmdb.org/t/p/w500/${searchMovie.poster_path}`;
let movieTitle = searchMovie.title;
let movieOverview = searchMovie.overview;
let movieVoteAverage = searchMovie.vote_average;
console.log('그런듯!!!!!!!!!!!!');
let div = movieDiv;
movieDiv.className = 'myClass';
let article = document.createElement('article');
let img = document.createElement('img');
/**/
let h2 = document.createElement('h2');
let p1 = document.createElement('p');
let p2 = document.createElement('p');
img.src = moviePoster;
h2.textContent = movieTitle;
p1.textContent = movieOverview;
p2.textContent = movieVoteAverage;
/* 이미지, 제목, 별점 */
movieArticle.appendChild(img);
movieDiv.appendChild(h2);
movieDiv.appendChild(p2);
movieDiv.appendChild(p1);
movieArticle.appendChild(movieDiv);
section.appendChild(movieArticle);
});
}
const enterKey = (e) => {
if (e.code === 'Enter') {
showSearchResult();
}
};
searchInput.addEventListener('keypress', (event) => {
enterKey(event);
});
})
.catch((err) => console.error(err));
/*
받아온 데이터 result 화면에 list로
서버에서 받아온 동적인 데이터
부모 컨테이너만 받아오기
반복문 for Eaxh로 돔조작 크리에이트 엘리먼트
구글에 tmdb api image PaymentResponse공식문서에 나와있음
*/
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link href="https://fonts.googleapis.com/css?family=Faster+One" rel="stylesheet" />
<link rel="stylesheet" href="movie.css" />
<script type="text/javascript" src="01.js"></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"
integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</head>
<body>
<header>
<div class="search_wrapper">
<input type="text" id="input" class="search" placeholder="검색" />
<button class="search-btn" type="submit">
<i class="fas fa-search"></i>
</button>
</div>
</header>
<section></section>
</body>
</html>
CSS
@font-face {
font-family: 'GmarketSansMedium';
src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2001@1.1/GmarketSansMedium.woff') format('woff');
font-weight: normal;
font-style: normal;
}
body {
background-color: rgb(15, 15, 15);
font-family: 'GmarketSansMedium';
}
header {
margin: 3%;
}
section {
margin: 0 20% 0 20%;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, minmax(100px, auto));
justify-content: center;
gap: 20px;
}
article {
width: 300px;
margin: 5%;
color: #fff;
border-radius: 15px 15px 0 0;
}
article img {
width: 100%;
height: 500px;
object-fit: cover;
border-radius: 15px 15px 0 0;
}
.myClass {
background-color: #fff;
border-radius: 0 0 15px 15px;
color: black;
padding: 5%;
}
.search_wrapper {
position: relative;
top: 50%;
left: 80%;
background-color: #618264;
width: fit-content;
height: 40px;
padding: 10px 10px 10px 30px;
border-radius: 40px;
}
.search {
background-color: #618264;
color: #fff;
padding: 0;
background: none;
border: none;
outline: none;
color: white;
font-size: 15px;
line-height: 40px;
}
.search-btn {
color: #ffffff;
float: right;
width: 40px;
height: 40px;
border-radius: 50%;
border: 1px solid #618264;
background: #949ba000;
display: flex;
align-items: center;
justify-content: center;
}
.search_wrapper input::placeholder {
color: #ffffff;
font-family: 'GmarketSansMedium';
}
'캠프 > 미니 프로젝트' 카테고리의 다른 글
미니 프로젝트 3,4일차 (쓰다가 ㅌ탈주) (0) | 2024.07.22 |
---|---|
미니 프로젝트 2일차 (0) | 2024.07.16 |
미니 프로젝트 1일차 (0) | 2024.07.16 |