[JavaScript30] Day6: Ajax Type Ahead

sunshani·2023년 12월 7일
0
post-thumbnail

(Appleton 잘 지내닝,,)

무엇을 만들 것인가.

미국의 도시들에 대한 정보가 각각의 요소로 들어가 있는 데이터를 가져와 사용자가 입력하는 글자와 일치하는 도시나 주를 suggestion으로 보여주는 웹페이지. 입력을 할 때마다 업데이트가 되어야 하고 일치하는 글자에 대한 하이라이트도 추가해야한다.

코드를 살펴보자.

HTML

<form class="search-form">
  <input type="text" class="search" placeholder="City or State" />
  <ul class="suggestions">
    <li>Filter for a city</li>
    <li>or a state</li>
  </ul>
</form>

JavaScript

const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

const cities = [];
fetch(endpoint)
  .then(blob => blob.json())
  .then(data => cities.push(...data));

function findMatches(wordToMatch, cities) {
  return cities.filter(place => {
    // here we need to figure out if the city or state matches what was searched
    const regex = new RegExp(wordToMatch, 'gi');
    return place.city.match(regex) || place.state.match(regex)
  });
}

function numberWithCommas(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

function displayMatches() {
  const matchArray = findMatches(this.value, cities);
  const html = matchArray.map(place => {
    const regex = new RegExp(this.value, 'gi');
    const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
    const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
    return `
      <li>
        <span class="name">${cityName}, ${stateName}</span>
        <span class="population">${numberWithCommas(place.population)}</span>
      </li>
    `;
  }).join('');
  suggestions.innerHTML = html;
}

const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');

searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);

무엇을 배웠나.

spread 문법

cities라는 빈 배열을 만들어두고 이제 여기에 데이터를 가져와 넣을 것이다.

const cities = [];

fetch(endpoint)
	.then((blob) => blob.json())
	.then((data) => cities.push(data)); 

이렇게 하면 될 것 같지만 문제가 생긴다. 데이터가 nested 되어 들어와서 불필요한 층이 생겨버린다. 이렇게 cities 배열에 데이터들이 들어올 때 데이터의 요소들이 하나 하나 들어오는 게 아니라 통째로 들어와버리기 때문이다.

이런 불필요한 nesting을 막으려면 스프레드 문법을 사용하면 된다. 스프레드 문법을 사용하면 묶여있던 배열을 개별적인 요소로 펼쳐져 우리가 원하던 것처럼 배열의 요소들이 cities에 하나하나 잘 들어간다.

const cities = [];

fetch(endpoint)
	.then((blob) => blob.json())
	.then((data) => cities.push(...data)); 

개발자 도구에서 cities 배열을 열어보면 nesting 없이 원하는 배열이 바로 나타난다.

정규표현식

사용자의 입력값과 일치하는 값이 cities 데이터에 있는지 찾아보고자 할 때 나는 includes 메소드를 사용해서 찾고자 했는데 영상에서는 정규표현식을 만들어 match 메소드를 사용하는 방식으로 일치하는 글자를 찾아냈다.

정규표현식을 생성하는 방법에는 두 가지가 있는데 두 가지 모두 이번 과제에 사용이 되었다. 우선 첫 번째 방법은 new 키워드를 통한 생성자 방식. 첫 번째 인수로는 '표현의 패턴', 그리고 두 번째 인수로는 '패턴을 어떠한 방식으로 검색할 것인지에 대한 옵션'을 받는다.

new RegExp('표현', '옵션')
function findMatches(wordToMatch, cities) {
  return cities.filter((place) => {
    const regex = new RegExp(wordToMatch, "gi"); // 생성자 방식
    return place.city.match(regex) || place.state.match(regex);
  });
}

두 번째 방식은 리터럴 방식이다. 생성자 방식보다 더 간단하게 /를 사용해서 만들 수 있다. 마침 이 방식도 과제에서 인구수를 쉼표 형식의 숫자로 나타내도록 만드는 함수에 사용되었다.

/표현/옵션
function numberWithCommas(x) {
        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
      }

정규표현식을 공부해본 적이 없어 너무 낯설었지만 이참에 어떻게 사용되는지 간단하게나마 공부했다. 이 글에는 만드는 방식 정도만 기록해두었고 이 영상을 보면서 연습을 좀 해봤다. 앞으로 문자형을 다루어야 할 때 요긴하게 쓰일 듯.

Array.prototype.join()

join() 메소드는 배열의 모든 요소를 연결해 하나의 문자열로 만든다.

const elements = ['Fire', 'Air', 'Water'];

console.log(elements.join());
// Expected output: "Fire,Air,Water"

console.log(elements.join(''));
// Expected output: "FireAirWater"

console.log(elements.join('-'));
// Expected output: "Fire-Air-Water"

아규먼트로 배열의 각 요소를 구분할 문자열을 지정하면 위의 예시처럼 배열 사이사이마다 지정해준 문자열이 들어가 원하는 형식으로 나타낼 수 있다. ''를 쓰면 단순히 붙여버린다.

오늘 과제에서는 이렇게 suggestion 리스트 각각의 요소들이 부자연스럽게 ,로 나뉘어져 버리는 문제를 해결하기 위해 .join()메소드가 사용되었다.업로드중..

function displayMatches() {
  const matchArray = findMatches(this.value, cities);
  const html = matchArray
    .map((place) => {
      return `
      <li>
        <span class="name">${place.city}, ${place.state}</span>
        <span class="population">${place.population}</span>
      </li>
    `;
    })
    .join(""); // Array.prototype.join()
  suggestions.innerHTML = html;
}

잘 해결되었다.
업로드중..

String.prototype.replace()

function numberWithCommas(x) {
 return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

숫자를 콤마 형식으로 변환해주는 함수다. 예컨대 123456이라는 숫자를 123,456의 형식으로 변환해준다.

replace() 메소드가 사용되었는데 이 메소드는 문자열에서 특정 문자열을 치환하는 메소드다. 아규먼트의 첫 번째 값에는 바꿀 대상이 되는 패턴이 들어가는데 일반 문자열 또는 정규표현식이 들어갈 수 있다. 아규먼트의 두 번째 값에는 새롭게 바꿀 문자열이나 함수가 들어갈 수 있다. 아래 예시 참고.

const paragraph = "I think Ruth's dog is cuter than your dog!";

console.log(paragraph.replace("Ruth's", 'my'));
// Expected output: "I think my dog is cuter than your dog!"

const regex = /Dog/i;
console.log(paragraph.replace(regex, 'ferret'));
// Expected output: "I think Ruth's ferret is cuter than your dog!"

무엇을 느꼈나.

  • 좀 더 능동적으로 학습하려고 노력했다. 영상을 보기 전 스스로 먼저 코드를 짜보고 나중에 내가 짠 코드와 영상 속 코드를 비교해보았다. 스스로 피드백을 해보려 했는데 아무래도 한계가 있다. 이 부분에 대해 누군가에게 피드백을 받을 수 있으면 참 좋겠다.
  • 더 나아가서 한 번 더 배운 내용을 바탕으로 스스로 다시 구현해보는 연습을 하는 것도 효율적인 학습 방법이 될 것 같다.

참고

profile
日新又日新 ☀️ 😎

0개의 댓글