[React] 14. 리액트에서 서버와 통신하는 법 ! Ajax

지렁·2023년 11월 7일
0

리액트에서 서버와 통신하기 위해선 ajax 방식을 사용하는데
우선 서버와, 요청방식에 대해 간략히 정리해보겠다

서버란?

데이터를 요청하면 데이터를 전송해주는 간단한 프로그램

  • 어떤 데이터인지 기재 -> URL로
  • 어떤 방법으로 요청할지 기재 -> GET/POST

GET? POST?

지금까지 get은 데이터 조회, post는 데이터 수정 이런식으로 암기하였지만 이제 서서히 이해가 가기 시작하였다

GET

  • 말 그대로 데이터를 get 하는 것이다
  • 당연히 수정, 업데이트는 불가하고 그냥 받기만 가능
  • 요청방법 : url을 브라우저 주소창에 입력하기

POST

  • 블로그를 포스팅하다의 그 post 이지 않을까?
  • 무언가 변경사항을 post 할 때 post 방식을 쓰는 듯 하다
  • 요청방법 : <form action="요청할url" method="post">

하지만 GET/POST의 단점은 브라우저가 새로고침이 된다는 것이다
🚫 리액트는 브라우저 새로고침을 권장하지 않는다
📢 그래서 사용하는 것이 AJAX 이다



🖤 AJAX

서버에 GET, POST 요청을 할 때 새로고침 없이 데이터를 주고받을 수 있게 도와주는 간단한 브라우저 기능

◾ AJAX 사용방법

  1. XMLHttpRequest라는 옛날 문법 쓰기
  2. fetch() 라는 최신 문법 쓰기
  3. axios 같은 외부 라이브러리

3번 axios을 사용하여 ajax를 구현 !

우선 설치를 한다

npm install axios 

💪 다음 버튼 클릭 시 서버로 ajax 요청

버튼을 클릭하면 서버에 신발 데이터를 요청해서 받아오고 페이지에 띄워보겠다
URL : https://codingapple1.github.io/shop/data2.json

  • GET요청을 하면 상품 3개를 가져오기
<button onClick={()=>{
    axios.get('https://codingapple1.github.io/shop/data2.json')
      .then((res)=>console.log(res.data))
      .catch(()=> console.log('요청 실패'))
  }
}}>다음</button>
  • axios.get(url) 을 통해 해당 url로 get 요청을 할 수 있고
  • 성공하면 .then의 파라미터를 통해 요청 결과를 받아온다 => res.data로 가져온 데이터만 확인 가능
  • 실패하면 catch 문을 실행해준다

💪 버튼을 2번 누르면 7,8,9번 상품을 가져와서 html로 보여주기

클릭한번 했을때 3개의 데이터 추가를 완료했었다
이번에는 한번 더 클릭했을 때 또 3개를 받아와서 추가해보겠다
URL : https://codingapple1.github.io/shop/data3.json

