표제 이미지

검색 API & 무한 스크롤

이번 포스팅에서는 카카오 API를 활용하여 책 검색 기능을 구현하고,
무한 스크롤 기능을 추가해보겠다. 결과물을 미리 살펴보면 다음과 같다.

1. 내부 페이지 구현

먼저 필요한 것은 내부 페이지인 HomeSearchPageView를 만드는 것이다.
그리고 HomePageView와 HomeSearchPageView 간의 이동, 즉 라우팅을 구현할 것이다.

화면 도식

시작해 보자.
현재 HomePageView는 두 개의 탭으로 구성되어 있다. (헤더는 제외)
하나는 ‘검색 탭’이고 다른 하나는 ‘달력 탭’이며,
두 개의 탭을 .home__tabs라는 컨테이너가 담고 있는 구조다.
이 중 검색 탭을 클릭했을 때 HomeSearchPageView로 이동하게 만들 것이다.

홈 화면 구조

우선 마크업은 아래와 같다.

  <div class="home__tabs">
    <a class="home__tab home__tab--search" href="/home/search">
      <h2>책을 검색해 보세요.</h2>
      <p>읽고 있는 책이 있나요?</p>
    </a>
    <a class="home__tab home__tab--calendar" href="/home/calendar">
      <div>
        <h2>독서 달력</h2>
        <p>이번 달은 얼마나 읽었나요?</p>
      </div>
      <i class="fa-solid fa-calendar-days"></i>
    </a>
  </div>

다음은 자바스크립트다.

// HomePageView.js
HomePageView.bindElement = function () {
  this.tabs = this.element.querySelector('.home__tabs');
};

HomePageView.setEvent = function () {
  this.tabs.addEventListener('click', (e) => this.onClickTab(e));
};

HomePageView.onClickTab = function (e) {
  const tab = e.target.closest('.home__tab');
  if (tab) {
    e.preventDefault();
    const path = tab.getAttribute('href');
    this.dispatch('@clickTab', { path });
  }
};

.home__tabs를 변수로 바인딩 한 뒤, click 이벤트 핸들러로 onClickTab을 등록하였다.
이러면 .home__tabs 내부 요소에서 click 이벤트가 발생해도 onClickTab이 호출된다.
이벤트의 전파 때문이다.

클릭이 발생한 탭을 tab 변수에 저장한 뒤, a 태그의 기본 동작을 막는다.
그리고 href 속성에서 path를 가져와서
커스텀 이벤트 @clickTab를 디스패치하고 path를 전달한다.

@clickTab은 HomeController에서 처리한다.

// HomeController.js
  addCustomEvent() { // 커스텀 이벤트 핸들러 등록
    HomePageView
      .on('@clickTab', (e) => this.onClickTab(e.detail.path));
  },

  onClickTab(path) {
    history.pushState(null, null, path);
    MainController.route();
  },

@clickTab의 핸들러는 onClickTab라는 메서드다.
전달받은 path 문자열로 url을 변경한 후, MainController의 route 메서드를 호출한다.

MainController의 route 메서드에서 다음 코드를 추가한다.

// MainController.js
  route() {
    const path = window.location.pathname;

    if (path === '/' || path === '/home') {
      HomePageView.setup(page);
      HomeController.init();
      NavigationView.show(); // 추가한 코드
      return;
    }
     // ---------- 추가한 코드 ----------
    if (path === '/home/search') {
      HomeSearchPageView.render(page);
      NavigationView.hide();
      return;
    }

path가 /home/search일 때, HomeSearchPageView를 렌더링한다.
그리고 NavigationView.hide를 통해 하단 네비게이션 바를 감춘다.

자 이제 HomeSearchPageView.js를 생성하고 다음과 같이 작성한다.

import View from './View.js';

const HomeSearchPageView = Object.create(View);

HomeSearchPageView.setup = function (element) {
  this.init(element);
};

HomeSearchPageView.render = function () {
  const html = this.getHtml();
  this.replaceChildren(html);
  this.bindElement();
  this.setEvent();
};

HomeSearchPageView.getHtml = function () {
  return /* html */ `
    <header class="search-page__header">
      <button class="search-page__btn">
        <i class="fa-solid fa-arrow-left"></i>
      </button>
    </header>
    <div class="search-page__content">
      <h1 class="search-page__title">책을 찾아보세요!</h1>
      <form class="search-page__form">
        <i class="fa-solid fa-magnifying-glass"></i>
        <input
        class="search-page__input"
        type="text"
        placeholder="검색할 책을 입력해주세요."
        autofocus
        >
      </form>
    </div>
    <ul class="search-list"></ul>
  `;
};

아래와 같은 화면이 만들어졌다. CSS는 생략하도록 하겠다.

화면 결과


뒤로 가기 버튼, 즉 search-page__btn을 클릭했을 때
다시 HomePageView로 이동하게 만들 것이다.
다음과 같이 코드를 추가한다.

HomeSearchPageView.bindElement = function () {
  this.btn = this.element.querySelector('.search-page__btn');
};

HomeSearchPageView.setEvent = function () {
  this.btn.addEventListener('click', () => this.onClickPrev());
};

HomeSearchPageView.onClickPrev = function () {
  this.dispatch('@clickPrev');
};

search-page__btn을 변수로 바인딩하고, click 이벤트 핸들러를 등록하였다.
이벤트 핸들러에서는 @clickPrev라는 커스텀 이벤트를 디스패치한다.

@clickPrev는 HomeController에서 처리한다.

// HomeController.js
  addCustomEvent() {
    HomePageView
      .on('@clickTab', (e) => this.onClickTab(e.detail.path));
    HomeSearchPageView // 추가한 코드
      .on('@clickPrev', () => this.onclickPrev());
  },

  onClickTab(path) {
    history.pushState(null, null, path);
    MainController.route();
  },

  // 추가한 코드
  onclickPrev() {
    history.pushState(null, null, '/home');
    MainController.route();
  },

onclickPrev에서는 /home으로 URL을 변경하고
MainController의 route 메서드를 호출한다.

이렇게 내부페이지의 구현 및 라우팅이 완료되었다.

2. 카카오 OpenAPI

(미완성입니다.)

드리는 말

학습용 프로젝트를 진행하며 작성한 글이며, 부정확한 내용이 있을 수 있습니다.
참고로만 읽으실 것을 권장드립니다. 감사합니다.