[24.07.26] 헤매고 또하고 헤매고 다시하고

생각보다 기능 분리하는게 헷갈려서 꽤 오래걸렸다..🥹 그래도 계속계속 하다보면 늘겠지..

 

js 파일 모듈화하기

api로 데이터를 가져오는 것에 감이 잡히질 않아 처음에는 데이터를 받아오고, HTML 구조로 출력하는 것을 모두 하나의 파일에서 처리하도록 작성했다. 테스트를 조금 거치면서 내가 작성한 코드의 흐름이 이해가 돼서 분리를 해보긴했는데.. 정상적으로 작동도 하지만 고민이 생겼다

 

지금 분리한 구조는 app.js에서 tmdb.js의 fetch()를 호출 -> fetch가 마무리되면 movie.js의 함수를 호출해 데이터로 HTML 구조를 추가함 각 함수를 불러오는 건 app.js인데 데이터는 tmdb.js에서 가져옴

어차피 tmdb.js에서 fetch한 데이터를 export해뒀고, 그걸 app.js가 임포트 -> 다시 movie.js로 넘겨주는 것보다 그냥 movie.js가 바로 받아오는게 낫지않을까?했었는데 꼬인건가 싶기도하고.. 

 

일단 조언을 구하고싶은 부분 -> 일단 이게 모듈화가 맞는지? 그리고 하나의 파일에서 담길 동작, 다른 파일로 분리할 동작을 어떤 기준을 두면 좋을지.. => 모듈화 기준?

1) 지금 있는 세 js 파일구조

2) 지금 완벽하게는 못하겠지만..  어떤 기준으로 분리한다고 생각하면 좋을지?

 

=> 피드백

1) tmdb.js 에서 모든 자료를 export 해서 사용할 수 있게하면 추후 수정이 어려울 수 있다! fetchFromTMDB() 함수명에서 의도가 정확하니 이 함수의 return 값으로 정보를 넘겨주고 화면을 그려내는 함수는 그 return 값을 받아 해보자

2) UI를 기준으로 파일을 분리해보자. 지금 구조에서라면 fetch해오는 파일에서 데이터를 그리는 것까지 같이 하거나 그리는 동작을 app.js에서 수행할 것 같음

 

 

영화 id alert창 띄우기 (필수구현요소)

영화 카드를 클릭했을 때 id를 클릭하는 창을 구현했다.  이벤트가 발생한 요소를 전달받기위해 addEventListener의 콜백함수에 event를 다른 함수인 alertID(event)로 넘겨주는 함수로 만들어줬다. 사실 너무 간단한 동작이라 바로 익명함수로 써줬어도 됐을 것 같지만.. 어떻게 이벤트가 발생한 요소를 인식할 수 있는지 해보고싶어서 개별 함수로 나눠보았다.

그래도 예전에 사전과제 때 해본 실수가 있어서 ㅎㅎㅎ 아직 카드를 만들지도 않았는데 이벤트를 등록하지 않고 카드를 만들 때 이벤트를 등록하는 건 이번엔 잘 했다!

movieCard.addEventListener('click', (event) => {
  alertID(event);
});

저기에 바로 alertID(event)하면 안되는 이유: addEventListener 이후로 코드를 읽으며 함수 호출 ()이 있으므로 바로 실행해버린다. 아직 클릭 이벤트가 발생하지도 않았는데!

발생한 이벤트가 없으니 undefined라고 뜨는 것 같다. 따라서 event를 매개변수로 넘겨 alertID를 호출하는 함수를 등록시켜둬야 하는 것!

 

 

버튼 클릭시 새로고침 되지 않도록 하기?

