두근두근 이제 만들 틀은 다 만들었다. axios를 사용하여 데이터를 주고, 또 받아오도록하자.
#api명세 [Back-end 구현사항]
1. 먼저 추가를 누르면 제품이 추가되야한다!
2. 삭제를 누르면 제품이 삭제되어야한다!
3. DB에서 제품의 정보를 받아올 수 있어야한다!
난 그렇기 때문에 장고 백엔드를 다음과 같이 설계할 것이다.
METHOD : GET = /api/post => DB에 있는 모든 정보 받아오기
METHOD : POST = /api/post => DB에 제품 추가
METHOD : DELETE = /api/post/<int:id> => 해당 제품번호 삭제
일부로 한 post에서 다 처리하도록 몰았다. 사실 내가한 방법이 맞는지는 모르겠다. 난 이번 프로젝트의 취지는 '대가리 박치기' 이다.. ㅎㅎ
# 리액트에서 서버로 POST하기 with axios
서버와 통신할 수 있는 방법중에 axios와 fetchapi인가 두개가 있는거 같았는데 axios가 많이쓴다고하고 쓰기도 편하다고해서 이걸로 썼다!
폼안에 있는 input태그( name, price ) 가 백엔드로 전송되어야한다. 그리고 백엔드에서는 이걸 받아서 DB에 저장해야한다. 지금은 프론트 단계이기때문에 보내는 것 만 신경쓴다.
import axios from "axios";
//..
//..
//..
const onSubmitItem = (e) => {
e.preventDefault();
const body = {
'name' : name,
'price' : price
}
axios.post('http://localhost:8000/api/post/',body)
.then((res) => {
console.log(res.data)
post_get() //다시 get해서 refresh해줌
})
.catch((err)=>{
console.error(err)
})
//전송 후에는 input태그의 값들을 다 지워줌(리셋)
setInputs({
'name' : '',
'price' : '',
})
}
return (
<>
<h1>포스트입니다</h1>
<form onSubmit={onSubmitItem}>
<input placeholder="제품명" type="text" name="name" onChange={onInputChange} value={name}/>
<input placeholder="가격" type="number" name="price" onChange={onInputChange} value={price}/>
<button type="submit">추가</button>
</form>
<h2>등록제품</h2>
<div>
<ItemList users={result} post_get={post_get}/>
</div>
</>
)
axios를 import해준다. axios는 서버에게 get post delete update를 날릴 수 있게 도와준다. 또 비동기 방식인 JS 동기적으로 async awiat, axios를 조합하여 실행 할수 있게해준다고 이해하고는 있다. 확실하지는 않다.. 왜냐하면 서버에서 뭔가를 받아올때는 JS는 비동기이기때문에 받아오기전에 다른게 돌아버리면 안되기 때문에 그렇게 이해하고는 있다.
onSubmitItem함수를 저렇게 적어주면 해당 주소로 body를 보내게 된다. body는 장고에서 req.body로 받게 되면 내가 보낸 정보들이 들어있다! ("주소",body,header)를 주면 3번째 인자는 req헤더에 들어가게 된다
# 리액트에서 서버에게 GET요청하기
##post보낸 후 자동으로 get하기 위해서 (자동으로 값 받아오기)
마지막에 딱 된거 같은데 내가 새로고침을 눌러줬다. 이렇게 post_get함수를 정의해서 사용하지 않으면 데이터를 보내든 삭제하든 딱 그요청만 수행하게 해서 내가 만들었다. 사실 이것도 원래 이렇게 쓰는지는 잘 모르겠다. 뭔가 오토로 해주는 기능도 있을거 같은데 ,.,
const post_get = () => {
axios.get('http://localhost:8000/api/post/')
.then((res)=> {
const data = JSON.parse(res.data);
//받아온 데이터를 가공하여 저장함
let new_data = [];
data.map((item,index)=> {
new_data.push({
...item.fields,
['id'] : item.pk
});
});
console.log(new_data)
setResult(new_data);
})
.catch((err)=>{
console.error(err)
})
}
get또 똑같다. axios.get('url')해주면 해당주소로 GET을 보내게 된다. 그리고 서버에서 알아서 데이터를 가공해서 보내주기만 하면 된다. 여기서 사실 노드와 리액트를 같이하면 왜 좋은지 알게 된거 같다. 장고(파이썬)은 기본적으로 json이라는 것을 지원하지 않는다. 그래서 서버로 리액트가 json형식으로 보내도, 장고에서는 그게 dic(사전형)으로 처리하게 된다. 여기서 사실 고생을 했다. 장고관련된거는 프론트 끝나면 정리할것이기 때문에 일단 지금은 패스이다!
서버로 GET요청을 하면 내 장고는 저렇게 데이터를 쏴준다. 사실 만족스럽지 못하다. 백에서부터 딱 정리해서 쏴주면 좋은데 난 장고 순수기능만 사용해서 구현하다보니 잘 모르겠었다. 차차 해보면서 개선시킬 것이다!
저렇게 쏴줬기 때문에 프론트에서는 필요없는 정보는 딱 자르고, [{id : ' ', name: ' ', price: ' '},{id : ' ', name: ' ', price: ' '}]형식으로 딱 잘라주기 위해서 new_data를 통해서 가공했다.
const data = JSON.parse(res.data);
여기도 사실 내가 해결해야할 과제 중 하나이다. 분명 장고에서는
json_posts = serializers.serialize("json", posts)
return JsonResponse(json_posts,safe=False)
과 같이 json화 해서 날려줬는데 프론트에서 도착해서 타입을 봐보니까 string이다. 그렇기 때문에 map함수도 돌릴 수 없고 난감했다. 왜 JSON을 써야하는지 아는 순간이었다. 여튼 JSON.parse를 사용하면 string을 json으로 바꿀 수 있다기에 저걸 사용했고, 성공적으로 변환해서 데이터를 가공 시켰다. 이렇게 정의한 함수를 delete, post 에 .then에 넣어줬다.
## useEffect를 사용해서, 처음 시작시 자동으로 값 GET 하기
useEffect(()=>{
fecthAPI()
//get_post() 사용하는게 더 좋을듯!
},[])
이렇게 작성하면 해당 페이지가 최초실행시에 fecthAPI()가 한번 실행된다. 클래스형 리액트를 사용할때는 componentDidmount 등등 라이플 사이클을 조작하는거와 같은 맥락이다.
const fecthAPI = async () => {
const result = await axios.get('http://localhost:8000/api/post/')
.then((res)=> {
const data = JSON.parse(res.data)
// console.log(data[0].fields)
console.log(data)
data.map((item,index)=> {
const push_id = {
...item.fields,
['id'] : item.pk,
}
setResult(prev => [...prev,push_id])
})
})
.catch((err)=>{
console.error(err)
})
}
async와 awiat를 사용해서 동기방식처럼 수행한다. 저 함수가 다돌아가고나서야 다른게 실행되야하기 때문이다. 예를들면 로딩상태라고 생각하면 될것 같다.
post_get함수랑 사실 다 똑같은데 다른점은 setResult에 뭐가 들어가는지이다. post_get은 덮어씌우는 방식이고 이건 차곡차곡 쌓아가는 느낌이다. 지금생각해보니까 fecthAPI없애고 그냥 post_get으로 사용해도 될것 같았다. 여튼 여기서 리액트에서 ...로 어떻게 state를 관리하는지 알 수 있었다.
# 리액트에서 DELETE요청 장고로 보내기, 함수 자손에게 내려보내기
//Post.js
return (
<>
<h1>포스트입니다</h1>
<form onSubmit={onSubmitItem}>
<input placeholder="제품명" type="text" name="name" onChange={onInputChange} value={name}/>
<input placeholder="가격" type="number" name="price" onChange={onInputChange} value={price}/>
<button type="submit">추가</button>
</form>
<h2>등록제품</h2>
<div>
<ItemList users={result} post_get={post_get}/>
</div>
</>
)
지우기를 하기 위해서 두가지 문제점에 봉착했었다. 맨 처음에는 ID값을 따로 관리를 안해줘서 DELETE요청을 뭘 기준으로 줄지를 몰랐다. 그래서 id도 함께 관리(users에서)한 것이다. 삭제 후 post_get으로 다시 auto-refresh를 해줘야하는데, 지우는 버튼은 Post -> ItemList-> Item 즉 Item에서 쓰인다. 한마디로 자식의 자식의 컴포넌트에서 쓰여서 함수를 내려 보내줬다. 사실 뭐 어려운건 없고 post_get을 저렇게 props로 넘겨보내면 되는데 2개 밑으로 보내야해서 조금 번거로웠다. 그래서 리덕스를 왜 공부해야하는지 알것 같았다. 옛날에는 Context.API인가를 사용해봤는데 리덕스도 리액트의 필수라니까! 공부해볼겸 진짜 나만의 프로젝트를 시작할때는 써보려고 한다.
### DELETE
// ItemList.js 에서 Item
const onBtnDel = () => {
axios.delete(`http://localhost:8000/api/post/${my_id}/`)
.then((req)=> {
console.log("Sucess")
// refresh함수 부모에서부터 가져오기
post_get()
})
.catch((err)=>{
console.error(err)
})
}
return (
<>
<td style={myStyle}>{index}</td>
<td style={myStyle}>{name}</td>
<td>{price}</td>
<td>{my_id}</td>
<td>
<button onClick={onBtnDel}>삭제</button>
</td>
</>
)
}
사실 DELETE는 진짜 프론트에서는 해줄게 없다. 지워줄 게시물고유 id값 delete로 쏴주면 된다.
프론트에서는 여기까지면 다 구현했다. 사실 백엔드 공부하려고 시작한건데 리액트 너무 재미없어서 놨었는데 이렇게 같이 하니까 갑자기 재밌어졌다.. 하하.. 이제 장고에서 설계한거 포스팅하고 로그인 구현해보려고 한다. 근데 로그인이 세션유지같은거 들어가서 조금 어려워보였다.
'• 개발 > React' 카테고리의 다른 글
[React] 리액트 장고 연동#2, 리액트 한 파일에서 두개 컴포넌트 만들기, 구조분해를 사용하여 props쉽게 받기 (0) | 2022.02.06 |
---|---|
[React] 리액트 장고 연동#1 , input태그 여러개한번에 관리하기, React에서 form사용하기 (0) | 2022.02.06 |