프론트엔드/React

06. API 통신

짱구를왜말려? 2020. 1. 25. 01:00
반응형
SMALL

# What? 이게 뭔데?

- Application Programming Interface, 응용 프로그램 프로그래밍 인터페이스)는 응용 프로그램에서 사용할 수 있도록, 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻한다.

 

=> 백엔드랑 소통하는 통신소라 생각하면 편함.

  • 프론트 : 백엔드야 사용자가 할일 저장하고싶다고 나한테 이런 데이터를 입력해줬어
  • 백엔드 : 어어 그래 데이터에 문제 있는지 살펴보고 문제 없으면 데이터베이스에 저장할게. 문제 없네? 저장했어 이게 저장한 데이터야
  • 프론트 : 어 고마워 나도 사용자한테 저장 잘됐다고 알려주고, 할일 목록에 할일 추가해놓을게

이런 대화를 API를 통해서 함.

 

# Why? 왜 쓰는데?

- 왜 이런 소통을 API를 통해서 하나? API는 모든 접속을 표준화하기 때문에 기계/운영체제 등과 상관없이 누구나 동일한 액세스를 얻을 수 있음. 즉 백엔드는 JAVA + 리눅스, 프론트는 JAVASCRIPT + 노드처럼 서로 다른 환경이라 하더라도 API를 통해서라면 문제 없이 소통 가능.

 

# How? 어떻게 쓰는데?

1. 가상 API 서버 세팅(실습을 위해 가상의 API 서버를 세팅)

$ npm install -g json-server

 

1) db.json 파일 생성 및 테스트데이터 1개 세팅해놓기(여기에 데이터들이 저장될 예정)

@db.json

{
  "favorites": [
    {
      "id": 1,
      "title": "수박",
      "reason": "시원하고 달달해서 참 좋다",
    }
  ]
}

db.json 파일 위치 및 내용

 

2) API 서버 실행하기(백엔드용 서버, 프론트용 서버 두 개 열기)

$ npm start
$ json-server db.json --port=5000

 

2. 가상 API 서버와 통신하기 위해 axios라는 패키지 설치

$ npm install --save axios
  METHOD URL axios로 통신 보낼 때
