프론트에서 서버에 데이터를 요청하는 방법(React로 fetch,axios 사용하기)

태민·2022년 11월 16일
0

바탕화면에 react for studying 이라는 폴더를 만들고

vscode로 그 폴더를 열어서

임의로 폴더 fetch를 만들고

그안에 server와 client를 만든다

터미널 창을 열고 cd fetch/server 를 통해 해당 폴더로 이동하고

npm init을 입력하고

엔터를 연타해서 pakage.json파일을 생성해준다

그리고 npm i express를 입력해서 서버를 설치해주자

서버는 express서버를 이용 할 예정이다

설치가 끝났으면

서버 폴더안에 app.js파일을 만들어주고

express 서버 코드를 짜주면 된다

코드짤 경우에는 구글에가서 npm express를 검색해주고

초기샘플 코드가 있는데 그걸 복사해서 붙여넣어준다

const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000)

이 코드를 붙여넣고

서버가 잘 실행됬는지 알기위해서

콜백함수를 하나 넣어서 확인해보자

const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000, ()=>{
    console.log('server start!!');
});

이렇게 작성해주고 실행해보자

node app.js 라고 검색하니까

server start!! 라고 잘 출력되어서 서버가 정상구동되는 것을 확인 할 수 있다

자 이제 주소창으로 가서 3000포트를 입력해서 출력이 잘 되는지 확인해주면 된다

api는 postman을 이용해서 데이터가 잘 넘어오는지

get방식 post방식으로 확인해보자

( 포스트맨 사용법은 따로 포스팅 해둔 것을 확인하면된다 )

확인해보니 정상적으로 동작이 되고있다면 서버는 완성이 되었다

이제 프론트 코드를 짜보자

클라이언트 폴더로 이동해줄건데 간단하게 터미널창에

cd ../client 엔터해주면 된다

이번에는 yarn이 아니라 npx를 이용해서 리액트 프로젝트를 만들어보자

npx create-react-app . 이렇게 입력해보자

설치가 완료되었다면 필요없는 파일을 전부 지워줄건데

logo.svg
index.css
App.css
App.test.js

이 4개 파일은 필요없으니 날려버리자!

그러면 이제 4개의 깔끔한 파일이 남는데

index.js에서 필요없는 import 지워주고

App.js에서도 필요없는 import 전부 지워주자

그리고 App.js에서 필요없는 파일 전부 지워주고

TODO LIST 라고 h1태그로 묶어서 출력해볼건데

npm start를 입력해서 확인해보자

TODOLIST 라고 잘나온다!

이제 서버에 데이터를 한번 요청을 해볼건데

기본제공하는 api fetch와

다운받아서 설치하는 비순수 라이브러리 axios가 있다

둘다한번 사용해보자

일단 서버에 데이터를 요청하려면 어떤게 필요할까

어떤 메서드를 사용하는지 주소가 무엇인지 알아야한다

이 2개가 핵심이다!

우리는 테스트를 위해서 프론트와 서버를 동시에 열어야한다

그러니까 서버 포트는4000번 프론트는 3000번으로 재수정해주고 시작하자

새로운 터미널창을 열고
cd fetch/server 를 이용해서

node app.js로 4000포트로 서버를 실행해보자

다시 포스트맨으로 가서 4000포트로 수정해주고 확인해보니 데이터가 잘 확인이되고

3000으로해주면 실패메세지가 확인된다!

아직까지는 재밌고 이해도 잘 되는 것 같다

계속 배워보자

다시 돌아가서 프론트에서 서버에 데이터를 요청하려면

HTTP메서드와 서버주소만 있으면된다 이 2개는 계속 기억해야한다

한번 그냥 써보자 사용법은 공식문서를 찾아보면 되는거지 외울필요는 없다

fetch를 이용할 경우에는

 fetch('http://localhost:4000/api/todo')
  .then((response)=> response.json())
  .then((data)=>setTodoList(data));

첫번째 데이터는 json형태로 정제를 해주고
두번째로 받는 데이터가 우리가 원하는 투두리스트 데이터이다

이렇게 적고 저장하고 새로고침해주고 네트워크를 눌러보면

todolist가 잘동작하는것을 확인 할 수 있는데

cors라는 에러가 나올 수 있다

그 에러는 개발자로 생활하면서 계속해서 봐야하는 에러임으로 따로 포스팅해서 정리해둬야겠다

물론 현재 나는 에러가 생기지 않았지만 알아둬서 나쁠건 없다!

자 이제 콘솔을 열어보면 데이터가 정상적으로 잘 나온다

이제 여기서 더 발전해서 콘솔창이 아니라 실제로

텍스트로 받아온 데이터를 프론트에 띄우는 것도 해보자

그럴려면 당연히! useState를 사용해야한다

