리액트는 UI를 이루고 있는 독립적이고 재사용가능한 코드 조각인 컴포넌트가 있다.
함수형 컴포넌트와 클래스형 컴포넌트가 있다. 전에는 클래스형 컴포넌트를 주로 사용하면서 State를 직접 사용하고 생명주기 매소드를 사용하였다.
하지만 리액트 Hook이 만들어지면서 함수형 컴포넌트도 상태와 생명주기 매소드를 Hook을 통해 사용할 수 있게되었다. Hook은 useState, useEffect 등 다양하고 이름앞에 use가 붙는 것이 특징이다.
함수형 컴포넌트는 특수한 매서드 없이도 JSX를 반환하는 함수로 쓰면 되기에 클래스형 컴포넌트보다 코드가 간단하다는 장점이 있어 요즘은 거의 다 함수형 컴포넌트와 리액트 Hook을 사용한다.
state(상태)는 변수라고 생각하면 된다. js에서 변수를 사용해 JSX에 넣을 수도 있다. 하지만 변수가 달라져도 컴포넌트가 재랜더링 되지 않아 페이지 상에는 변화된 변수가 업데이트 되지 않는다. 그래서 state를 사용한다.
state를 사용하면 만약 state가 변했을 때 리액트가 변화를 감지하고 컴포넌트를 재랜더링 해준다.
import React, { useState } from 'react';
const Counter = () => {
// count 상태와 이를 변경하는 setCount 함수를 정의
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // 상태를 변경
};
return (
<div>
<p>Count: {count}</p> {/* 상태를 UI에 반영 */}
<button onClick={increment}>Increment</button> {/* 버튼 클릭 시 상태를 변경 */}
</div>
);
};
export default Counter;
useState는 다음과 같이 사용할 수 있다. useState를 호출하면 변수명에 원하는 값으로 초기화하고 값을 변화시킬 수 있는 set함수를 선언할 수 있다.
클릭을 할때마다 increment 함수를 호출하여 count를 증가시키면 리액트가 state 변화를 감지하고 Counter 컴포넌트를 재랜더링해준다.
useEffect는 컴포넌트의 Side Effect (부수 효과)를 만들어주고 생명주기 매서드와 같은 역할을 해준다.
import React, { useEffect } from 'react';
const Example = () => {
useEffect(() => {
// componentDidMount: 컴포넌트가 마운트된 후 실행
return () => {
// componentWillUnmount: 컴포넌트가 언마운트되기 전에 실행
};
}, []); // 빈 배열: 마운트 시 한 번만 실행
// [] 을 의존성 배열이라 한다. 안에 있는 값, State, 함수 등이 update 할 때마다 useEffect가 실행된다.
return <div>Example</div>;
};
export default Example;
프론트에서 백엔드 서버를 요청하기 위해서는 우선 프론트를 띄울 port와 서버를 운영할 port가 따로 필요하다.
서버
1. post요청은 프론트에서 바디에 데이터를 담아서 보낸다.
2. 서버가 받은 데이터를 꺼내쓰기 위해 파싱한다.
3. 파싱한 데이터를 받아서 데이터로 필요한 작업을 수행한다.
4. 결과를 저장해 주고
5. Get 요청을 했을 때는 결과를 보여준다.
클라이언트
1. 서버 요청을 할때 fetch와 axios를 이용해서 할 수 있다.
2. 서버 주소와 HTTP 매서드를 정해주어야 한다.
3. 버튼을 눌러 submit 이벤트가 발생하면
4. post 요청으로 input안에 입력한 값을 전달할 수 있다.
5. 전달이 끝나면 그에 대한 답을 get요청으로 받아와야한다.
기본 제공하는 api
아래는 유튜브를 보면서 따라서 실습을 한 코드이다.
import { useEffect, useState } from 'react';
function App(){
const { todoList, setTodoList } = useState(null);
const fetchData = () => {
fetch("http://localhost:4000/api/todo")
.then((res)=> res.json())
.then((data)=> setTodoList(data));
};
useEffect(()=>{
fetchData();
},[]); //useEffect를 사용해 첫 랜더링이 될때만 서버에 요청을 한다.
const onSubmitHandler = (e) => { //form 이 제출될 때 발생하는 이벤트를 받아와
e.preventDefault(); // 쿼리에 넣어서 get요청 보내는 기본 실행 없애기
const text = e.target.text.value; // 변수에 저장한다.
const done = e.target.done.checked;
fetch('http://localhost:3000/api/todo', {
method: 'POST',
headers: {'Content-Type': 'application/json', //보내는 데이터가 JSON 데이터라는 것을 알림
},
body: JSON.stringify({
text,
done,
}),
}).then(()=>fetchData()); // 서버에 입력을 해서 데이터를 추가하고 나서 다시 불러와 출력
};
return (
<div className='App'>
<h1>Todo List</h1>
<form>
<input name="text"/>
<input name='done' type='checkbox' />
<input type='submit' value='추가'/>
</form>
{todoList.map((todo)=>(
<div key={todo.id}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div>{todo.done ? 'Y' : 'N'}</div>
</div>
))}
</div>
);
}
Axios 또한 api 요청의 기능이 있다. 하지만 fetch와는 다르게, 따로 다운을 받아야 하는 라이브러리이다.
axios는 fetch 보다 문법이 더 간단하다는 장점이 있다.
//나머지 동일
import axios from 'axios'
const fetchData = async() => {
const res = await axios.get(SERVER_URL); // 비동기 작업이 끝나고 아래 코드가 실행
setTodoList(res.data);
};
useEffect(()=>{
fetchData();
},[]); //useEffect를 사용해 첫 랜더링이 될때만 서버에 요청을 한다.
const onSubmitHandler = async (e) => { //form 이 제출될 때 발생하는 이벤트를 받아와
e.preventDefault(); // 쿼리에 넣어서 get요청 보내는 기본 실행 없애기
const text = e.target.text.value; // 변수에 저장한다.
const done = e.target.done.checked;
await axios.post(SERVER_URL, { text, done }); // 메소드, 헤더 설정, 바디설정 다 알아서 해줌
fetchData()); // 서버에 입력을 해서 데이터를 추가하고 나서 다시 불러와 출력
};
return (
// 위 코드와 동일한 JSX
)
Cross Origin Resource Sharing
Origin이 다를때 서로 데이터를 꺼내가도 된다는 의미, 당연히 origin이 다르면 데이터를 공유할 수 없다고 막아놓았다.
origin : host와 port를 포함한 데이터의 출처
가 다른 localhost:3000의 데이터와 localhost:4000의 데이터는 출처가 다르다.
서버가 데이터를 주지 않기로 막고있는것이기 때문에 서버에서 CORS 정책을 풀어줘야한다.
from flask_cors import CORS
CORS(app)
이렇게 코드를 작성하면 import하고 cors를 해제해줄 수 있다. 그럼 데이터가 잘 넘어온다.