앞선 글에 포스팅하였던 Nest.js로 User 프로젝트의 서버를 만들었다면, 이번에는 React로 화면을 만들 차례이다.
npx create-react-app [프로젝트 이름]

먼저 오늘 진행했던 프로젝트의 폴더 구조는 이렇다. 원래는 하나의 폴더 안에 front, back으로 분리하려했지만, 나중에 배포할 상황에 대비하여 아예 다른 폴더로 분리해주었다.
먼저 NestJs에 가서 App.ts에
const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3000);
app.enableCors를 꼭 추가해준다. 이 코드의 의미는 다른 자원에서 접근할 수 있는 권한을 부여하는 것이다.
function App() {
const [isSearching, setIsSearching] = useState(false);
const searching = () => {
setIsSearching(true);
}
return (
<div className="App">
<h1>Users</h1>
<Search isSearching={searching} />
{!isSearching && <UserList />}
<CreateUser />
</div>
);
}
const UserList = () => {
const [dataList, setDataList] = useState([]);
useEffect(() => {
const getUserData = async () => {
await axios.get('http://localhost:3000/user')
.then(response => {
setDataList(response.data)
})
}
getUserData()
}, [])
// 해당 id의 User 정보 삭제하기
const deleteUserData = async(selectedId) => {
await axios.delete(`http://localhost:3000/user/${selectedId}`)
.then(response => {
console.log(response);
console.log("삭제되었습니다");
})
}
return (
<div>
{dataList.map((data) => {
return (
<ul key={data.id} className="list-group list-group-horizontal justify-content-center">
<li className="list-group-item">{data.id}</li>
<li className="list-group-item" >{data.name}</li>
<li className="list-group-item">{data.description}</li>
<li className="list-group-item"><button onClick={() => {deleteUserData(data.id)}}>삭제</button></li>
<li className="list-group-item"><button onClick={() => { <UpdateUser id = {data.id}/>}}>수정</button></li>
</ul>
)
})}
</div>
)
}
export default UserList;
Axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리입니다.
const CreateUser = () => {
const [userData, setUserData] = useState({
id: '',
name: '',
description: ''
});
const InfoChangeHandler = (event) => {
setUserData({
...userData,
[event.target.name]: event.target.value
});
}
const dataSubmitHandler = () => {
axios.post('http://localhost:3000/user', {
id: userData.id,
name: userData.name,
description: userData.description
})
.then((data) => {
console.log(data);
})
}
return (
<form onSubmit={dataSubmitHandler}>
<div className="mb-3 mt-4 col-md-6 mx-auto">
<label htmlFor="Id" className="form-label">ID</label>
<input type="text" name="id" className="form-control mx-auto" aria-describedby="emailHelp" onChange={InfoChangeHandler} />
</div>
<div className="mb-3 col-md-6 mx-auto">
<label htmlFor="Name" className="form-label">Name</label>
<input type="text" name="name" className="form-control mx-auto" onChange={InfoChangeHandler} />
</div>
<div className="mb-3 col-md-6 mx-auto">
<label htmlFor="Description" className="form-label">Description</label>
<input type="text" name="description" className="form-control mx-auto" onChange={InfoChangeHandler} />
</div>
<button type="submit" className="btn btn-primary mx-auto">Submit</button>
</form>
)
}
export default CreateUser
const Search = (props) => {
const [findId, setFindId] = useState('');
const [searchUser, setSearchUser] = useState('')
const findInfo = (event) => {
setFindId(event.target.value)
}
const searchUserInfo = async (event) => {
// 검색 중인지 아닌지 확인
props.isSearching();
event.preventDefault();
// 검색 시 해당 User 정보 반환
await axios.get(`http://localhost:3000/user/${findId}`)
.then(response => {
setSearchUser(response.data)
})
}
return (
<div>
<form onSubmit={searchUserInfo}>
<label htmlFor="Search"></label>
<input type="text" name="id" onChange={findInfo}></input>
<button type="submit">검색</button>
</form>
<div className="mt-4">
{searchUser && <ul key={searchUser.id} className="list-group list-group-horizontal justify-content-center">
<li className="list-group-item">{searchUser.id}</li>
<li className="list-group-item" >{searchUser.name}</li>
<li className="list-group-item">{searchUser.description}</li>
</ul>
}
</div>
</div>
)
}
export default Search;