코드스테이츠 8주차 / StatesAirline Client

support·2021년 12월 7일
5
post-thumbnail

검색 조건을 변경하면, 해당 검색 조건에 맞는 항공권이 필터링되어 목록에 표시되게 만듭니다.


Main.js # 첫화면 컴포넌트, 필터링 상태를 담고 있습니다.
Search.js # 검색 도구 컴포넌트, 필터링 상태를 변경합니다.
FlightDataApi.js # 항공편 정보를 받아오는 API
세군데를 수정해주면 된다

Part 1: 항공권 목록 필터링

Main.js

메인 (부모컴포넌트)에 있는 search함수가
search 컴포넌트 (자식컴포넌트)의 검색 버튼 클릭시 실행되어야 한다 - 상태끌어올리기를 사용하자

export default function Main() {
  const [condition, setCondition] = useState({
    departure: 'ICN'
  })
  const [flightList, setFlightList] = useState(json)
  const [isloading, setloading] = useState(false)

  const search = ({ departure, destination }) => {
    if (condition.departure !== departure || condition.destination !== destination) {
      console.log('condition 상태를 변경시킵니다')

      setCondition({departure, destination}) 
      // 1 상태변경 함수인 serach에 condition변경 함수인 setCondition을 이용해 값을 바꿔준다
    }
  }

  useEffect(async() =>{
    setloading(true)
    setFlightList(await getFlight(condition))
    setloading(false)
  }, [condition])


  const filterByCondition = (flight) => {
    let pass = true;
    if (condition.departure) {
      pass = pass && flight.departure === condition.departure
    }
    if (condition.destination) {
      pass = pass && flight.destination === condition.destination
    }
    return pass;
  }

  global.search = search 

  return (
    <div>
      <Head>
        <title>States Airline</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1>
          여행가고 싶을 땐, States Airline
        </h1>
        <Search onSearch={search}/> 
// 2 Search 컴포넌트에는 상태 변경 함수 `search`가 `onSearch` props로 전달되어야 한다
        <div className="table">
          <div className="row-header">
            <div className="col">출발</div>
            <div className="col">도착</div>
            <div className="col">출발 시각</div>
            <div className="col">도착 시각</div>
            <div className="col"></div>
          </div>
          {/* <FlightList list={flightList.filter(filterByCondition)} /> */}
          {isloading
          ? <LoadingIndicator />
          : <FlightList list ={flightList} />
          }
        </div>

        <div className="debug-area">
          <Debug condition={condition} />
        </div>
      </main>
    </div>
  )
}

Search.js

import { useState } from 'react'

function Search({onSearch}) { 
// 3 onSearch props를 받아온다
  const [textDestination, setTextDestination] = useState('')

  const handleChange = (e) => {
    setTextDestination(e.target.value.toUpperCase())
  }

  const handleKeyPress = (e) => {
    if (e.type === 'keypress' && e.code === 'Enter') {
      handleSearchClick()
    }
  }

  const handleSearchClick = () => {
    console.log('검색 버튼을 누르거나, 엔터를 치면 search 함수가 실행됩니다')

  // 4 상태 변경 함수 `search`는 Search 컴포넌트의 `검색` 버튼 클릭 시 실행되어야 합니다 
  // 검색 버튼을 클릭했을때 onChange는 handleChange값이 변경된다고 아래에 적혀있다
  // handleChange는 도착지 값이 변경되는 것을 알려주고 있고 
  // 출발지는 고정하고 도착지의 값을 textDestination으로 변경해주면 된다 
  // 서치 함수에서 setCondition({departure, destination}) 객체형태로 값을 받아오는걸 알 수 있으니
  // 똑같이 값을 넣어줘야 한다
    onSearch({departure : "ICN", destination : textDestination})
  }

  return <fieldset>
    <legend>공항 코드를 입력하고, 검색하세요</legend>
    <span>출발지</span>
    <input id="input-departure" type="text" disabled value="ICN"></input>
    <span>도착지</span>
    <input id="input-destination" type="text" value={textDestination} onChange={handleChange} placeholder="CJU, BKK, PUS 중 하나를 입력하세요" onKeyPress={handleKeyPress} />
    <button id="search-btn" onClick={handleSearchClick}>검색</button>
  </fieldset>
}

export default Search

Part 2: AJAX 요청

FlightDataApi.js

import flightList from '../resource/flightList'
import fetch from 'node-fetch'

if (typeof window !== "undefined") {
  localStorage.setItem('flight', JSON.stringify(flightList));
}

export function getFlight(filterBy = {}) {
  // HINT: 가장 마지막 테스트를 통과하기 위해, fetch를 이용합니다. 아래 구현은 완전히 삭제되어도 상관없습니다.
  // TODO: 아래 구현을 REST API 호출로 대체하세요.

   let json = []
  if (typeof window !== "undefined") {
    json = localStorage.getItem("flight");
  }
  const flight = JSON.parse(json) || [];




  return new Promise((resolve) => {
    const filtered = flight.filter((flight) => {
      let condition = true;
      if (filterBy.departure) {
        condition = condition && flight.departure === filterBy.departure
      }
      if (filterBy.destination) {
        condition = condition && flight.destination === filterBy.destination
      }
      return condition;
    })

    setTimeout(() => {
      resolve(filtered)
    }, 500);
  });
}

Main.js


export default function Main() {
  const [condition, setCondition] = useState({
    departure: 'ICN'
  })
  const [flightList, setFlightList] = useState(json)
  const [isloading, setloading] = useState(true)
  // 로딩상태일때를 구현 , 상태를 만들어준다

  const search = ({ departure, destination }) => {
    if (condition.departure !== departure || condition.destination !== destination) {
      console.log('condition 상태를 변경시킵니다')

      // TODO:
      setCondition({departure, destination})
    }
  }

// useEffect로 상태를 변화하는 condition을 FlightDataApi의 getflight로 넘겨준다
  
  useEffect(async() =>{
    setloading(true)
    setFlightList(await getFlight(condition))
    // 필터링 조건인 객체를 인자로 넣어준다 
    setloading(false)
  }, [condition])
  // 검색 조건이 바뀔 때 마다 useEffect 실행
  // useEffect에서 어떤 값이 바뀔때만 호출하고싶다면 배열 안에 
  // useEffect로 관리하고 있는 상태인 condition을 넣어준다

 useEffect 다른풀이
 
 useEffect(() =>{
	setloading(true)
 	getFlight(condition) // local서버가 아닌 진짜서버에서 데이터를 받아온다
   .then(filtered =>{
     setFlightList(filtered)
     setloading(false)
    })
 }, [condition])
  
  
  global.search = search // 실행에는 전혀 지장이 없지만, 테스트를 위해 필요한 코드입니다. 이 코드는 지우지 마세요!

  return (
    <div>
      <Head>
        <title>States Airline</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1>
          여행가고 싶을 땐, States Airline
        </h1>
        <Search onSearch={search}/>
        <div className="table">
          <div className="row-header">
            <div className="col">출발</div>
            <div className="col">도착</div>
            <div className="col">출발 시각</div>
            <div className="col">도착 시각</div>
            <div className="col"></div>
          </div>
          {/* <FlightList list={flightList.filter(filterByCondition)} /> */}
          // getFlight 요청이 다소 느리므로, 
          // 로딩 상태에 따라 LoadingIndicator 컴포넌트를 표시해야 합니다 
          // 컴포넌트 내 필터 함수 `filterByCondition` 대신 삼항연산자 사용 
          {isloading
          ? <LoadingIndicator />
          : <FlightList list ={flightList} />
          }
        </div>

        <div className="debug-area">
          <Debug condition={condition} />
        </div>
      </main>
    </div>
  )
}

REST API를 호출

FlightDataApi.js

FlightDataApi에서 기존 구현 대신, REST API를 호출하도록 바꿉니다

import flightList from '../resource/flightList'
import fetch from 'node-fetch'

if (typeof window !== "undefined") {
  localStorage.setItem('flight', JSON.stringify(flightList));
}

export function getFlight(filterBy = {}) {
  // HINT: 가장 마지막 테스트를 통과하기 위해, fetch를 이용합니다. 아래 구현은 완전히 삭제되어도 상관없습니다.
  // TODO: 아래 구현을 REST API 호출로 대체하세요.
  // fetch 를 사용해 다시 작성한다 

  let emString = ''
  if(filterBy.departure){
    emString = emString + `departure=${filterBy.departure}&`
  }
  if(filterBy.destination){
    emString = emString + `destination=${filterBy.destination}`
  }
  let endpoint = `http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight?${emString}`

  return fetch(endpoint)
    .then(res => res.json())

  // let json = []
  // if (typeof window !== "undefined") {
  //   json = localStorage.getItem("flight");
  // }
  // const flight = JSON.parse(json) || [];




  // return new Promise((resolve) => {
  //   const filtered = flight.filter((flight) => {
  //     let condition = true;
  //     if (filterBy.departure) {
  //       condition = condition && flight.departure === filterBy.departure
  //     }
  //     if (filterBy.destination) {
  //       condition = condition && flight.destination === filterBy.destination
  //     }
  //     return condition;
  //   })

  //   setTimeout(() => {
  //     resolve(filtered)
  //   }, 500);
  // });
}

0개의 댓글