바닐라-영화소개프로젝트(2)

김희목·2024년 8월 13일
0

패스트캠퍼스

목록 보기
39/53

프로젝트 깃허브

1. Headline 컴포넌트

Headline 컴포넌트는 Home.js에서 사용할 하위 컴포넌트입니다.
즉 Home.js에서 화면에 보여줄 요소입니다.

Headline.js는 src폴더안에 components라는 폴더를 만들어 그 안에 만들어 주세요.

Headline.js

import { Component } from "../core/heropy";

export default class Headline extends Component {
  render() {
    this.el.classList.add('headline')
    this.el.innerHTML = /* html */ `
      <h1>
        <span>OMDb API</span><br>
        THE OPEN<br>
        MOVIE DATEBASE
      </h1>
      <p>
      The OMDb API is a RESTful web service to obtain movie information, 
      all content and images on the site are contributed and maintained by our users.<br>
      If you find this service useful, please consider making a one-time donation or become a patron.
      </p>
    `
  }
}

1.컴포넌트 만들기

우선 컴포넌트를 만들기 위해 우리가 만들어 놓은 heropy.js에서 컴포넌트 기능을 가져와 사용해야 합니다.

2.Headline 클래스 정의

export default class Headline extends Component {
  render() {
    ...
  }
}

export default는 Headline 클래스를 다른 파일에서 쉽게 가져올 수 있게 합니다.

Headline 클래스는 Component 클래스를 상속받아 새로운 컴포넌트를 정의합니다.

3. render() 메서드

  render() {
    this.el.classList.add('headline')
    this.el.innerHTML = /* html */ `
      <h1>
        <span>OMDb API</span><br>
        THE OPEN<br>
        MOVIE DATEBASE
      </h1>
      <p>
      The OMDb API is a RESTful web service to obtain movie information, 
      all content and images on the site are contributed and maintained by our users.<br>
      If you find this service useful, please consider making a one-time donation or become a patron.
      </p>
    `
  }
  • render() = 이 메서드는 컴포넌트를 실제로 화면에 렌더링하는 역할을 합니다. 즉, 이 메서드가 실행될 때
    Headline 컴포넌트의 HTML 구조와 내용을 정의하고 표시합니다.

  • this.el.classList.add('headline'): 이 줄은 Headline 컴포넌트의 최상위 요소(this.el)에 headline이라는 클래스를 추가합니다. 이 클래스는 CSS 스타일링을 적용하는 데 사용됩니다.

  • this.el.innerHTML = ...: 이 코드는 this.el의 내부 HTML 콘텐츠를 설정합니다. innerHTML 속성을 사용하여 HTML 문자열을 요소 안에 삽입합니다.

즉 이 Headline 컴포넌트는 heropy.js에 Componet 클래스를 상속받아 컴포넌트를 정의하고, 작성된 Headline.js는 Home.js에서 화면에 보여지게 됩니다.

그렇다면 Home.js에서 어떻게 Headline 컴포넌트를 사용하는지 코드를 보겠습니다.

Home.js

import { Component } from "../core/heropy";
import Headline from "../components/Headline";

export default class Home extends Component {
  render() {
    const headline = new Headline().el;

    this.el.classList.add("container");
    this.el.append(headline);
  }
}

import를 사용하여 우리가 작성한 Headline 컴포넌트를 가져온 후 headline이라는 변수안에 생성자 함수로 컴포넌트를 호출 한 후 el 속성을 가져와 넣어줍니다.

headline 변수는 append 메서드를 통해 Home.js의 요소로 넣어주게되면 Home.js에서 화면에 보여지게 됩니다.

그리고 해당 코드를 보면 classList를 통해 상위요소에 'container'이라는 클래스를 추가했습니다.
그 이유는 HTML 요소에 특정한 스타일을 적용하기 위해서입니다.


2. Search 컴포넌트

다음 컴포넌트는 Home.js에서 보여지고 있는 Headline 바로 밑에 검색부분을 만들기 위해
우리는 components 폴더 안에 Search.js라는 컴포넌트를 만들겁니다.