코드를 짜보면

const[todoList,setTodoList]=useState([null]);
이렇게 한줄을 추가해주고

console을 지우고
setTodoList(data)
이렇게 변경해주고 저장해주면된다

이렇게 저장하고 실행하면 정상적으로 작동을 해야할텐데

무조건 100프로 확률로 콘솔을 열어보면 에러가 나오고 미친듯이 에러가 출력되고 멈추지않는다

왜그럴까? 그 이유는 계속해서 setTodoList로 데이터가 들어와서 상태가 계속해서 변경되니까

리액트는 본연의 일을 해야함으로 계속 렌더를 돌리는 것이다

무한렌더의 늪에 빠져서 계속해서 데이터를 끌어오고 있는 것이다

단 훅에서 받아온 코드는 최초 실행이후 별로 하는 일이 없으니 그 밑에 코드부터 계속 리렌더가된다

이렇게 미친듯이 리렌더가 일어날 때 우리가 할 수 있는 방법은

useEffect! 이 녀석을 이용해서 실행될 경우 딱 한번만 실행되도록 막아주면 된다!

그리고 테스트를 하기위해서 서버를 껏다 켜야 할 경우에

express서버는 node app.js로 실행하고
리액트 서버는 npm start인 것을 까먹지말자

useEffect로 감싸줄 때 마지막에 ,콤마 [] 빈배열을 넣어주면

딱 한번만 실행되도록 해줄 수 있다!

이제 서버로부터 받은 데이터를 map형태로 뿌려주면 되는데

h1태그 바로아래 map을 돌려서 div형태로 뿌려보자

