프론트가 아니어서 자세히는 모르지만 기억나는대로 간단하게 정리해 보았다.
react 프로젝트 생성
프로젝트 생성하려는 위치로 이동한다.
npm과 node.js는 설치가 되어있어야한다.
프로젝트 생성
npx create-react-app my-react
명령어로 생성
react 실행
npm start
이 화면이 뜬다면 완료.
npm start
실행시
> my-react@0.1.0 start
> react-scripts start
'react-scripts'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는
배치 파일이 아닙니다.
라는 오류가 발생한다면
npm install react-scripts으로 npm 재설치를 해주면 된다.
api 호출
Proxy 설정
React 애플리케이션에서는 API 호출 시 프록시(proxy)를 설정하여 API 서버로 요청을 보낼 수 있다.
따라서 API 서버의 엔드포인트들은 /api
를 포함한 경로로 정의하였고, React 애플리케이션에서 setupProxy.js 설정파일을 통해 /api
를 포함한 경로로 호출 시 API서버로 리다이렉트 해주었다.
axios 라이브러리 설치
npm install axios
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
// /api로 시작하는 모든 요청을 localhost:8081로 프록시
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:8081',
changeOrigin: true,
})
);
};
데이터를 불러올때
api 호출
const [users, setUsers] = useState([]);
try {
const response = await axios.get('/api/get'); // API endpoint에 맞게 URL 수정
setUsers(response.data); // API 응답 데이터를 상태에 설정
} catch (error) {
console.error('Error fetching data:', error);
}
axios를 통해 api를 호출하고 응답 데이터를 상태에 설정해준다.
주로 async/await 구문을 사용하여 비동기적으로 API를 호출하고 응답을 처리
예시
http://localhost:8081/api/get api를 호출하고,
응답 데이터가 다음과 같을때
[
{
"id": "qweqwe",
"pw": "123123",
"birth": "1997-11-26",
"myList": [
"11",
"22"
],
"imgPath": "https://firebasestorage.googleapis.com/v0/b/<>/o/IMG_3842.JPG?alt=media"
},
{
"id": "yooon",
"pw": "qwe123",
"birth": "2002-04-24",
"myList": [
"11",
"22"
],
"imgPath": "https://firebasestorage.googleapis.com/v0/b/<>/o/firebase.png?alt=media"
}
]
api 호출을 테스트하기위해 간단한 화면을 만들었다.
전체 코드는 다음과 같다.
UserList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const UserList = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('/api/get'); // API endpoint에 맞게 URL 수정
setUsers(response.data); // API 응답 데이터를 상태에 설정
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchUsers();
}, []);
return (
<div>
<h2>User List</h2>
<ul className="user-list">
{users.map(user => (
<li key={user.id} className="user-item">
<div className="user-info">
<div><strong>ID:</strong> {user.id}</div>
<div><strong>Password:</strong> {user.pw}</div>
<div><strong>Birth Date:</strong> {user.birth}</div>
<div>
<strong>My List:</strong>
<ul className="nested-list">
{user.myList.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
<img src={user.imgPath} alt="이미지" style={{ width: '200px'}}/>
</div>
</li>
))}
</ul>
</div>
);
};
export default UserList;
화면을 띄우기 위해 App.js도 수정해주었다.
App.js
import './App.css';
import UserList from "./UserList";
import CreateUserForm from "./createUserForm";
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/user" element={<UserList />} />
<Route path="/usercreate" element={<CreateUserForm />} />
</Routes>
</Router>
{/*<UserList />*/}
</div>
);
}
export default App;
결과화면
데이터를 저장할때
api에서 요구하는 데이터 형식에 맞추어 전송하면 된다.
주로 async/await 구문을 사용하여 비동기적으로 API를 호출하고 응답을 처리
- 파일을 함께 전송하는 경우 헤더를
'Content-Type': 'multipart/form-data'
으로 지정해준다.const formDataForRequest = new FormData(); formDataForRequest.append('user', JSON.stringify(formData)); // Test 객체를 JSON 문자열로 변환하여 FormData에 추가 formDataForRequest.append('file', selectedImg); // 파일 객체를 FormData에 추가 const response = await axios.post('/test/create', formDataForRequest, { headers: { 'Content-Type': 'multipart/form-data', // 파일 업로드 시에는 Content-Type을 지정해야 합니다. }, });
예시
컨트롤러에서 요구하는 데이터가 Json형식의 데이터와 파일 데이터 두가지일때
controller
@PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> createUser(@RequestParam("user") String testDataJson,
@RequestPart("file") MultipartFile file) throws Exception{
String response = testService.createUser(testDataJson,file);
return ResponseEntity.ok(response);
}
- 이때 user파라미터는 User형식의 데이터를 string 형식으로 받아 backend에서 User클래스로 매핑해준다.
User.java
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class User {
private String id;
private String pw;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date birth;
private List<String> myList;
private String imgPath;
}
React에서 전체코드는 다음과 같다.
createUserForm.js
import React, { useState } from 'react';
import axios from 'axios';
import Chip from '@mui/material/Chip';
import Box from '@mui/material/Box';
import {ListItem} from '@mui/material';
import { TextField } from '@mui/material';
const CreateUserForm = () => {
const [formData, setFormData] = useState({
id: '',
pw: '',
birth: '',
myList: [],
imgPath: "qwe123",
});
const [selectedImg, setSelectedImg] = useState([]);//첨부한 이미지
const handleInputChange = (event) => {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value,
});
};
const handleKeyPress = (event) => {
if (event.key === 'Enter' && event.target.value.trim() !== '') {
const newItem =event.target.value.trim();
setFormData((prevFormData) => ({
...prevFormData,
myList: [...prevFormData.myList, newItem], // 이전 myList 배열에 새 label 값 추가
}));
event.target.value = ''; // 입력 필드 비우기
event.preventDefault(); // 엔터키 이벤트의 기본 동작인 폼 제출을 막음
}
};
const handleChipDelete = (itemToDelete) => () => {
const updatedList = formData.myList.filter((item) => item !== itemToDelete);
setFormData({
...formData,
myList: updatedList,
});
};
const handleFileChange = (event) => {
setSelectedImg(event.target.files[0]); // 첫 번째 파일만 선택
};
const handleSubmit = async (event) => {
event.preventDefault();
console.log("click");
console.log(formData);
try {
const formDataForRequest = new FormData();
formDataForRequest.append('user', JSON.stringify(formData)); // Test 객체를 JSON 문자열로 변환하여 FormData에 추가
formDataForRequest.append('file', selectedImg); // 파일 객체를 FormData에 추가
const response = await axios.post('/test/create', formDataForRequest, {
headers: {
'Content-Type': 'multipart/form-data', // 파일 업로드 시에는 Content-Type을 지정해야 합니다.
},
});
console.log(response.data); // API 응답 데이터 확인
// 성공적으로 요청을 처리했을 때 필요한 작업을 수행합니다.
} catch (error) {
console.error('API 요청 실패:', error);
// 요청 실패 시에는 오류 처리 로직을 추가합니다.
}
};
return (
<form onSubmit={handleSubmit}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<label>
ID:
<input type="text" name="id" value={formData.id} onChange={handleInputChange} />
</label>
<br />
<label>
Password:
<input type="password" name="pw" value={formData.pw} onChange={handleInputChange} />
</label>
<br />
<label>
Birth:
<input type="date" name="birth" value={formData.birth} onChange={handleInputChange} />
</label>
<br />
<label style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
My List (comma-separated):
{/*<input type="text" name="myList" value={formData.myList.join(',')} onChange={handleInputChange} />*/}
<Box component="ul">
{formData.myList.map((data, index) => (
<ListItem key={index}>
<Chip
label={data}
onDelete={data === 'React' ? undefined : handleChipDelete(data)}
/>
</ListItem>
))}
<ListItem>
<TextField
placeholder="Add item..."
onKeyDown={handleKeyPress}
/>
</ListItem>
</Box>
</label>
<br />
<label>
Image File:
<input type="file" onChange={handleFileChange} />
</label>
<br />
<button type="submit">Create User</button>
</div>
</form>
);
};
export default CreateUserForm;
결과화면
리액트를 실행시킨 후 해당 화면으로 접속하면 다음과 같은 화면을 볼 수 있다.
데이터를 입력한 후 Create User 버튼을 눌러 데이터를 전송하면 api를 통해 데이터 저장을 할 수 있다.
api 호출 후 응답데이터를 확인할 수 있다.
- 실패
- 성공
설치 필요한 npm
npm install @mui/material
npm install @emotion/styled
npm install @emotion/react