좋아하는것 목록 GET /favorites axios.get("/favorites");
좋아하는것 생성
(좋아하는것 = favorite)
POST /favorites axios.post("/favorites", data);
좋아하는것 수정 PATCH or PUT /favorites/{고칠 favorite의 id} axios.patch("/favorites/1", data);
좋아하는것 조회 GET /favorites/{보고싶은 favorite의 id} axios.get("/favorites/1");
좋아하는것 삭제 DELETE /favorites/{삭제하고픈 favorite의 id} axios.delete(/favorites/1");

 

3. proxy 설정

- api 서버로 통신을 보낼 때 axios("http://localhost:5000/favorites");와 같이 앞에 "http://localhost:5000" 붙여줘야함. 매번 붙여주려면 귀찮으니까 package.json의 proxy라는걸 설정해서 api를 보내는 root url을 http://localhost:5000로 고정할 수 있음. 이러면 매번 앞에 root url 안적어줘도됨.

 

@package.json(package.json 설정 후 npm start 다시 해줘야 변경사항 적용됨)

{
  "name": "react-course",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^7.2.1",
    "axios": "^0.19.2",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-scripts": "3.3.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy": "http://localhost:5000" // 여기가 핵심
}

 

# Practice 연습

1. 좋아하는것 목록을 받아와서 뿌려보기

@App.js

import React from 'react';
import Favorites from './Favorites';

function App() {
	return (
		<div className="App">
			<Favorites />
		</div>
	);
}

export default App;

 

 

@ Favorites.js

import React, {useEffect, useState} from 'react';
import axios from 'axios';
import Favorite from "./Favorite";

const Favorites = () => {
	
	let [favorites, setFavorites] = useState([]);
	
	/* 컴포넌트가 렌더링 되면 바로 좋아하는것 목록을 불러와 favorites 데이터를 설정하기 */
	useEffect(() => {
		axios.get("/favorites")
			.then(response => {
				// 응답 데이터 형태 관찰해보기
				console.log(response);
				
				setFavorites(response.data);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
		
	}, []);
	
	return (
		<div>
			{/* map으로 컴포넌트를 여러개 만들 때는 각 컴포넌트를 구별할 수 있는 유일한 key값을 넘겨줘야함. */}
			{favorites.map(favorite => <Favorite key={favorite.id} favorite={favorite}/>)}
		</div>
	);
};

export default Favorites;

 

 

@ Favorite.jsx

import React, {} from 'react';

const Favorite = ({favorite}) => {
	return (
		<div>
			<p>좋아하는 것 : {favorite.name}</p>
			<p>좋아하는 이유 : {favorite.reason}</p>
		</div>
	);
};

export default Favorite;

 

2. 좋아하는것 생성해보기

@ Favorites.js

import React, {useEffect, useState} from 'react';
import axios from 'axios';
import Favorite from "./Favorite";

const Favorites = () => {
	
	let [favorites, setFavorites] = useState([]);
	
	let [form, setForm] = useState({
		title: "", // 좋아하는것 이름
		reason: "", // 좋아하는 이유
	});
	
	/* 컴포넌트가 렌더링 되면 바로 좋아하는것 목록을 불러와 favorites 데이터를 설정하기 */
	useEffect(() => {
		axios.get("/favorites")
			.then(response => {
				// 응답 데이터 형태 관찰해보기
				console.log(response);
				
				setFavorites(response.data);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
		
	}, []);
	
	const changeForm = (event) => {
		setForm({
			...form,
			[event.target.name]: event.target.value
		})
	};
	
	const save = (event) => {
		event.preventDefault();
		
		axios.post("/favorites", form)
			.then(response => {
				// 응답 데이터 형태 관찰해보기(저장한 data를 반환해줌)
				console.log(response);
				
				// 데이터를 저장한 후, todos 목록 전체를 다시 불러올 수도 있지만,
				// 기존 목록에 새로 생성된 todo만 추가해주는게 더 효율적
				setFavorites([...favorites, response.data]);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
	};
	
	
	return (
		<div>
			<form onSubmit={save}>
				<div>
					<input type="text" name="title" placeholder="좋아하는것" onChange={changeForm}/>
				</div>
				<div>
					<input type="text" name="reason" placeholder="좋아하는 이유" onChange={changeForm} />
				</div>
				
				<button>좋아하는것 추가</button>
			</form>
			
			{/* map으로 컴포넌트를 여러개 만들 때는 각 컴포넌트를 구별할 수 있는 유일한 key값을 넘겨줘야함. */}
			{favorites.map(favorite => <Favorite key={favorite.id} favorite={favorite}/>)}
		</div>
	);
};

export default Favorites;

 

 

3. 좋아하는것 수정해보기

  1. 좋아하는것 수정 후 새로고침 해서 데이터 업데이트됐는지 확인해보기
  2. 수정 후 바로 반영되게 변경해보기(setState 활용)

* 삼항연산자 알고가기

- 조건 ? (조건이 참일 때 실행할 내용) : (조건이 거짓일 때 실행할 내용)

  • 예제 1) 너는 남자냐 ? 그렇다면 남탕으로 들어가 : 아니라면 여탕으로 들어가
  • 예제 2) 철수가 맞는지 확인해보기
let name = "철수";

name === "철수" ? alert("나 철수 맞아") : alert("아닌데요"); // "나 철수 맞아"가 alert됨

 

@ Favorites.js

import React, {useEffect, useState} from 'react';
import axios from 'axios';
import Favorite from "./Favorite";

const Favorites = () => {
	
	let [favorites, setFavorites] = useState([]);
	
	let [form, setForm] = useState({
		title: "", // 좋아하는것 이름
		reason: "", // 좋아하는 이유
	});
	
	/* 컴포넌트가 렌더링 되면 바로 좋아하는것 목록을 불러와 favorites 데이터를 설정하기 */
	useEffect(() => {
		axios.get("/favorites")
			.then(response => {
				// 응답 데이터 형태 관찰해보기
				console.log(response);
				
				setFavorites(response.data);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
		
	}, []);
	
	const changeForm = (event) => {
		setForm({
			...form,
			[event.target.name]: event.target.value
		})
	};
	
	const save = (event) => {
		event.preventDefault();
		
		axios.post("/favorites", form)
			.then(response => {
				// 응답 데이터 형태 관찰해보기(저장한 data를 반환해줌)
				console.log(response);
				
				// 데이터를 저장한 후, todos 목록 전체를 다시 불러올 수도 있지만,
				// 기존 목록에 새로 생성된 todo만 추가해주는게 더 효율적
				setFavorites([...favorites, response.data]);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
	};
	
	
	return (
		<div>
			<form onSubmit={save}>
				<div>
					<input type="text" name="title" placeholder="좋아하는것" onChange={changeForm}/>
				</div>
				<div>
					<input type="text" name="reason" placeholder="좋아하는 이유" onChange={changeForm} />
				</div>
				
				<button>좋아하는것 추가</button>
			</form>
			
			{/* map으로 컴포넌트를 여러개 만들 때는 각 컴포넌트를 구별할 수 있는 유일한 key값을 넘겨줘야함. */}
			{favorites.map(favorite => <Favorite key={favorite.id} favorite={favorite} favorites={favorites} setFavorites={setFavorites}/>)}
		</div>
	);
};

export default Favorites;

 

@ Favorite

import React, {useState, Fragment} from 'react';
import axios from "axios";

const Favorite = ({favorite, setFavorites, favorites}) => {
	let [updateMode, setUpdateMode] = useState(false); // 수정모드 여부
	
	let [form, setForm] = useState({
		title: favorite.title,
		description: favorite.description
	});
	
	const changeMode = () => {
		setUpdateMode(!updateMode);
	};
	
	const changeForm = (event) => {
		setForm({
			...form,
			[event.target.name]: event.target.value
		})
	};
	
	const update = (event) => {
		event.preventDefault();
		
		axios.patch("/favorites/" + favorite.id, form)
			.then(response => {
				// 응답 데이터 형태 관찰해보기(수정한 data를 반환해줌)
				console.log(response);
				
				// 부모로부터 받은 setFavorites 참조를 이용하여 부모의 setFavorites를 호출하여 favorites를 업데이트
				setFavorites(favorites.map(favorite => {
					if(favorite.id === response.data.id)
						return response.data;
					
					return favorite;
				}));
				
				setUpdateMode(false);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
	};
	
	return (
		<div>
			{updateMode ?
				(
					<Fragment>
						<div>
							<input type="text" name="title" defaultValue={favorite.title} onChange={changeForm}/>
						</div>
						<div>
							<input type="text" name="reason" defaultValue={favorite.reason} onChange={changeForm}/>
						</div>
						<button onClick={update}>수정완료</button>
						<button onClick={changeMode}>취소</button>
					</Fragment>
				) :
				(
					<Fragment>
						<p>좋아하는 것 : {favorite.title}</p>
						<p>좋아하는 이유 : {favorite.reason}</p>
						<button onClick={changeMode}>수정</button>
					</Fragment>
				)
			}
		</div>
	);
};

export default Favorite;

 

 

4. 좋아하는것 삭제해보기

 

* filter 알고가기

- filter는 배열의 요소들을 하나씩 검사해보면서 조건에 안맞는 요소는 빼버린 배열을 반환해줌.

  • 숫자 배열에서 짝수인 숫자는 다 빼버리기
let numbers = [1,2,3,4,5];

numbers = numbers.filter(number => {
	if(number % 2 !== 0)
    	return number;
})

@ Favorite.js

import React, {useState, Fragment} from 'react';
import axios from "axios";

const Favorite = ({favorite, setFavorites, favorites}) => {
	let [updateMode, setUpdateMode] = useState(false); // 수정모드 여부
	
	let [form, setForm] = useState({
		title: favorite.title,
		description: favorite.description
	});
	
	const changeMode = () => {
		setUpdateMode(!updateMode);
	};
	
	const changeForm = (event) => {
		setForm({
			...form,
			[event.target.name]: event.target.value
		})
	};
	
	const update = (event) => {
		event.preventDefault();
		
		axios.patch("/favorites/" + favorite.id, form)
			.then(response => {
				// 응답 데이터 형태 관찰해보기(수정한 data를 반환해줌)
				console.log(response);
				
				// 부모로부터 받은 setFavorites 참조를 이용하여 부모의 setFavorites를 호출하여 favorites를 업데이트
				setFavorites(favorites.map(favorite => {
					if(favorite.id === response.data.id)
						return response.data;
					
					return favorite;
				}));
				
				setUpdateMode(false);
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
	};
	
	const remove = () => {
		axios.delete("/favorites/" + favorite.id)
			.then(response => {
				// 응답 데이터 형태 관찰해보기
				console.log(response);
				
				/* filter는 배열의 요소 중 본인이 명시한 조건을 만족하는 요소만 남긴 배열을 반환함 */
				setFavorites(favorites.filter(favoriteData => {
					return favoriteData.id !== favorite.id;
				}));
				
			}).catch(error => {
			console.log(error);
			
			alert("문제가 발생하였습니다.");
		});
	};
	
	return (
		<div>
			{updateMode ?
				(
					<Fragment>
						<div>
							<input type="text" name="title" defaultValue={favorite.title} onChange={changeForm}/>
						</div>
						<div>
							<input type="text" name="reason" defaultValue={favorite.reason} onChange={changeForm}/>
						</div>
						<button onClick={update}>수정완료</button>
						<button onClick={changeMode}>취소</button>
					</Fragment>
				) :
				(
					<Fragment>
						<p>좋아하는 것 : {favorite.title}</p>
						<p>좋아하는 이유 : {favorite.reason}</p>
						<button onClick={changeMode}>수정</button>
					</Fragment>
				)
			}
			
			<button type="button" onClick={remove}>삭제</button>
		</div>
	);
};

export default Favorite;

 

# Problem 과제

할일 조회, 생성, 수정, 삭제 해보기

* 코드참고: https://github.com/ShinHyungJune/react-course/blob/course-6-api/src/App.js

 

LIST