프로그래머스 고양이 사진 검색 사이트 - 개발 (컴포넌트 재구성)

Z6su3·2022년 4월 13일
1

변경을 위해 기존 모듈 삽입 방식과 함수형 컴포넌트로 재구성을 진행합니다.

프로젝트의 구성도는 다음과 같습니다.

main.js ← App.js ← api/api.js
← components(floder) ← SearchInput.js
← SearchResult.js
← ImageInfo.js

🐇 프로젝트 재구성

🥕 main 모듈 및 모듈 삽입

index.html

...
  <head>
		...
    <link rel="stylesheet" href="src/style.css" />
    <script type="module" src="src/main.js"></script>
    <title>Cat Search</title>
  </head>
  <body>
    <div id="App"></div>
  </body>
...

main.js

import App from "./App.js";

new App(document.querySelector("#App"));

🐇 컴포넌트 재구성

함수형 컴포넌트에 대한 구성 이해도가 낮다면 컴포넌트 구현을 통해 회고 진행합니다.

앞서 API 요청을 확인한 결과 데이터는 다음과 같은 형태로 반환됩니다. 고려하여 관련된 부분들은 수정 합니다.

responseData: {
	data: [{item}, {item}, {item}, ..., {item}],
}

🥕 App.js 재구성

App.js

import SearchInput from "./components/SearchInput.js";
import SearchResult from "./components/SearchResult.js";
import ImageInfo from "./components/ImageInfo.js";

import { request } from "./api/api.js";

export default function App($app) {
  this.state = {
    visible: false,
    image: null,
    data: [],
  };

  const searchInput = new SearchInput({
    $app,
    onSearch: async (keyword) => {
      const searchData = await request("search", keyword);
      this.setState({
        ...this.state,
        data: searchData.data,
      });
    },
  });

  const searchResult = new SearchResult({
    $app,
    initialState: [],
    onClick: (image) => {
      this.setState({
        visible: true,
        image,
      });
    },
  });

  const imageInfo = new ImageInfo({
    $app,
    initialState: {
      visible: false,
      image: null,
    },
  });

  this.setState = (nextState) => {
    this.state = nextState;
    searchResult.setState(this.state.data);
		imageInfo.setState({
      image: this.state.image,
      visible: this.state.visible,
    });
  };
}

🥕 SearchInput.js 재구성

SearchInput.js

export default function SearchInput({ $app, onSearch }) {
  this.$target = document.createElement("input");
  this.$target.className = "SearchInput";
  this.$target.placeholder = "고양이를 검색해보세요.|";
  $app.appendChild(this.$target);

  this.onSearch = onSearch;

  this.$target.addEventListener("keyup", (e) => {
    if (e.keyCode === 13) {
      this.onSearch(e.target.value);
    }
  });
}

🥕 SearchResult.js 재구성

추가 요구사항에 EventDelegation을 이용하여 클릭 이벤트를 수정이 있습니다. closest를 이용해서 이벤트 위임을 통한 최적화를 진행해준다. 해당 내용은 EventDelegation에 기재되어 있으니 회고 진행해주길 바랍니다.

SearchResult.js

export default function SearchResult({ $app, initialState }) {
  this.state = initialState;
  this.$target = document.createElement("div");
  this.$target.className = "SearchResult";
  $app.appendChild(this.$target);

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.render = () => {
		if (this.state.data) {
	    this.$target.innerHTML = this.state.data
	      .map(
	        (cat, index) => `
	        <div class="item" data-index="${index}">
	          <img src=${cat.url} alt=${cat.name} />
	        </div>
	        `
	      )
	      .join("");
		}
  };

  this.onClick = onClick;

  this.$target.addEventListener("click", (e) => {
    const $searchItem = e.target.closest(".item");
    const { index } = $searchItem.dataset;
    this.onClick(this.state[index]);
  });

  this.render();
}

🥕 ImageInfo.js 재구성

ImageInfo.js

export default function ImageInfo({ $app, initialState }) {
  this.state = initialState;
  this.$target = document.createElement("div");
  this.$target.className = "ImageInfo";
  $app.appendChild(this.$target);

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.render = () => {
    if (this.state.image) {
      const { name, url, temperament, origin } = this.state.image;

      this.$target.innerHTML = `
        <div class="content-wrapper">
          <div class="title">
            <span>${name}</span>
            <div class="close">x</div>
          </div>
          <img src="${url}" alt="${name}"/>        
          <div class="description">
            <div>성격: ${temperament}</div>
            <div>태생: ${origin}</div>
          </div>
        </div>`;
    }
    this.$target.style.display = this.state.visible ? "block" : "none";
  };

  this.render();
}
profile
기억은 기록을 이길수 없다

0개의 댓글