우선 클릭 횟수를 저장하는 state가 필요할 듯 하다

 let [click,setClick]=useState(0);
   return(
   ...
 <button onClick={()=>{
      setClick(click+1)
      if(click==1){
        axios.get('https://codingapple1.github.io/shop/data2.json')
          .then((res)=>{
            let newData=[...shoes,...res.data]
            setShoes(newData)})
          .catch(()=> console.log('요청 실패'))
      } else if (click===2){
        axios.get('https://codingapple1.github.io/shop/data3.json')
          .then((res)=>{
            let newData=[...shoes,...res.data]
            setShoes(newData)})
          .catch(()=> console.log('요청 실패')) 
      }
    }}>다음</button>

🚨문제발생!

click=1일때 로드하도록 조건문작성을 하였는데 클릭을 2번해야 데이터가 로드된다..?

click state 는 버튼 클릭하면 setter 함수를 통해 click+1로 변경되는데 이는 비동기적으로 작동한다

setClick 함수를 호출하고 나서, 해당 상태(click 상태)가 즉각적으로 업데이트 되지 않는다
대신에, 리액트는 상태 업데이트를 예약하고, 다음 렌더링이 일어날 때 새로운 상태 값을 사용하여 컴포넌트를 업데이트한다

예를들어

setClick(click + 1);
console.log(click);

윗줄은 click 상태를 현재 값에서 +1 증가시키도록 예약하지만(다시 렌더링할때 업데이트 됨)
밑줄은 아직 상태 업데이트가 되지 않았기 때문에 +1이 되지 않은 이전 상태의 click 값을 출력한다

👌 이럴 때 사용할 수 있는 것이 useEffect 이다

click 상태 값이 바뀔 때마다 실행시키고 싶은 코드를 useEffect에 입력하고 dependency에 click 을 입력하면

click 이 바뀔때마다 console.log 출력해준다

useEffect(() => {
  console.log(click);
}, [click]);

이제 출력되는 click의 값은 업데이트 된 click값이 된다!

그래서 나는 데이터 요청 코드를 이곳으로 옮겼다

function Main(){
  let [shoes,setShoes]=useState(data);
  let [click,setClick]=useState(0);
  let [isLoading,setIsLoading]=useState(false)

  useEffect(()=>{
    if(click===1){
      setIsLoading(true)
      axios.get('https://codingapple1.github.io/shop/data2.json')
        .then((res)=>{
          setShoes((prev)=>[...prev,...res.data])
          setIsLoading(false)})
          
        .catch(()=> {
          console.log('요청 실패')
          setIsLoading(false)})
    } else if (click===2){
      axios.get('https://codingapple1.github.io/shop/data3.json')
        .then((res)=>{
          setShoes((prev)=>[...prev,...res.data])
          setIsLoading(false)})
        .catch(()=> {
          console.log('요청 실패')
          setIsLoading(false)}) 
    }
  },[click])

setter함수로 click 값이 변경할때마다 조건문으로 click 을 바로바로 검사해주기 때문에
1번 클릭 시 data2.json이
2번 클릭 시 data3.json이 잘 로드된다!

다시 말하지만 useEffect를 사용하지 않고 onClick안에서 setClick(click+1)바로 밑부터 코드를 작성한다면 변경된 click 값대로 조건문이 실행되지 않는다

💪 버튼을 3번 누르면 버튼 비활성화

& 연산자를 통해 조건을 만들어서 true 일때만 html을 보여주는 방식을 사용하였다

{click <2 && <button onClick={()=>{
     setClick((prev)=>prev+1) 
   }}>다음</button>}

💪 버튼을 누른 직후엔 "로딩중입니다" UI띄우기

  • 요청이 성공하거나 실패하면 없애고 성공 직전에만 띄우기

isLoading 이라는 state로 ajax 내에서 로딩관리를 하였다

 let [isLoading,setIsLoading]=useState(false)
 
 return(
   ...
{ isLoading && <p className="alert alert-danger"> 로딩중입니다</p>}


🖤 추가 작업 !

🤔 추가로, 한줄에 상품 3개만을 표시하려면?

현재 리액트 부트스트랩의 그리드시스템을 사용하고 있는데
다음버튼을 클릭하여 상품이 추가되어 렌더링이 되면 한줄에 계속 추가되는 문제가 있었다

그래서 <Col> 태그의 속성을 추가였다

12를 기준으로 하여 계산하는 듯하다
xs={1}은 한줄에 12개
xs={2}은 한줄에 6개
xs={3}은 한줄에 4개 등등

function Card({data}){
return(
  <>
    {data.map((item,i)=>(
      <Col key={i} xs={4}>
          <Link to={`/detail/${i}`}>
          <img src={`https://codingapple1.github.io/shop/shoes${i+1}.jpg`} alt="img" width="80%" />
          <h4>{item.title}</h4>
          <p>{item.price}</p>
         </Link>
         </Col>
      ))}
   </>
)
}

🤔 배열의 카피본 만들어서 추가하는 방식 말고 더 편한 방법

.then((res)=>{
            let newData=[...shoes,...res.data]
            setShoes(newData)})

이처럼 지금까지는 기존배열을 보존하기 위해 새로운 배열을 만들어서 setter 함수로 배열 값을 바꾸는 방식으로 진행하였다

하지만 setter 함수로 한번에 가능하다

어떻게?

setter 함수의 파라미터에는 그 전의 state 남아있다
즉 파라미터= shoes가 되는 것이다

그래서 바로 setter 함수에서 값 변경을 할 수 있는 것이다

.then((res)=>{
            let newData=[...shoes,...res.data]
            setShoes((prevShoes)=>[...prevShoes,...res.data]})

Main에는 추가 데이터가 뜨는데 왜 Detail에는 추가데이터가 존재하지 않지?

Main 컴포넌트에서 shoes state를 만들고 data를 이쪽에서 관리했기 때문!
state로 굳이 만든 이유는 state가 변경될 때마다 새로 렌더링해주는 리액트 특성을 이용하기 위함이었는데 이 shoes는 main컴포넌트에서만 사용되기 때문에 나는 data.json에 get 요청으로 받아온 데이터를 push 해주었다

 if(click===1){
      setIsLoading(true)
      axios.get('https://codingapple1.github.io/shop/data2.json')
        .then((res)=>{
          data.push(...res.data)
          setShoes((prev)=>[...prev,...res.data])
          setIsLoading(false)})
          
        .catch(()=> {
          console.log('요청 실패')
          setIsLoading(false)})

위의 코드처럼 setShoes 값이 변경되는 곳에서 data.json도 같이 변경시켜주면 된다

profile
공부 기록 공간 🎈💻

0개의 댓글