* 상태 끌어올리기, 데이터 흐름 개념을 활용하여 항공편 검색 기능을 구현한다. - React의 데이터 흐름에 대해 이해하고 state를 전달할 수 있다. - 상태 끌어올리기를 활용하여, 원하는 컴포넌트에서 state를 변경할 수 있다. - 검색 조건을 변경하면, 해당 검색 조건에 맞는 항공권이 필터링되어 목록에 표시되게 한다.
디렉토리 구조
.
├── README.md
├── __tests__
│ └── index.test.js # 테스트 파일
├── api
│ └── FlightDataApi.js # 항공편 정보를 받아오는 API
├── package.json
├── pages
│ ├── Main.js # 첫 화면 컴포넌트, 필터링 상태를 담고 있다.
│ ├── component
│ ├── Debug.js # 디버그용 컴포넌트 (테스트 통과에 필요)
│ ├── Flight.js # 단일 항공편
│ ├── FlightList.js # 항공편 목록
│ ├── LoadingIndicator.js # 로딩 컴포넌트
│ └── Search.js # 검색 도구 컴포넌트, 필터링 상태를 변경한다.
├── public
├── resource
│ └── flightList.js # 하드코딩된 항공편 정보
└── styles
└── globals.css # 스타일 시트
상태 끌어올리기
상태 관리, 상태 끌어올리기
Search 컴포넌트는 필터링 조건을 업데이트하는 액션을 보낸다.
React의 데이터 흐름 방식에 따라 "필터링 조건(condition)"이라는 상태는, Main 컴포넌트에 위치하며, 이에 따라 FlightList에 영향을 준다.
이 흐름의 전반이 바로 상태 끌어올리기이다.
Search에 존재하는 버튼 클릭이 Main의 상태 변화를 만든다.
Main의 상태 변화는 FlightList의 목록을 업데이트한다.
// ** 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를 삭제합니다.
import json from '../resource/flightList';
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
) {
// console.log('condition 상태를 변경시킨다');
setCondition({
departure: departure, // 🟣 (설명1)
destination: destination
})
// search 함수가 전달 받아온 '항공편 검색 조건' 인자를 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;
};
// search 함수를 onSearch라는 이름으로 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>
</main>
</div>
);
}
// 같은 표현 방식이다. 익숙해지자!
// 1)
const {
target: { value },
} = event;
// 2)
const value = event.target.value
// ** Search.js
import { useState } from 'react';
function Search({onSearch}) {
const [textDestination, setTextDestination] = useState('');
const handleChange = (e) => {
setTextDestination(e.target.value.toUpperCase());
};
const handleKeyDown = (e) => {
// 🟣 (설명1) 검색 버튼을 누르거나, 엔터를 치면 search 함수가 실행
if (e.type === 'keydown' && e.code === 'Enter') {
handleSearchClick();
}
};
const handleSearchClick = () => {
// console.log('검색 버튼을 누르거나, 엔터를 치면 search 함수가 실행');
// 상위 컴포넌트에서 props를 받아서 실행
onSearch({departure: 'ICN', destination: textDestination}) // **
};
return (
<fieldset>
<legend>공항 코드를 입력하고, 검색하세요! </legend>
<span>출발지</span>
<input id='input-departure' type='text' value='ICN'></input>
<span>도착지</span>
<input
id='input-destination'
type='text'
value={textDestination}
onChange={handleChange}
placeholder='CJU, BKK, PUS 중 하나를 입력하세요'
onKeyDown={handleKeyDown}
/>
<button id='search-btn' onClick={handleSearchClick}>
검색
</button>
</fieldset>
);
}
export default Search;
🟣 (설명1)
[e.type과 e.code]
e.type은 이벤트의 타입을 나타내는 문자열 속성이다. 이벤트 객체 e에는 이벤트에 대한 정보가 담겨있는데, 이 정보 중 type 속성은 이벤트의 타입을 나타내며, code 속성은 눌린 키의 코드를 나타낸다.
- 위 코드에서 handleKeyDown 함수는 keydown 이벤트가 발생했을 때 실행되므로, e.type은 항상 'keydown'이다. (keydown 이벤트는 키보드에서 어떤 키가 눌렸을 때 발생) - 만약 e.type === 'keydown'만 있으면 키보드가 눌릴 때마다 검색 결과가 나오며 마지막 키가 눌리기 전의 결과만 확인할 수 있다. ('ICN'을 눌렀다면 'IC'까지의 결과만, 그 뒤에 다른 키보드를 한 번 더 눌러야만 'ICN'의 결과를 볼 수 있음) - 따라서 위 코드에서 if 문은 엔터 키('Enter')가 눌렸을 때 handleSearchClick() 함수를 실행하는 역할을 한다.