02/03

nowayยท2023๋…„ 2์›” 3์ผ
0

๊ฐœ๋˜ฅ๋ฒŒ๋ ˆ

๋ชฉ๋ก ๋ณด๊ธฐ
4/7

๐Ÿ’ก Part 1: ํ•ญ๊ณต๊ถŒ ๋ชฉ๋ก ํ•„ํ„ฐ๋ง

๐Ÿงฉ Main ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•ญ๊ณตํŽธ์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค
โœ“ Main ์ปดํฌ๋„ŒํŠธ ๋‚ด search ํ•จ์ˆ˜๋Š” ๊ฒ€์ƒ‰ ์กฐ๊ฑด์„ ๋‹ด๊ณ  ์žˆ๋Š” ์ƒํƒœ ๊ฐ์ฒด condition์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

  > const [condition, setCondition] = useState({
departure: 'ICN', });

    const search = ({ departure, destination }) => {
if (
  condition.departure !== departure ||
  condition.destination !== destination
) {
  console.log('condition ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ต๋‹ˆ๋‹ค');
  setCondition({ departure, destination }) /* search๊ฐ€ ์‹คํ–‰๋ ๋•Œ setCondition์œผ๋กœ condition์—  departure, destination์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ */
}

};

๐Ÿงฉ Search ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ๋ฅผ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค
โœ“ ๊ฒ€์ƒ‰ ํ™”๋ฉด์ด Search ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
โœ“ Search ์ปดํฌ๋„ŒํŠธ์—๋Š” ์ƒํƒœ ๋ณ€๊ฒฝ ํ•จ์ˆ˜ search๊ฐ€ onSearch props๋กœ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

  > <main>
    <h1>์—ฌํ–‰๊ฐ€๊ณ  ์‹ถ์„ ๋•, States Airline</h1>
    <Search onSearch={search} />  /* Search ์ปดํฌ๋„ŒํŠธ๋กœ ํ™”๋ฉด์„ ํ‘œ์‹œํ•จ๊ณผ ๋™์‹œ์— 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>
    </div>

โœ“ ์ƒํƒœ ๋ณ€๊ฒฝ ํ•จ์ˆ˜ search๋Š” Search ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

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 = () => {
console.log('๊ฒ€์ƒ‰ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜, ์—”ํ„ฐ๋ฅผ ์น˜๋ฉด search ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค');
onSearch({departure: 'ICN', destination: textDestination}) / ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜, ์—”ํ„ฐ๋ฅผ ์น˜๋ฉด(handleKeyPress) Prop์—์„œ Onsearch๋ช…์œผ๋กœ ๋ฐ›์€ searchํ•จ์ˆ˜ ์‹คํ–‰ /
};

๐Ÿ’ก Part 2: AJAX ์š”์ฒญ
๐Ÿงฉ Side Effect๋Š” useEffect์—์„œ ๋‹ค๋ค„์•ผ ํ•ฉ๋‹ˆ๋‹ค
โœ“ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค, FlightDataApi์˜ getFlight๋ฅผ ๊ฒ€์ƒ‰ ์กฐ๊ฑด๊ณผ ํ•จ๊ป˜ ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
โœ“ getFlight์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„, flightList ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
โœ“ ๋”์ด์ƒ, ์ปดํฌ๋„ŒํŠธ ๋‚ด ํ•„ํ„ฐ ํ•จ์ˆ˜ filterByCondition๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
โœ“ ๋”์ด์ƒ, ํ•˜๋“œ์ฝ”๋”ฉ๋œ flightList JSON์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์ดˆ๊ธฐ๊ฐ’์€ ๋นˆ ๋ฐฐ์—ด๋กœ ๋‘ก๋‹ˆ๋‹ค)
โœ“ getFlight ์š”์ฒญ์ด ๋‹ค์†Œ ๋Š๋ฆฌ๋ฏ€๋กœ, ๋กœ๋”ฉ ์ƒํƒœ์— ๋”ฐ๋ผ LoadingIndicator ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

const [isLoading, setIsLoading] = useState(false); /* ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์œ„ํ•œ ๋ณ€์ˆ˜, ์ƒํƒœ๊ฐ€ true์ผ๊ฒฝ์šฐ ๋กœ๋”ฉํ™”๋ฉด ๋‚˜ํƒ€๋‚จ

useEffect(() =>{
setIsLoading(true) / getFlight ์‹คํ–‰์ „ ๋กœ๋”ฉ ํ™”๋ฉด ๋‚˜ํƒ€๋‚จ /
getFlight(condition) / getFlightํ•จ์ˆ˜์— condition๋ณ€์ˆ˜ ๋„ฃ์–ด์„œ ์‹คํ–‰ /
.then((filterlist) =>{
setFlightList(filterlist) / ์‹คํ–‰์— ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ setFlightList์— ์ „๋‹ฌ /
setIsLoading(false) / ๋กœ๋”ฉ ํ™”๋ฉด ์‚ฌ๋ผ์ง€๊ฒŒํ•จ /
})
}, [condition]) / condition ๊ฐ’์ด ๋ณ€๊ฒฝ๋ ๋•Œ๋งˆ๋‹ค ์‹คํ–‰ /


๐Ÿงฉ FlightDataApi์—์„œ ๊ธฐ์กด ๊ตฌํ˜„ ๋Œ€์‹ , REST API๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค
โœ“ ๊ฒ€์ƒ‰ ์กฐ๊ฑด๊ณผ ํ•จ๊ป˜ StatesAirline ์„œ๋ฒ„์—์„œ ํ•ญ๊ณตํŽธ ์ •๋ณด๋ฅผ ์š”์ฒญ(fetch)ํ•ฉ๋‹ˆ๋‹ค

getFlight(filterBy = {}) {
let nothing = '' / ๋ณ€์ˆ˜ nothing ์„ ์–ธ /
if(filterBy.departure){
nothing += departure={filterBy.departure}& /* ๋ณ€์ˆ˜ nothing์— departure๊ฐ’ ํ• ๋‹น */ } if(filterBy.destination){ nothing += destination={filterBy.destination} / ๋ณ€์ˆ˜ nothing์— destination๊ฐ’ ํ• ๋‹น /
}
let url = http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight?${nothing} / ๋ฐ›์•„์˜ฌ url์— ๋ณ€์ˆ˜ nothing๋„ฃ์–ด์„œ ์–ด๋–ค ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ์ง€ ํ• ๋‹น /