검색 버튼을 클릭했을 때 새로운 html 파일로 연결되어 검색결과가 보이는 화면으로 넘어가고싶었는데 계속 새로고침이 되면서 넘어가질 않았다 혹시 이벤트가 실행되지 않는건가 싶어서 로그를 찍어봤지만 콘솔창에 빠르게 띄워졌다 사라지는걸로 보아선 호출은 되는데 동시에 바로 새로고침이 되는 것 같았다..! 그래서 button 태그가 아닌 input type=button으로 변경해보았더니 새로고침 되지 않고 새로운 html 페이지로 넘어갔다!

// 기존
<button class="searchButton">검색</button>

// 변경 후
<input type="button" class="searchButton">검색</input>
// 버튼에 걸려있는 이벤트
searchButton.addEventListener('click', searchMovies);


function searchMovies() {
  window.location.href = './searchResult.html';
  console.log('HI');  // 테스트용
}

 

 

검색버튼 클릭 후 이동한 페이지에서 키워드 받아보기

검색해보다가 URL 쿼리파라미터 발견하여 적용해보는 중..

 

버튼 이벤트에서 URL로 keyword 전달

function searchMovies() {
  const keyword = searchText.value;
  window.location.href = `./searchResult.html?keyword=${encodeURIComponent(keyword)}`;
}

 

결과를 보여줄 html 파일은 URL에서 키워드 추출

const params = new URLSearchParams(window.location.search);
const keyword = params.get('keyword');

 

이제 해볼 것 API로 받아온 데이터에서 어떻게 걔네만 받아올 것인가

 

API 데이터를 받아오는 js에서 export 한 함수를 import 해서 데이터를 받아오려하니 Cannot use import statement outside a module 오류 발생

검색해보니 모듈이 아닌 곳에서 import 해오려할 때 발생한다고 하는데..

1) import 경로 살펴보기

2) html에서 type='module'로 연결했는지 살펴보기 -> 여기서 안해뒀었다 ㅎㅎㅎㅎ

 

이제 다음 산..^^

fetch로 받아온 데이터를 이 스크립트에서 받아 쓰려하니 undefined가 떴다 ㅠㅠ

 

힌트를 받아왔다! 꼭 API 호출을 한군데에서만 해와서 그 데이터만 돌려서 쓸 필요가 없다. API가 필요한 곳에서 필요한 정보를 받아오면된다!!

그리고 sort도 잘못됐다 나는 전체 목록에서 top10을 받아오고싶었던건데 topRated 중에서 1페이지에서 정렬을 하고있었다.. 이것도 수정해보기!!!!! 꼭!!

 

일단 키워드를 주면 그걸 받아오는 API를 써보자

https://developer.themoviedb.org/reference/search-movie

 

Search - Movies

Search for movies by their original, translated and alternative titles.

developer.themoviedb.org

-> 해결!

 

동일한 이벤트를 하나의 스크립트에서 관리

index.html과 searchResult.html 에 공통적으로 타이틀 텍스트와 검색창이 있다. 둘은 같은 역할을 갖고있는데 이 동작을 하나의 스크립트에서 구현하고, 두 html이 로드될 때 그 스크립트에서 이벤트 등록 함수를 import 해오면 편할 것 같아 분리해보았다.

 

// globalEvents.js
const searchButton = document.querySelector('.searchButton');
const searchText = document.querySelector('.searchText');
const title = document.querySelector('.title');

export function setGlobalEvents() {
  searchButton.addEventListener('click', searchMovies);
  title.addEventListener('click', goIndex);
}

function searchMovies() {
  const keyword = searchText.value;
  window.location.href = `./searchResult.html?keyword=${encodeURIComponent(keyword)}`;
}

function goIndex() {
  window.location.href = `./index.html`;
}
// 각 파일에서 동일하게 Import
import {setGlobalEvents} from './globalEvents.js';
setGlobalEvents();

 

 

검색 결과가 없을 때!

결과가 없다는 걸 보여줄 div를 미리 만들어두고 dispaly: none을 on, off 하자!

 

card.css 에서 보이지 않도록 설정할 클래스 스타일을 만들어줌