{todoList.map((todo)=>(
        <div key={todo.id}>
          <div>{todo.id}</div>
          <div>{todo.text}</div>
          <div>{todo.done? "Y" : "N"}</div>
        </div>

이런식으로 넣어주는데

map으로 돌리고 todo라고 지어주고

map은 항상 key값이 필요함으로 key값은
항상 같은 id를 넣어주고

서버에서 지정된 이름과 같은

todo.id
todo.text
todo.done

이렇게 뿌려주는데 done은 boolean타입이니까

삼항연산자를 써서 트루이면 Y를 false면 N을 출력하도록 코드를 짜주면된다

그러면 결과가

TODO LIST
1
할일 1
N

이렇게 출력된다

항상 코드를 짤 경우 이렇게 짜야한다는 것을 명심하자

자 이번에는 데이터를 추가하는것도 해보자

데이터를 추가하기위해서 늘 그렇듯 form태그를 하나 만들어주고

form태그안에 input창 2개를 만들건데

하나는 text를 적는 것 다른 하나는 체크박스를 만들어주고
추가라는 버튼을 input창을 만들어서 기본 value를 '추가'라는 텍스트를 넣어서

버튼처럼 보이게해서 클릭하면 데이터를 넘겨줄 생각이다

여기서 type에 submit을주고 form태그에서 onSubmit을 이용해보자

form태그에서 onSubmit을 사용하려면

함수하나를 만들어줘야한다

const onSubmitHandler=(e)=>
const text = e.target.text.value;
    const done = e.target.done.value;
    fetch('http://localhost:4000/api/todo',{
      method: 'POST',
      body: JSON.stringify({
        text,
        done,
      }),
    });
    

이렇게 작성을 해주는데 코드를 하나씩 뜯어서 설명을 해보면

우선 함수하나를 만들고 ()안에 e를 넣어서 이벤트가 호출되게 만든다

그리고 그 안에 첫번째 텍스트에서 작성한 벨류가 들어오게 e.target.text.value라고 적어주는데

input창 첫번째 녀석을 보면

<input name="text"/> 이렇게 네임을 적어줬기때문에

이 네임에 맞는 텍스트를 이벤트로 지정할 수있다

그리고 두번째도 마찬가지로 2번째 input창을 보면

<input name="done" type={"checkbox"}/>이렇게 작성했는데

마찬가지로 이름이 done인 녀석을 찾아서 그 타입이 체크박스인것을 e.target으로 잡아줬다
이제 이 작성된 데이터를 다시 서버에 던져줘야한다

받았을 때와 마찬가지로 fetch를 이용해서 던져주는데 던질 주소가

4000포트이니까 똑같이 주소를 적어주고
get방식이 default이고 우리는 post로 보다 안전하게 던지기위해

method를 post로 지정해준다

그리고 body로 담아서 던질 생각이니까

body 안에 자바스크립트 값을 json 문자열 형태로 직렬화를해서
text와 done을 잘 담아서 던졌다

딱히 응답을 받아서 무언가를 할 생각은 없기 때문에

요청만 간단하게하고 끝내볼 생각이다

이제 결과가 어떻게 나올까?

입력해서 추가해보자

아무값이나 넣고 추가버튼을 누르면

주소창 / 뒤에 내가적은 텍스트가 그대로 노출되는 것을 볼 수 있다

form태그 sumbit을 클릭하면 기본동작이 있는데 바로 새로고침이 되면서

url에 내가 던진값이 노출되는 것이다

더 자세하게 설명하면 / 뒤에 query-parameter에 name이랑 값을 넣고 GET요청을 보내버린다

그래서 우리는 이것을 처음에 막아줄 필요가있는데

바로 함수 제일 앞에 e.preventDefault(); 이렇게 적어주면된다

이제 새로고침을 하고 값을 넣고 추가버튼을 누르면

눌렸는지도 모르게 된다 왜냐하면 GET요청을 막으면서 url에 값이 찍히지않을뿐 아니라
새로고침 깜빡이는게 사라져서 얼핏보면 제대로 눌렸는지 알 수가 없다

그럼 우클릭을해서 network를 눌러서 값을 넣으면 어떻게 들어오는지를 통해서

우리가 요청한것이 잘 처리되었는지 확인할 수 있다

이렇게 초록불 나오면 정상적으로 값이 들어간 것이다

그리고 네트워크에서 Headers옆에 payload를 누르면

이렇게 데이터가 어떻게 들어왔는지 더 상세하게 볼 수 있다

그런데 boolean값으로 넣은 녀석이 on이라고 나와있는 것도 이상하고

새로고침해서 나오는 출력된 값도 제대로 넘어는 오는데 전부 넘어오지않고 특정값만 넘어와서

잘못된게 있다

이걸 지금부터 바로잡아봐야한다

어떤데이터가 넘어오고있는지 전부 열어보려면 그 네트워크에서 탭3번째 response를 누르면되는데

현재 데이터가 어떻게 넘어왔는지 기록을 보니까

[{"id":1,"text":"할일 1","done":false},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15},{"id":16},{"id":17}]

이런식으로 데이터가 넘어왔는데 처음에만 제대로 아이디와 텍스트 그리고 true/false이고

그 뒤에는 전부 id값만 줄줄이 들어와있는 것으로 확인이 되었다

문제를 파악했으니 이제 해결을 해봐야한다

서버에서 body를 잘 꺼내쓰지 못하는 것 같다

그럼 서버에서 데이터가 잘 넘어가는지 확인부터해보자

app.post('/api/todo', (req, res)=> {
    const { text , done } = req.body;
    console.log("req.body : " , req.body);
    todoList.push({
        id: id++,
        text,
        done,
    });
    return res.send('success');
});

이렇게 app.js로 가서 console을 직접 찍어서 어떻게 넘어오는지 확인 할 수 있다

다시 서버를 껏다 켠 후에

test라는 텍스트를 담고 체크박스에 체크를 하고 추가를 누르고

vscode 터미널 창에 어떻게 찍히는지 확인해보자

req.body : {}
이렇게 바디안에 아무것도 담기지 않았다는 것이 확인되었다 !

그렇다면 바디를 못읽어오니까 headers에 컨텐츠 타입을 넣어서
다시 던져보자

이걸 넣고 아무리해도 빈값만 넘어와서 20분을 날렸다...

알고보니 cation을 caton으로 적었던 것이다

역시 복사/붙여넣기가 최고인 것을 다시 느끼는 순간이였다

이제 다시 새로고침을 해보면

req.body : { text: 'www', done: 'on' } 이렇게 콘솔에 잘 나온다

하지만 done에 on이라고 찍히는게 좀 별로다

이것도 바꿔주자

값을 던질 때 done.value가 아니라 체크여부를 보는것이고

받아서 뿌릴 때 참이면 T 거짓이면 N으로 뿌려지도록 이미 코드를 만들었으니

done.checked로 적어서 서버에 던져주자

그리고 다시 체크하고 던져보고 체크안하고 던져보자

체크하면 true 안하면 false 잘 넘어온다

보기에 너무 별로니까 약간의 css만 추가해보고 끝내도록 하자

맵으로 뿌려지는 부모 div에 display flex를 줘서 가로로 나열시켰다

근데 생각해보니까 데이터를 추가하면 바로 추가가 되어서 출력이 되어야하는데

서버에는 들어가는데 새로고침을 하지 않으면 데이터가 출력되지는 않는다

추가버튼을 눌렀을때 fetch를 이용해서 우리는 데이터를 보냈지만

다시받아오는 코드를 만들지는 않았다

그러면 여기서 받아오는 코드를 만들어보자 then을 쓸껀데

그렇게 넣어주면 추가버튼 누르자마자 출력도 바로바로 된다

하지만! 아주 큰 문제가 있다 아래 코드를 보자

useEffect (()=>{

  fetch('http://localhost:4000/api/todo')
  .then((response)=> response.json())
  .then((data)=>setTodoList(data));
},[]);

  const onSubmitHandler=(e)=>{
    e.preventDefault();
  
    const text = e.target.text.value;
    const done = e.target.done.checked;
    fetch('http://localhost:4000/api/todo',{
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        text,
        done,
      }),
    }).then(()=>{
      fetch('http://localhost:4000/api/todo')
  .then((response)=> response.json())
  .then((data)=>setTodoList(data))
    });
  };