return fetch(url).then((res) => res.json()) / jsonํŒŒ์ผ๋กœ ๋ฐ›์•„์˜ด /
}

Main ์ปดํฌ๋„ŒํŠธ

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([]);
  

  // ์ฃผ์–ด์ง„ ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ์— ๋”ฐ๋ผ 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(() => {
  // }, [])
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() =>{
    setIsLoading(true)
    getFlight(condition)
     .then((filterlist) =>{  
       setFlightList(filterlist) 
       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>
        </div>
        {isLoading ? <LoadingIndicator /> : <FlightList list={flightList} />}
        <div className="debug-area">
          <Debug condition={condition} />
        </div>
        <img id="logo" alt="logo" src="codestates-logo.png" />
      </main>
    </div>
  );
}

Search ์ปดํฌ๋„ŒํŠธ

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 = () => {
console.log('๊ฒ€์ƒ‰ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜, ์—”ํ„ฐ๋ฅผ ์น˜๋ฉด search ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค');
onSearch({departure: 'ICN', destination: textDestination})

// TODO: ์ง€์‹œ์— ๋”ฐ๋ผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ props๋ฅผ ๋ฐ›์•„์„œ ์‹คํ–‰์‹œ์ผœ ๋ณด์„ธ์š”.

};

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;

FlightDataApi ์ปดํฌ๋„ŒํŠธ

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 nothing = ''
if(filterBy.departure){
nothing += departure=${filterBy.departure}&
}
if(filterBy.destination){
nothing += destination=${filterBy.destination}
}
let url = http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight?${nothing}

return fetch(url).then((res) => res.json())
}

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด