과제 :: States Airline-client

KoEunseo·2022년 8월 10일
0

코드스테이츠

목록 보기
14/45

part1

구조 살펴보기

pages/Main.js
pages/component/Serch.js

Main.js

import { useEffect, useState } from 'react';
import FlightList from './component/FlightList';
import Search from './component/Search';
import json from '../resource/flightList'; //하드코딩된 data를 json이라는 이름으로 가져옴

export default function Main() {
  // 항공편 검색 조건을 담고 있는 상태
  const [condition, setCondition] = useState({
    departure: 'ICN',
  });
  const [flightList, setFlightList] = useState(json);

  // 주어진 검색 키워드에 따라 condition 상태를 변경
  const search = ({ departure, destination }) => {
    if (
      condition.departure !== departure ||
      condition.destination !== destination
    ) {
      //search 함수가 전달 받아온 '항공편 검색 조건' 인자를 condition에 담는다.
      setCondition({departure, destination});
    }
  };

  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;
  };
  return (
    <div>
      <Head>
        <title>States Airline</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1>여행가고 싶을 땐, States Airline</h1>
        <Search onSearch={search}/> //search 함수를 Search 컴포넌트로 내려준다.
        <div className="table">
          <div className="row-header">
            //...생략
          </div>
          <FlightList list={flightList.filter(filterByCondition)} />
        </div>
      </main>
    </div>
  );
}

Search.js

import { useState } from 'react';

function Search({ onSearch }) {
  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 = () => { //onSearch 이벤트를 받아옴
    console.log('검색 버튼을 누르거나, 엔터를 치면 search 함수가 실행됩니다');

    //상위 컴포넌트에서 props를 받아서, search함수 실행
    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;

data

//flight 컴포넌트의 형태(모양)정의.
function Flight({ departure, destination, departureTimes, arrivalTimes }) {
  return (
    <div className="row">
      <div className="col">🛫 {departure}</div>
      <div className="col">🛬 {destination}</div>
      <div className="col">{departureTimes}</div>
      <div className="col">{arrivalTimes}</div>
      <div className="col">
        <button>예약하기</button>
      </div>
    </div>
  );
}

export default Flight;

//flight 컴포넌트에 flightList를 map으로 전달함
import Flight from './Flight';

function FlightList({ list = [] }) {
  if (list.length === 0) {
    return <div className="merge-col">목록이 없습니다</div>;
  }

  return list.map(
    ({ uuid, departure, destination, departure_times, arrival_times }) => {
      return (
        <Flight
          key={uuid}
          departure={departure}
          destination={destination}
          departureTimes={departure_times}
          arrivalTimes={arrival_times}
        />
      );
    }
  );
}

React의 데이터 흐름 :: state 전달하기

부모컴포넌트인 Main에서 onSearch로 search함수를 전달했다.(내려줬다)
이 search함수는 주어진 검색 키워드에 따라 condition 상태를 변경한다.

상태 끌어올리기 :: state 변경하기

자식컴포넌트인 Search에서 onSearch 이벤트를 받는다.
검색버튼을 누르거나 엔터를 치면 onSearch함수에 인자를 넣어 실행한다.
이때 departure은 ICN으로 동일하게 적용되도록 제한되어있고,
destination은 input으로 입력받아서 textDestination이라는 변수(state)로 저장된다.

part2

Main.js

import { useEffect, useState } from 'react';
import { getFlight } from '../api/FlightDataApi';
import FlightList from './component/FlightList';
import LoadingIndicator from './component/LoadingIndicator';
import Search from './component/Search';

export default function Main() {
  // 항공편 검색 조건을 담고 있는 상태
  const [condition, setCondition] = useState({
    departure: 'ICN'
  });
  const [flightList, setFlightList] = useState([]);
  const [isLoading, setIsLoading] = useState(false); 
  //로딩을 구현하기 위한 useState 초기값으로 true를 줘야할까 false를 줘야할까?

  const search = ({ departure, destination }) => {
    //생략: 주어진 검색 키워드에 따라 condition 상태를 변경시켜주는 함수
  };

  //Effeck Hook을 이용해 AJAX 요청
  //네트워크 요청이 진행됨을 보여주는 로딩 컴포넌트(<LoadingIndicator/>)를 제공
  useEffect(() => { //데이터가 오면 실행될 수 있게끔 비동기적으로 실행한다.
    setIsLoading(true); //데이터가 오기 전 먼저 로딩을 시켜준다.
    getFlight(condition)
      .then(res => {
        setFlightList(res); //res를 받아와 곧바로 flightList에 업데이트한다.
        setIsLoading(false); //작업이 끝났기 때문에 로딩상태를 false로 돌려준다.
      });
  }, [condition])// condition 상태가 변할 때마다 실행시켜줌.

  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를 나타낸다.
            //FightList 내에서 자체적으로 map을 사용하고 있기 때문에 props를 전달만 하면 되지 않을까? 
            //페어랑 할때는 map으로 전달했는데 테스트는 통과했다.
            isLoading ?
              <LoadingIndicator /> :
              <FlightList list={flightList} />
          }
        </div>
      </main>
    </div>
  );
}

