pages/Main.js
pages/component/Serch.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>
);
}
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;
//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}
/>
);
}
);
}
부모컴포넌트인 Main에서 onSearch로 search함수를 전달했다.(내려줬다)
이 search함수는 주어진 검색 키워드에 따라 condition 상태를 변경한다.
자식컴포넌트인 Search에서 onSearch 이벤트를 받는다.
검색버튼을 누르거나 엔터를 치면 onSearch함수에 인자를 넣어 실행한다.
이때 departure은 ICN으로 동일하게 적용되도록 제한되어있고,
destination은 input으로 입력받아서 textDestination이라는 변수(state)로 저장된다.
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>
);
}
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의 역할을 하기 때문에 쓰지 않는다.
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());
부수효과. 나는 정처기 공부하면서 의도하지 않은 부작용을 sied effect라고 배워서 매우 헷깔렸다.
리액트에서는 부작용으로서가 아닌 부수적인 효과로서 이것을 다루는 것 같다.
fetch를 하는 것, DOM에 변화를 주는 것도 side Effect에 해당한다.
useEffect(함수, [종속성]) 이런식으로 다루는데,
종속성배열은 모니터링 대상이라고 생각하면 될 것 같다.
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 상태가 변할 때마다 실행시켜줌.