.displayNone {
  display: none;
}

HTML에서는 미리 구조를 만들어두고 displayNone, 검색후 결과가 없을 때 displayNone을 클래스에서 지워주고 이후 검색결과를 보여주는 동작을 실행하지 않음

function showResult(searchResult) {
  if (searchResult.length === 0) {
    notFound.classList.remove('displayNone');
    return;
  }
  
  // 생략
}

 

 

기본 포스터 설정

일부 영화는 포스터 이미지가 등록되어 있지 않은 것 같다. 그래서 포스터가 없는 경우를 대비해 기본 background-color: black을 지정해두었다.

 

keyword 빈 상태로 전달되는 경우

검색할 텍스트를 입력하지 않고 엔터키/버튼를 눌렀을 때 keyword가 null인 상태로 전달되면서 null을 검색한 결과와 같은 결과가 나타났다

null을 실제로 검색했을 때와 구분하기 위해 keyword가 null일때는 검색결과가 없는 것으로 표시하도록 설정했다.

function showResult(searchResult) {
  if (searchResult.length === 0 || keyword === null) {
    notFound.classList.remove('displayNone');
    return;
  }
  
  // 생략
}

스타일 설정하기

input 중에서 특정 타입을 선택하고 싶은 경우 input[type="text"]

 

마우스를 올렸을 때 배경이미지(포스터)가 어두워지면서 영화의 정보가 흰글자로 보이도록 하고싶었다!

.movieCard:hover {
  filter: brightness(0.5);
  color: white;
}

별생각없이 hover했을 때 style 속성을 위와같이 줬더니 어쩌면 당연하게도.. 이미지뿐만 아니라 글자까지 다 어두워졌다 ㅋㅋㅋㅋ

ㅜㅜ

 

뭔가.. 레이어 같은게 얹어졌으면 좋겠다 생각하다가 사전과제 때 카드 뒤집기 효과를 주면서 위치값을 조정해 두 요소가 동일한 위치에 있게 설정한 것이 생각났다. 구글링을 통해 약간의 힌트를 얻고 아래와같이 수정했다.

 

<div class="movieCard">
  <img src=${posterImage} class="posterImage">
  <div class="infoLayer">
    <p class="title">${title}</p>
    <p class="ratingScroe">${ratingScore}</p>
    <p class="overview">${overview}</p>
  </div>
</div>

그래서 HTML 구조를 이렇게 변경해주고

 

.movieCard {
  /* 생략 */
  position: relative;
}

.posterImage {
  width: inherit;
  height: inherit;
  object-fit: cover;
}

.infoLayer {
  position: absolute;
  top: 0;
  left: 0;
  width: inherit;
  height: inherit;
  color: white;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.5s;
}

.movieCard:hover .infoLayer {
  opacity: 1;
}

포스터 이미지와 정보들의 가장 상위 컨테이너인 .movieCard -> position: relative, 텍스트 정보가 들어갈 infoLayerfmf absolute로 지정해주었다. (카드 영역에 꽉차게 하기 위해) 그러고 infoLayer를 검정색 투명 배경 + 흰글씨로 설정하고 기본상태를 opacity 0 -> hover 됐을 때 opacity 1로 변경하되 transition으로 0.5초동안 변하도록 설정했다.

 

딱 원했던거다~~~~~ 이제 폰트 좀 적용하고 꾸며주자

 

마무리!!

 

오늘은 마무리하고 정처기 준비하러..

주말동안 더 해야할 것!!

1) 필수

- 배열 메서드 추가 사용하기 (과제 필수 구현요소)

- 타이틀 부분 폰트 적용

 

2) 하고싶은 것

- 물결 효과.......

- 슬라이더..

- 컬러팔레트 구경한 거 변수로 저장해두고 적용하기..

- top10 외에 다른 리스트 추가해보기

- 페이지 접속시 검색창에 커서 고정??