FlightDataApi.js

let aa = new Promise(resolve => resolve("hi"))
let bb = aa.then(data => {console.log(data}; return "bye")
Promise 내 hi에 접근하는 방법: .then()
import flightList from '../resource/flightList';
import fetch from 'node-fetch';

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

export function getFlight(filterBy = {}) { 
  //filterBy로 들어오는 값은 condition이었다. 근데 처음 마운트될때는 destination값이 없음.
  //{departure: 'ICN'} 이런식이다.
  //filterBy자리에 아무것도 들어오지 않았을때 초기값(기본값)을 주는 것이다.
  
  const url = 'http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight';
//주어진 url에서 flight을 GET 하기 위해 엔드포인트에 /flight을 추가했다.
  let param = filterBy.destination ? //삼항연산자:filterBy값에 destination이 있으면?
    `${url}?departure=${filterBy.departure}&destination=${filterBy.destination}` //같이 필터링해준다.
    : `${url}?departure=${filterBy.departure}` 
  //destination이 없다면? departure만 넣어서 모든 비행정보가 나오도록 한다.
  //fetch 사용해 REST API 호출: 비동기로 호출한다.  
  const res = fetch(param);
  return res.json(); //리턴에 await 유무여부에 따라 뭔가 달라질까??
}
  const res = await fetch(param);
  return await res.json();

then이 async await의 역할을 하기 때문에 쓰지 않는다.

FlightDataApi.js 코드스테이츠 풀이

export function getFlight(filterBy = {}) {
  let queryString = '';
  if (filterBy.departure) {
    queryString = queryString + `departure=${filterBy.departure}&`;
  }
  if (filterBy.destination) {
    queryString = queryString + `destination=${filterBy.destination}`;
  }

  let endpoint = `http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight?${queryString}`;

  return fetch(endpoint).then((resp) => resp.json());

side Effect

부수효과. 나는 정처기 공부하면서 의도하지 않은 부작용을 sied effect라고 배워서 매우 헷깔렸다.
리액트에서는 부작용으로서가 아닌 부수적인 효과로서 이것을 다루는 것 같다.
fetch를 하는 것, DOM에 변화를 주는 것도 side Effect에 해당한다.
useEffect(함수, [종속성]) 이런식으로 다루는데,
종속성배열은 모니터링 대상이라고 생각하면 될 것 같다.

Effect Hook :: 비동기 호출, Ajax요청

 useEffect(() => { //데이터가 오면 실행될 수 있게끔 비동기적으로 실행한다.
    getFlight(condition)
      .then(res => {
        setFlightList(res); //res를 받아와 곧바로 flightList에 업데이트한다.
      });
  }, [condition])// condition 상태가 변할 때마다 실행시켜줌.

로딩화면 구현

const [isLoading, setIsLoading] = useState(false); //로딩 상태처리. 초기값을 뭘 주든지 true/false 상관 없다.
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}

 useEffect(() => { //데이터가 오면 실행될 수 있게끔 비동기적으로 실행한다.
    setIsLoading(true);
    getFlight(condition)
      .then(res => {
        setFlightList(res); //res를 받아와 곧바로 flightList에 업데이트한다.
        setIsLoading(false);
      });
  }, [condition])// condition 상태가 변할 때마다 실행시켜줌.
profile
주니어 플러터 개발자의 고군분투기

0개의 댓글