위에서 받아오고 데이터를 추가하고 추가하면 다시 받아오고

이게 어떤 상황인가?

개발자라면 누구나 피하고싶은 나쁜코드

같은 코드를 반복하는 아주 나쁜 코드가 생성된 것이다

이렇게되면 가독성도 떨어지고 최적화에도 아주 나쁜 상황이다

여기서 우리는 함수를 하나 생성해서

그 안에 fetch를 담아서 그 함수만 넣어서 실행할 수 있다

const fetchData=()=>{
    fetch('http://localhost:4000/api/todo')
  .then((response)=> response.json())
  .then((data)=>setTodoList(data));
  }
  

이렇게 담아주고

원래있던 자리에 복잡한 코드를 다 지우고

fetchData(); 이거 한줄로 대체하면 된다

이제 실행해보면 이전과 똑같이 잘 실행된다

자 이제 대망의 행복전도사 axios를 써보자

axios는 fetch보다 훨씬 쉽고 더 편리하다

단 설치를 해줘야하니까 설치부터 해보자!

서버를 잠시끄고 npm i axios 적고 엔터!

설치가 끝났으면 이제 배워보자 사용법을 !

우선 axios를 사용하기에 앞서

url이 반복되니까 url도 함수로 바꿔버리자

const SERVER_URL = 'http://localhost:4000/api/todo';
이렇게 수정해주고

const fetchData= async()=> {
    const response = await axios.get(SERVER_URL);
    setTodoList(response.data);
    
  //   fetch('http://localhost:4000/api/todo')
  // .then((response)=> response.json())
  // .then((data)=>setTodoList(data));
  };
  

이렇게 수정해주는데 아래 코드는 json형태로 바꾸는 것을 선언도 해줘야하고
then을 두번이나 써줘야하지만

async와 await을 이용해서 더 깔끔하게 코드를 짤 수 있고

기존에 복잡한 url대신 함수를 넣어서 정리해줬다

이제 추가하는 곳도 바꿔보자

const onSubmitHandler = async (e)=>{
   e.preventDefault();
   const text = e.target.text.value;
   const done = e.target.done.checked;
   await axios.post(SERVER_URL, {text,done});
   fetchData();
   // fetch('http://localhost:4000/api/todo',{
   //   method: 'POST',
   //   headers: {
   //     'Content-Type': 'application/json',
   //   },
   //   body: JSON.stringify({
   //     text,
   //     done,
   //   }),
   // }).then(()=>
   //   fetchData());
 };

이렇게 아래 주석으로 된 곳에 비해 훨씬 간결하게 코드를 짤 수 있는데

동일하게 async와 await을 이용해서 코드를 짰고

method도 보다 편리하게 .get .post 로 편리하게 선언할 수 있고

헤더나 바디로 적어줬던 복잡한 코드도

필요없이 뒤에 그냥 {} 안에 원하는 데이터를 넣어만 주면 된다

이 얼마나편리한가!

이렇게 실행하고 모듈을 못찾는다는 에러가 나왔는데

클라이언트와 서버 둘 다 axios를 설치했어야하는데

한쪽에만 설치해서 모듈을 찾지 못했던 것이다

백과 프론트 통신을 axios로 하는거면 둘다 설치를 해야 사용이 가능하다

비순수이기때문에 fetch와 달리 설치가 필수적이다

이렇게 사용을 해봤지만 내것으로 만들기에는 부족하다

await와 fetch axios 그리고 async까지 제대로 알 필요가 다분하다

내일은 이것들에 대해서 더 알아보고 통신을 더 자주해보자

오늘은 여기까지!

오늘 순공부시간이 궁금해서 측정을 해봤다

열심히했다고 생각하는데 화장실가는시간 잡담시간 잠깐이라도 개발관련없는 행위를 하면

스탑을 누르면서 열심히 측정했는데 화면 사이즈만지다가 중간에 꺼버렸다...

그래도 다합치면 10시간10분정도했고 깨어있는 시간중에

점심 저녁 먹는시간 포함 3시간50여분 정도 쉬었네

더 열심히 분발하자 오늘은 진짜 여기까지 ! 벌써 새벽1시다 자야지...zzz

profile
몰입이 즐거운 개발자

0개의 댓글