Search.js

import { Component } from "../core/heropy";

export default class Search extends Component {
  render() {
    this.el.classList.add("search");
    this.el.innerHTML = /* html */ `
      <input 
      placeholder="Enter the movie title to search!" />
      <button class="btn btn-primary">Search!</button>
    `;

    const inputEl = this.el.querySelector("input");
    inputEl.addEventListener("input", () => {
      	//
    });
    inputEl.addEventListener("keydown", (event) => {
   		//
      }
    });

    const buttonEl = this.el.querySelector("button");
    buttonEl.addEventListener("click", () => {
      	//
    });
}
}

해당 코드를 보면 현재 검색할 수 있는 기능들은 작성해놓지 않았기 때문에 이벤트들만 작성을 해놓은 상태이고,
이벤트 부분들을 제외하고 살펴보겠습니다.

우선 classList를 통해 search라는 클래스를 해당요소에 추가해줍니다.

이후 그 하위에 input과 button을 넣어주고 해당 input요소와 button버튼을 클릭했을시에 이벤트들을 작성해놓은 상태인데 이 상태 그대로 css를 작성하여 구조를 잡아주고 Home.js에 넣어주도록 하겟습니다.

Home.js

import { Component } from "../core/heropy";
import Headline from "../components/Headline";
import Search from "../components/Search";

export default class Home extends Component {
  render() {
    const headline = new Headline().el;
    const search = new Search().el;

    this.el.classList.add("container");
    this.el.append(headline,search);
  }
}

현재 진행 상황


3. 상태 관리

그렇다면 Search 컴포넌트에서 사용할 검색기능을 개발하기 위해서는 Search 컴포넌트에서 검색을 한 다음에
영화 목록에 데이터만 가져오기만 하는 것이 아니고 해당 데이터를 화면에 실제 보여지는 요소로 출력해야 합니다.

그래서 해당 화면의 출력은 Movie List라는 컴포넌트를 만들어 진행을 하겠습니다.
또한 Search 컴포넌트에서 만든 input과 button을 통해 검색한 내용을 Movie List라는 컴포넌트에서 사용할 수 있어야 합니다.

즉, 서로 다른 컴포넌트에서 같은 데이터를 취급해야 하는 스토어 개념이 필요합니다.

그래서 우리가 만들어 놓은 heropy.js에서 store 클래스를 사용하도록 하겠습니다.

혹시 Store가 이해가 되지 않는다면 heropy에서 학습하고 오십시오.

이러한 내용들을 가지고 영화 검색과 관련된 store의 상태 관리 코드를 작성해보겠습니다.

우선 src폴더에서 새로운 폴더를 만들겠습니다. 이름은 store라고 작성하여 폴더를 만든 후
그 내부에다가 moive.js라는 파일을 만들어 주세요.

현재 작성된 코드는 완성된 상태가 아닙니다.

moive.js

import { Store } from '../core/heropy'

const store = new Store({
	searchText: '',
    page: 1,
    movies: []
})

export default store
export const searchMovies = async page => {
	const res = await fetch(`https://omdbapi.com?apikey=?&s=${store.state.searchText}&page=${page}`)
    const json = await res.json()
}

우선 heropy.js에 Store 클래스는 상태(state) 관리와 상태 변경 감시(observer)를 위한 시스템을 가져와서 사용하고 있습니다.

이 클래스는 상태가 변경될 대 등록된 콜백 함수를 호출하여 변경사항을 반영합니다.

또한 subscribe 메서드를 통해 특정 상태 속성의 변경을 감지하여 실행될 콜백함수를 등록하고 호출할 수 있습니다.

이후 해당 코드를 살펴보면 이 코드는 Store 클래스를 사용해 store 객체를 생성합니다.
store 객체는 영화 검색과 관련된 세 가지 상태를 관리합니다

  • searchText: 사용자가 검색 창에 입력한 검색어를 저장합니다.
  • page: 현재 검색 결과의 페이지 번호를 저장합니다.
  • movies: 검색 결과로 얻은 영화 목록을 저장합니다.

