StatesAirline Client 과제

이성은·2022년 12월 7일
0
post-custom-banner

학습목표

  • React의 데이터 흐름에 대해 이해하고 state를 전달할 수 있다.
  • 상태 끌어올리기를 활용하여, 원하는 컴포넌트에서 state를 변경할 수 있다.
  • Side Effect의 개념에 대해서 이해할 수 있다.
  • Effect Hook을 활용하여 비동기 호출 및 Ajax 요청과 같은 side effect를 처리할 수 있다.
  • 네트워크 요청이 느릴 경우에 표시되는 로딩 화면을 구현할 수 있다.

StatesAirline Client- part 1

Part 1: 항공권 목록 필터링
🧩 Main 컴포넌트에서 항공편을 조회합니다
✓ Main 컴포넌트 내 search 함수는 검색 조건을 담고 있는 상태 객체 condition을 업데이트해야 합니다
🧩 Search 컴포넌트를 통해 상태 끌어올리기를 학습합니다
✓ 검색 화면이 Search 컴포넌트로 분리되어야 합니다
✓ Search 컴포넌트에는 상태 변경 함수 searchonSearch props로 전달되어야 합니다
✓ 상태 변경 함수 search는 Search 컴포넌트의 검색 버튼 클릭 시 실행되어야 합니다

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

import Head from "next/head";
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";
import Debug from "./component/Debug";
import json from "../resource/flightList";

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

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

  global.search = search; // 테스트를 위해 필요한 코드

  // 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)} />
        </div>

        <div className="debug-area">
          <Debug condition={condition} />
        </div>
        <img id="logo" alt="logo" src="codestates-logo.png" />
      </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();
    }
  };

	// 상위 컴포넌트에서 props를 받아서 실행
  const handleSearchClick = () => {
    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;

StatesAirline Client- part 2

Part 2: AJAX 요청
🧩 Side Effect는 useEffect에서 다뤄야 합니다
✓ 검색 조건이 바뀔 때마다, FlightDataApi의 getFlight를 검색 조건과 함께 요청해야 합니다
✓ getFlight의 결과를 받아, flightList 상태를 업데이트해야 합니다
✓ 더이상, 컴포넌트 내 필터 함수 filterByCondition를 사용하지 않습니다
✓ 더이상, 하드코딩된 flightList JSON을 사용하지 않습니다 (초기값은 빈 배열로 둡니다)
✓ getFlight 요청이 다소 느리므로, 로딩 상태에 따라 LoadingIndicator 컴포넌트를 표시해야 합니다
🧩 FlightDataApi에서 기존 구현 대신, REST API를 호출하도록 바꿉니다
✓ 검색 조건과 함께 StatesAirline 서버에서 항공편 정보를 요청(fetch)합니다

< Main.js >

import Head from "next/head";
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";
import Debug from "./component/Debug";
// 후반 테스트를 진행할 때 아래 import를 삭제합니다.

export default function Main() {
 // 항공편 검색 조건을 담고 있는 상태
 const [condition, setCondition] = useState({
   departure: "ICN",
 });
 const [flightList, setFlightList] = useState([]);//초기값을 빈배열로
 const [isLoading, setIsLoading] = useState(true);
 // 로딩상태일때를 구현 , 상태를 만들어준다

 // 주어진 검색 키워드에 따라 condition 상태를 변경시켜주는 함수
 const search = ({ departure, destination }) => {
   if (
     condition.departure !== departure ||
     condition.destination !== destination
   ) {
     console.log("condition 상태를 변경시킵니다");

     // TODO: 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;
 };

 global.search = search; // 실행에는 전혀 지장이 없지만, 테스트를 위해 필요한 코드입니다. 이 코드는 지우지 마세요!

 // TODO: Effeck Hook을 이용해 AJAX 요청을 보내보세요.
 // TODO: 더불어, 네트워크 요청이 진행됨을 보여주는 로딩 컴포넌트(<LoadingIndicator/>)를 제공해보세요.
 useEffect(() => {
   setIsLoading(true);
   getFlight(condition) // local서버가 아닌 진짜서버에서 데이터를 받아온다
     .then((filtered) => {
       setFlightList(filtered);
       setIsLoading(false);
     });
 }, [condition]);

 // TODO: 테스트 케이스의 지시에 따라 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>
       <img id="logo" alt="logo" src="codestates-logo.png" />
     </main>
   </div>
 );
         }

< 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());
}

과제후 느낌점

이전에 했던 React진도를 나름 이해하고 넘어갔다고 생각했는데..
아직도 state와 props개념이 많이 모자른 것 같아 추가공부를 해야겠다.
문제를 풀때 관련된 변수를 좀더 꼼꼼히 보는 습관을 가져야겠다.

profile
함께 일하는 프론트엔드 개발자 이성은입니다🐥
post-custom-banner

0개의 댓글