REST API와 시각화
식품검색을 통해서 구성하는 영양소를 보여주고 시각화 한다 이때까지 공부한것으로 리액트를 이용해서 rest api로 요청을 전송및 수신을 하고 받은 데이터를 시각화해서 보여줘야한다
필요한 기능
- Recharts등 라이브러리를 이용해서 view보여주기
- REST API를 통해서 외부데이터 가져오기
- State를 통해서 변경되는 데이터를 다음 컴포넌트의 프로퍼티로 이용하기
웹앱의 기능
- 검색을 통해서 원하는 데이터를 가져오기
- 데이터를 받아오기전까지 검색중입니다 출력
- 어디서든 검색창을 이용해서 재검색을 가능하게한다
- 받아온 데이터를 출력하기
- LInk와Route를 적절하게 이용해서 화면 구성을 바꾸기
- 상세보기 페이지를 따로 구현(이번에 보여준 데이터 버튼은 보여주지 않아야한다)
검색을 통해서 데이터를 받아오기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//app.js
import logo from './logo.svg';
import Input from './components/Input'
import Search from "./components/Search"
import React,{ useState } from 'react';
import { BrowserRouter} from 'react-router-dom';
import './App.css';
function App() {
const [search, setSearch] = useState("");//search는 입력이 바뀌면 계속 바뀜
const onChangeSearch = (e) => {
setSearch(e.target.value)
}
const [input, setInput] = useState("");
const onChangeInput = (e) => {
setInput(e)
}
console.log('검색어',input);//input에는 검색 버튼을 눌렀을때 입력한 값
function inputCheck(input) {
if(input.length > 0){
return <Search name={input} />
} else{
return <h1>입력된 값이 없습니다</h1>
}
}
return (
<BrowserRouter>
<h1>음식 성분 분석</h1>
<input type="text" value={search} size="30" onChange={onChangeSearch} />
<Input text={search} changeBtn={onChangeInput}/>
{inputCheck(input)}
</BrowserRouter>
);
}
export default App;
app.js 페이지에서
<input type="text" value={search} size="30" onChange={onChangeSearch} />를 통해서 스테이트 search의 값을 글자가 변경될 때마다 재실행이되어서 값을 고정시켜서 다음 컴포넌트에 전달해줘야한다
글자하니씩 입력될때마다 값이 바뀌어서 state input을 추가해서 내가 원하는 입력 값을 저장할 수 있도록 했다
1
2
3
4
5
6
7
8
9
10
11
// Input.js 컴포넌트
import React,{ useState } from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function Input(props) {
const btn = () => {
props.changeBtn(props.text);
}
return <button onClick={btn}><Link to="/">입력</Link></button>
}
export default Input;
버튼을 통해서 app에 있는 state를 변경할 수 있다(함수를 넘겨줘서 함수를 다른 컴포넌트에서 실행)onClick={btn}받은 프로퍼티는 함수라 함수가 실행된다
REST API 사용
이제 입력값을 받았으니 이 값을 검색해야한다 fetch를 이용해서 데이터를 받아왔다 inputCheck(input)함수를 실행해서 조건에 따라 아래에 보여줄 컴포넌트를 결정한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//Search.js 컴포넌트
import React,{ useState } from 'react';
import List from "./List"
import { BrowserRouter, Routes, Route, Link, Router} from 'react-router-dom';
//이부분에서 값을 다음 컴포넌트로 전달해야하는데
//비동기적 작업이 실행되어 값을 받기전에 다음 컴포넌트로 넘어가서 null이 넘어간다
//따라서 fetch가 다 실행되고 나서 다음 컴포넌트를 실행해야한다
function FetchList(name) {
let url = "http://openapi.foodsafetykorea.go.kr/api/인증키/I2790/json/1/7/DESC_KOR="+name;
console.log(url);
return fetch(url).then(function(response) {
return response.json();
});
}
function Search(props) {
const [target,setTarget] = useState(props.name); //재검색을 위한 state
const [reload, setReload] = useState(false);//무한 api통신 방지 state
const [data, setData] = useState("");//데이터 추출용
if (target != props.name){//재검색을 위한 state
setTarget(props.name);
setReload(false);
}
async function GetList(name) {
try{
let foodObj = await FetchList(name);
console.log(foodObj);
setReload(true);
setData(foodObj.I2790);
} catch(err){
alert("에러발생");
}
}
if (reload == false){
GetList(props.name);
}
//리턴값을 보여주는 함수
function reloadCheck(reload) {
if(reload === false){
return <h2>검색중입니다...</h2>
} else {
return (<List data={data} name={target}/>)
}
}
return (<>{reloadCheck(reload)}</>)
}
export default Search;
이 부분에서 그냥 값을 받아오고 그 값을 파라미터로 이용해서 다음 컴포넌트에서 보여줄려고하면 데이터에선 null이나 undefined로 값이 제대로 나오지 않는데 이 이유는 fetch가 비동기 작업이기 때문에 값을 받아오기 전에 다음 컴포넌트가 실행되어서 그렇다
해결하기 위해서 state를 사용했다 reloadCheck(reload)함수의 내용기 reload 스테이트의 값에 따라 다른 리턴을 주도록했다 문제의 조건에서 데이터를 받아오기전에 검색중입니다를 출력할 수 있도록 하고
데이터를 받아오면 setReload(true);로 다시 값이 변경되어 재랜더링이 실행되고 리턴 값이 변경된다 state 추가 설명 {:targe=’_blank’}
재검색 기능
재검색 기능은 Search.js에서 state를 추가해서 구현했다
target과
1
2
3
4
if (target != props.name){//재검색을 위한 state
setTarget(props.name);
setReload(false);
}
조건문을 이용해서 현재가지고 있는 값과 다시들어온 props.name을 비교해서 다르다면 위 무한 api를 막기위해 사용되었던 reload를 false로 바꿔주고 target도 바꿔준다 그러면서 재랜더링이 되고 변경된 값으로 데이터를 받아온다
state는 컴포넌트가 다시 실행되더라도 가진 값을 유지한다(배열의 두번째 함수로 변경하기 전까지)
받아온 데이터 보여주기
받아온 데이터는 객체형태이다 그래서 map함수를 이용해서 리스트를 보여줬다
데이터를 받으면 Object로 받는다
식품정보 API 이용예시 여기서 해당 값이 뭘 의미하는지 알 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//List.js
import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import Detail from './Detail';
import './button.css';
function List(props) {
let lis = [];
let rout = [];
props.data.row.map((elem,id) => {
lis.push( <ul key={id}><Link to={elem.FOOD_CD} ><button type="button" className="button" >{elem.DESC_KOR}</button> </Link></ul> )
rout.push(<Route key={id} path={elem.FOOD_CD} element={<Detail data={elem}/>}></Route>)
}
)
return(
<>
{lis}
<Routes>
{rout}
</Routes>
</>
)
}
export default List;
map함수로 받아온 데이터를 리스트로 변경해서 보여줬다 리스트는 2개를 생성하는데 하나는 버튼을 보여주고 다른 하나는 상세보기를 위한 라우터를 생성했다
상세보기 페이지
음식 리스트에서 버튼을 누르면 해당 음식의 상세 페이지를 보여줘야한다 만약 그냥 컴포넌트로 제작한다면 버튼 아래에 상세보기가 생성된다(위 코드 대로라면 )
상세 페이지를 따로 구성하기 위해서 Link를 통해서 주소를 바꿔주고Route를 이용해야한다
마지막 return 코드 변경
1
2
3
4
5
6
7
//List.js 마지막 부분
return(
<Routes>
<Route path="/" element={lis}></Route>
{rout}
</Routes>
)
라우터를 통해서 이때까지 리스트를 보여는곳의 주소는 ‘/’가 되고 상세 페이지는 버튼을 누르면 이동하는 페이지로 라우터조건을 적용했다
<ul key={id}><Link to={elem.FOOD_CD} ><button type="button" className="button" >{elem.DESC_KOR}</button> </Link></ul>
변경된 코드를 적용하면 상세 페이지는 따로 보여진다