이 store 객체는 영화 검색 애플리케이션 전반에 걸쳐 상태를 중앙에서 관리하고, 필요한 경우 다른 컴포넌트나 모듈에서 이 상태를 사용할 수 있게 합니다.

searchMovies 함수: 이 함수는 비동기 함수로, 영화 정보를 OMDb API에서 가져오는 역할을 합니다. 주어진 페이지 번호에 해당하는 검색 결과를 가져옵니다.

API 요청

fetch 요청: fetch 함수는 OMDb API에 GET 요청을 보내, searchText 상태와 page 번호에 따라 영화를 검색합니다.

쿼리 파라미터: URL에 있는 s=${store.state.searchText} 부분은 store 객체의 searchText 상태를 사용하여 API에 요청을 보냅니다. 예를 들어, 사용자가 "Star Wars"를 검색하면 store.state.searchText에 "Star Wars"가 저장되고, 이 값을 사용해 https://omdbapi.com?apikey=?&s=Star Wars&page=1과 같은 형태의 요청이 만들어집니다.

페이지 번호: URL의 page=${page} 부분은 특정 페이지 번호에 대한 검색 결과를 요청합니다.

응답 처리

await res.json(): API 요청의 응답을 JSON 형식으로 변환합니다. 이 데이터를 이후의 로직에서 활용할 수 있습니다.

여기까지 작성된 내용을 Search.js에서 가져와 한번 어떻게 보여지는지 확인해보겠습니다.

Search.js

import { Component } from "../core/heropy";
import movieStore, { searchMovies } from "../store/movie";

export default class Search extends Component {
  render() {
    this.el.classList.add("search");
    this.el.innerHTML = /* html */ `
      <input 
      placeholder="Enter the movie title to search!" />
      <button class="btn btn-primary">Search!</button>
    `;

    const inputEl = this.el.querySelector("input");
    inputEl.addEventListener("input", () => {
      movieStore.state.searchText = inputEl.value;
    });
    inputEl.addEventListener("keydown", (event) => {
      if (event.key === "Enter" && movieStore.state.searchText.trim()) {
        searchMovies(1);
      }
    });

    const buttonEl = this.el.querySelector("button");
    buttonEl.addEventListener("click", () => {
      if (movieStore.state.searchText.trim()) {
        searchMovies(1);
      }
    });
}
}

Search라는 컴포넌트는 사용자가 영화제목을 입력하고, 버튼을 클릭하면 영화를 검색하는 기능을 제공합니다.

우선 store 폴더의 moive.js파일을 가져오는데 export default의 경우 {}를 생략하고,
export인 경우는 {}를 사용하여 불러올 수 있습니다.

  • movieStore: 영화 검색 애플리케이션의 상태를 관리하는 객체입니다. searchText, page, movies와 같은 상태를 중앙에서 관리합니다.
  • searchMovies: OMDb API에 요청을 보내고, 영화 데이터를 검색하는 비동기 함수입니다.

input 이벤트 리스너 설정

  • input 이벤트: 사용자가 input 필드에 입력할 때마다 발생하는 이벤트입니다. 이 이벤트를 통해 입력된 값이 실시간으로 movieStore.state.searchText에 저장됩니다.

  • keydown 이벤트: 사용자가 키보드를 누를 때 발생하는 이벤트입니다. 여기서는 Enter 키를 눌렀을 때 검색어가 비어 있지 않다면(trim()으로 공백 제거 후 확인) searchMovies(1) 함수를 호출하여 첫 번째 페이지의 영화를 검색합니다.

button 요소와 클릭 이벤트 처리

  • click 이벤트 리스너 설정 = 사용자가 버튼을 클릭할 때 발생하는 이벤트입니다. 이 이벤트에서 movieStore.state.searchText에 값이 있는지 확인하고, 값이 있다면 searchMovies(1) 함수를 호출하여 첫 번째 페이지의 영화를 검색합니다.

0개의 댓글