이전 포스팅에서 로그인 성공 후 응답으로 돌아온 토큰을 로컬스토리지에 저장하는 것까지 성공했다.

이후 로그인이 된 상태를 유지해야 하고 로그인 여부에 따라 다른 UI를 보여줘야 한다.

많은 페이지의 UI를 다르게 띄워줘야하는데 페이지마다 로컬 스토리지의 토큰을 get 해와서 처리하는 것은 번거로울 것 같았다.

 

그래서 생각해낸 방법은 useContext를 사용해서 전역으로 로그인 상태를 관리하는 것이었다.

 

 

로그인 된 상태 true / 로그인 하지 않은 상태 false 로 처리해서 전역변수 처리를 한 다음

다른 UI를 띄워줘야할 때 true/false 값을 활용해 간단하게 처리할 수 있는 것이다.

 

해야할 것은 
1. LoginContext.jsx 생성하여 전역 변수로 선언해주기
2. 로그인하면 isLoggedIn 값을 true로 변경
3. 로그아웃하면 isLoggedIn 값을 false로 변경
4. 다른 컴포넌트에서 isLoggedIn 값 활용하여 다른 UI 띄우기 !

1. LoginContext.jsx 생성하여 전역 변수로 선언해주기

import React, { createContext, useState, useContext, useEffect } from 'react';

const LoginContext = createContext();

export const LoginProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    // localStorage에서 토큰을 가져와서 유무를 판별하여 isLoggedIn 상태 설정
    const token = localStorage.getItem('Token');
    setIsLoggedIn(!!token); // 토큰이 있으면 true, 없으면 false
  }, []);

  return (
    <LoginContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
      {children}
    </LoginContext.Provider>
  );
};

export const useLogin = () => useContext(LoginContext);

전역으로 사용할 isLoggedIn 변수를 useState로 관리한다.

그리고 처음 렌더링 될 때, 로컬스토리지의 토큰을 가져와서 상태를 true/false로 저장한다.


이후 전역적으로 로그인 상태를 관리하기 위해 app.js의 컴포넌트들을 <LoginProvider>로 감싼다.

<LoginProvider>을 사용하면 하위의 여러 컴포넌트에서 동일한 로그인 상태를 사용하고 업데이트 할 수 있게 된다.

function App() {
  return (
    <LoginProvider>
      <div className="App textFont">
        <Routes>
          <Route path="/user/*" element={<User/>} />
            <Route path="/admin/*" element={<Admin/>} />
        </Routes>
      </div>
    </LoginProvider>
  );
}
export default App;

2. 로그인하면 isLoggedIn 값을 true로 변경

import { useLogin } from '../../context/LoginContext';

const { isLoggedIn, setIsLoggedIn } = useLogin();

// 로그인
const handleLogin = async () => {
  try {
    console.log(studentInfo)
    const result = await API().post('/login', studentInfo); // 로그인 성공
    navigate('/user'); // 사용자 메인으로 이동
    localStorage.clear()
    localStorage.setItem('Token', result.data.accessToken)

    setIsLoggedIn(true);
  } catch (error) {
    // 에러처리
  }
};

로그인 컴포넌트에 와서 사용하겠다. 선언해준뒤 로그인이 성공했을 때 값을 true 로 변경한다.


3. 로그아웃하면 isLoggedIn 값을 false로 변경

로그인과 거의 동일하다. 값만 false로 변경하면 된다.

const handleLogout = () => {
    localStorage.clear()
    setIsLoggedIn(false);
    console.log(isLoggedIn)
}

로그아웃하는 함수


4. 다른 컴포넌트에서 isLoggedIn 값 활용하여 다른 UI 띄우기 !

메뉴바의 마이페이지 아이콘을 클릭했을 때

로그인 상태면 > 마이페이지

로그인 상태가 아니라면 > 로그인 페이지로 이동하게끔 했다.

const { isLoggedIn, setIsLoggedIn } = useLogin();
const navLink = isLoggedIn ? 'myPage' : 'login';

상태에 따라 navLink의 값이 다르게 들어가고

<Link to={navLink}><AiOutlineUser size={30} className='mr-2'/></Link>

요렇게 링크를 해주면 끝!

로그인 했을 때와 안 했을 때

로그인 로직

동아리연합회와 협업 프로젝트를 진행하고 있고 그 중 로그인 부분을 진행하고 있다.

 

이번 로그인은 몇가지 조건이 있다.

- 동아리 사람들만 로그인할 수 있다. (동아리 소속 아닌 사람들은 로그인 자체가 안됨)

- 처음 로그인 하는 동아리원들은 동의 수집 페이지를 거쳐야 한다.

 

위의 항목들을 만족하지 않으면 에러로 돌아오고 에러 처리를 해줘야 한다.

 

정리해보면,

로그인 시도(/login)를 했을 때
- 동아리원O, 동의O -> 로그인 성공(200) -> 메인으로 이동
- 동아리원O, 동의X -> 401 에러 -> 동의 항목 페이지로 이동 ->  동의하기(/agree) -> 로그인 재시도(/login) -> 로그인 성공(200)
- 동아리원X -> 400 에러 -> 로그인 불가능

// 로그인
const handleLogin = async () => {
  try {
    console.log(studentInfo)
    const result = await API().post('/login', studentInfo); // 로그인 성공
    console.log(result);
    navigate('/user'); // 사용자 메인으로 이동
  } catch (error) {
    if (error.response) {
      const statusCode = error.response.status;
      if (statusCode === 400) { // 400 : 학번 or 이름 틀렸을 경우
        setFailModalOpen(!failModalOpen);
        setStudentInfo({
          studentId: '',
          name: ''
        });
      } else { // 401 : 개인정보 동의하지 않았을 경우
        setModalOpen(!modalOpen);
      }
    }
  }
};

 

로그인을 성공했을 때는 navigate로 페이지를 이동 시켜주고

실패했을 때는 에러 코드에 따라 다른 처리를 해주었다.

에러코드 === 400 > 동아리 소속이 아닌사람 (동아리원X)

해당 창이 뜨면서 로그인 실패

에러코드 === 401 > 동아리 소속이지만 동의항목을 거치지 않은 사람(동아리원O, 동의X)

개인정보 수집이용 동의 모달창을 띄워준다.

이후 모달창에서 동의를 누르면 서버(/agree)로 동아리원 정보가 전달되고 서버에서 전달받은 동아리원의 isAgree 항목을 true로 변경함.

// 동의했을 때
const handleAgree = async () => {
    try {
        await API().post('/agree', studentInfo); // 성공적으로 처리되면
        handleLogin(studentInfo); // 다시 로그인 함수 호출
    } catch (error) {
        console.log(error)
    }
}

이후 다시 로그인 재시도(/login)를 하면 동아리원O, 동의O 이므로 로그인 성공(200) !


로그인 성공시 돌아온 토큰 저장

const result = await API().post('/login', studentInfo); // 로그인 성공
    navigate('/user'); // 사용자 메인으로 이동
    localStorage.clear()
    localStorage.setItem('Token', result.data.accessToken)

로그인을 성공하게 되면 서버에서 토큰을 발행해주는데 이 토큰을 어딘가에 저장해두어야 한다.

일반적으로 발행된 토큰은 로컬 스토리지에 저장된다.

 

localStorage.clear()로 한번 비워주고,

localStorage.setItem(키, 값)의 형태로 돌아온 토큰을 저장하면

잘 저장되는 것을 볼 수 있다 ! 야호

React

리액트 프로젝트 생성 : npx create-react-app 프로젝트명

리액트 실행 : npm start


React Native

ReactNative cli

리액트네이티브 프로젝트 생성 : npx react-native init 프로젝트명

ios 실행 : npx react-native run-ios

android 실행 : npx react-native run-android


expo cli

expo cli 설치 : npm install -g expo-cli

프로젝트 생성 : npx create-expo-app my-app

작년 11월 비상대책위원회와 협력하여 총선거 프로젝트를 진행했었다

이후 올해 3월 보궐선거로 변경해서 재진행 하기로 했다.

 

원래는 총선거 > 보궐선거로 모든 텍스트와 콘텐츠들을 다 바꿀 계획이었지만 아예 덮어버리기가 아쉬워서 총선거도 살리면서 보궐선거를 할 수 있는 방법이 없을까 ... 구상하다가 경로 분리를 해보기로 했다.

 

원래는 URL/voting, URL/candidate 이었다면

총선거 > URL/main-election/voting, URL/main-election/candidate

보궐선거> URL/by-election/voting, URL/by-election/candidate 형식으로

URL 하위 경로를 기준으로 완전히 페이지를 분리하려고 시도했던 것이다.

 

기존은 App.js에서 모든 경로를 라우팅하여 URL/ 을 시작으로 바로 하위에 경로가 바뀌는 방식이었다.

이와 조금 다르게 App.js에서는 

먼저 크게 두 분류로 main-election 총선거 / by-election 보궐선거로 분리한다음 각 컴포넌트 안에서 또 라우팅을 진행했다.

function App() {
  return (
    <BrowserRouter>
        <Routes>
          <Route path='/' element={<MainPage />} />
          <Route path='/main-election/*' element={<MainElection />} />
          <Route path='/by-election/*' element={<ByElection />} />
        </Routes>
    </BrowserRouter>
  );
}

App.js


MainElection 컴포넌트 하위에 한번 더 라우팅

이곳에서 라우팅된 것들은 이제 main-election/Election 형식으로 접근할 수 있는 것이다.

주의할 점은 path=내부에 '/election' 형식으로 적는 것이 아니라 path='election' 형식으로 / 없이 경로를 작성해야 한다.

function MainElection() {
  return (
      <div className="App Font_bombaram relative">
        <div div className="leftDate absolute top-16 left-1 verticalLeft text-3xl" >2024.11.20-11.22</div>
        <div className="rightDate absolute bottom-48 right-1 verticalRight text-3xl">2023.11.20-11.22</div>
        <MainNav />
        <Routes>
          <Route path='' element={<Main />} />
          <Route path='Election' element={<Election />} />
          <Route path='Candidate' element={<Candidate />} />
          <Route path='Map' element={<Map />} />
          <Route path='About' element={<About />} />
          <Route path='CandidateInfo' element={<CandidateInfo />} />
          <Route path='Voting' element={<Voting />} />
          <Route path='CandidateContent' element={<CandidateContent />} />
        </Routes>
      </div>
  );
}

export default MainElection;

MainElection.js


ByElection도 같은 방식으로 진행했다.

경로는 변경하지 않고 컴포넌트만 보궐선거로 보일 수 있도록 수정한 것이다.

function ByElection() {
  return (
      <div className="App Font_bombaram relative">
        <div div className="leftDate absolute top-16 left-1 verticalLeft text-3xl" >2024.03.25-03.27</div>
        <div className="rightDate absolute bottom-48 right-1 verticalRight text-3xl">2024.03.25-03.27</div>
        <ByMainNav />
        <Routes>
          <Route path='' element={<ByMain />} />
          <Route path='Election' element={<ByElectionPage />} />
          <Route path='Candidate' element={<ByCandidate />} />
          <Route path='Map' element={<ByMap />} />
          <Route path='About' element={<ByAbout />} />
          <Route path='CandidateInfo' element={<ByCandidateInfo />} />
          <Route path='Voting' element={<ByVoting />} />
          <Route path='CandidateContent' element={<ByCandidateContent />} />
          <Route path='PledgeBook' element={<PledgeBook />} />
          <Route path='CampaignVideo' element={<CampaignVideo />} />
          <Route path='Manifesto' element={<Manifesto />} />
        </Routes>
      </div>
  );
}

export default ByElection;

ByElection.js


위와 같은 방식으로 경로 설정은 성공했다. 

이후 보궐선거로 콘텐츠들을 바꿀 생각을 하니 ,,

총선거의 모든 페이지와 컴포넌트들을 동일하게 복사해서 보궐선거로 변경해야 하는 것일까 ........? 하는 의문이 들었다.

이건 너무 막노동인 것 같았고 조금 더 머리를 굴려보니 답을 얻을 수 있었다.

 

하나의 js 파일 안에 하나의 컴포넌트를 만드는게 일반적이다.

권장하는 것은 아닌 것으로 알고 있지만, js 파일 안에 컴포넌트 여러 개를 만들어도 된다.

이번 프로젝트의 경우 하나의 js 파일 안에 두 개의 함수형 컴포넌트를 만들어서 총선거 / 보궐선거로 구분하면 되겠다고 생각했다. 

모든 파일들 안에 By~와 같이 컴포넌트를 하나 만들어주고 보궐선거로 진행하기 위해 문구들을 바꾸어주었다.

이렇게 해서 도메인을 분리하지 않고, 라우팅을 다르게 해서 총선거, 보궐선거를 같이 볼 수 있게 했다.

 

리액트 프로젝트를 생성하면 이렇게나 많은 파일들이 자동으로 생성된다.

여기서 필요한 것과 불필요한 것을 나뉘어서 불필요한 것은 과감하게 삭제할 것이다.

나는 개발을 시작할 때 딱 필요한 파일들만 남겨두고 모두 삭제한채로 개발을 시작한다.

삭제한다고 해서 문제될 것 딱히 없기 때문이다

프로젝스 생성 시 상태


먼저 public 폴더에 index.html을 두고 다 삭제한다.

그리고 index.html 파일 내 코드들도 필요한 것만 남기고 삭제한다.

폴더 안을 보면 삭제하는 것들이 리액트png 로고 이런 것들이어서 딱히 상관없다는 것을 알 수 있다.

public 폴더 정리


src 폴더도 정리한다.

index.js, App.js 파일만 남기고 모두 삭제한다.

css 파일 같은 경우는 필요하다면 내가 직접 만들면 되기 때문에 전혀 상관없다!

 

파일들을 삭제하면 에러가 날 수 있는데 에러가 나는 코드들을 찾아서 그것들도 지워주면 된다.

파일들을 import 했는데 해당 파일을 삭제해서 경로를 찾을 수 없어 나오는 에러들일 것이다.

src 폴더 정리


index.html / index.js 기본 이해

index.html : 웹 브라우저에서 가장 먼저 실행되는 파일
index.js : index.html 열리고 나서 그 다음으로 실행되는 파일

 

index.html 파일에는 <body> 안에 id가 "root"인 <div> 태그가 있고

index.html

index.js 파일에서는 id가 "root"인 <div> 태그를 찾아서 React 루트를 생성

생성된 React 루트에서는 App 컴포넌트를 렌더링

index.js

이후 React 애플리케이션이 시작되고,

App 컴포넌트 내부의 다른 컴포넌트들이 트리를 형성하여 전체 애플리케이션을 구성

(컴포넌트는 뒤쪽에서 설명할 것임 ..!)

우리가 많은 컴포넌트들과 파일을 만들지만 결국은 index.html 안의 body 태그에 그려진다는 것

JSX : JavsScript 의 확장 문법

JSX 문법은 js에서 html문법을 편리하게 활용할 수 있는 문법

js의 확장이다보니 html 속성들을 활용할 때 주의해야할 부분들이 있다.

 

HTML과 다른 속성명

 

예를 들면

js, html에서 동일한 이름으로 사용하는 기능들이 있는데 대표적으로 for, class가 있다.

jsx는 js의 확장이다보니 이럴 때 html 속성을 조금 변경해서 사용한다.

 

  • class > className
<div className="App">

이런식으로 태그에 class명을 줄 때 className을 사용하는 것!

 

  • for > htmlFor
<label htmlFor="title"></label>
<input type="text" id='title'/>

js에서는 반복문 for이 있고, html에서는 label의 속성 for이 있다.

이러한 경우 html 속성을 htmlFor로 변경해서 사용한다.

 

  • 여러 단어가 조합된 속성들은 카멜 케이스로 작성
    onclick, onblur, onfocus 등과 같은 이벤트 속성들을 onClick, onBlur, onFocus 처럼 카멜 케이스로 작성

어렵진 않지만 헷갈릴 수 있는 것들.. 몇가지가 더 있지만 그건 개발하면서 그때 그때 알아가는 게 효율적일 듯 하다.

개발 환경 세팅

1. node.js 설치 (다운로드 왼쪽이 더 안정적인 버전) https://nodejs.org/en

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org


2. 프로젝트 생성 : npm init react-app <폴더이름>

npm init react-app . : 현재 폴더에 프로젝트를 생성하겠다. (vsCode열고 터미널에서 실행)


3. 프로젝트 시작(개발 모드 실행) : npm run start


4. 구조를 살펴보자

src / App.js

js 파일인데 html 코드가 섞여있다. hmm

리액트는 js 코드 안에 html 코드를 섞어쓴다.

이런 문법을 jsx 라고 함 !! 다음 포스팅에 설명


5. 개발 모드 종료

http://localhost:3000/ 로 계속 개발 실행중인 상태

CTRL + C로 개발 모드 종료!

죽었다!


6. 꿀팁!! 리액트 개발자 도구 설치하기

검색
크롬에서 확장자 설치
개발자 도구에 도구 추가 됨

미리 설치해두기~
지금은 생소하지만 나중에는 많은 도움이 될 것 ദ്ദി( ◠‿◠ )

'주문하기' 페이지에서 사용자가 주문정보를 모두 입력하고 '주문하기'를 누르면! 서버로 주문자의 정보가 넘어간다.

주문하기 페이지

 

이후 나오는 페이지는 '주문완료' 페이지'주문자정보 + 주문번호, 결제만료일 등의 추가 정보가 필요하다.

 

서버에서 받은 데이터들을 완료페이지에 뿌려주어야하는 것이다.

그냥 뿌려주는 것이 아니라 받은 데이터를 다음페이지로 가지고 가서 뿌려주어야했다. 

useNavigate, useLocation를 활용해서 데이터를 전달했다. 그리고 생각보다 간단했다.. 

1. POST 메서드를 통해 비동기 방식으로 서버에 데이터 전달

먼저 주문자가 입력한 정보들을 서버에 전송해야 한다.

        try {
            const response = await APIClient().post('/orders/', formData);
            if (response.data) {
                const responseData = response.data
                navigate('/orderSuccess', { state: { responseData } });
            } else {
                throw new Error(`오류 : ${response.status}`);
            }
        } catch (error) {
            console.error(error);
        }
 

Axios를 사용하여 비동기적 방식으로 서버에 POST 요청을 보내고, 응답을 기다린다.

/orders/ 엔드포인트로 formData를 전송하고, 서버의 응답에 따라 다른 처리를 하도록 한다.

서버의 응답이 존재한다면, 응답 데이터는 responseData에 저장되고,

navigate함수를 통해 /orderSuccess 페이지로 이동한다.

 

아래 코드는 Axios를 사용하여 HTTP 요청을 생성하는 함수를 정의한 것이다.

axios.create() 메서드를 사용하여 Axios 인스턴스를 생성하고, 이를 통해 기본 URL과 헤더를 설정한다.

    import axios from 'axios';

    export const APIClient = () => axios.create({
        baseURL: 'https://www.petinuniverse.com/',
        headers: {
            'Content-Type': 'application/json',
        }
    })
 
  1. baseURL: 'https://www.petinuniverse.com/',: Axios 인스턴스의 기본 URL을 설정한다. 이 기본 URL은 나중에 이 인스턴스를 사용하여 보낼 모든 요청의 기본 주소가 되는 것이다.
  2. headers: { 'Content-Type': 'application/json', }: Axios 인스턴스의 기본 헤더를 설정합니다. 여기서는 요청의 컨텐츠 타입을 JSON으로 설정한다.

이렇게 설정된 Axios 인스턴스는 APIClient 함수를 호출함으로써 반환된다.

APIClient 함수를 한번 정의해두면 기본 URL 및 헤더를 가진 일관된 설정을 유지하면서 여러 요청을 쉽게 수행할 수 있어 간편하다.

 

2. useNavigate 훅을 사용해 응답 데이터와 함께  페이지 이동

위의 코드에서 서버로 보낸 응답이 존재할 때, 응답데이터를 responseData에 담는 것을 보았다.

responseData에는 주문완료 페이지에 뿌려줄 데이터들이 담겨있다. 이 변수를 가지고 다음페이지에 넘어가야한다.

 

useNavigate React Router에서 제공하는 훅으로,
함수형 컴포넌트 내에서 프로그래밍 방식으로 다른 경로로 이동할 수 있게 해주는 역할을 합니다.

 

사용법은 간단하다.

1. navigate 함수를 사용하기 전 useNavigate 훅을 사용하여 navigate 함수를 얻어온다.

const navigate = useNavigate();

 

2. navigate 함수 사용

 navigate('/orderSuccess', { state: { responseData } });
  • 첫 번째 인자 ('/orderSuccess'): 이동하고자 하는 경로를 나타낸다. 페이지가 /orderSuccess로 이동하게 된다.
  • 두 번째 인자 ({ state: { responseData } }): 옵션 객체로, 이동할 때 함께 전달하고 싶은 데이터를 설정한다. 대표적으로 state라는 키를 사용한다.

이렇게 설정하면 /orderSuccess로 이동할 때 { responseData }라는 데이터가 함께 전달되며,

이 데이터는 이동한 페이지에서 location.state를 통해 접근할 수 있게 된다.

3. 이동한 페이지에서 useLocation으로 데이터에 접근하기

자 이제 서버의 응답도 받았고, 응답데이터와 함께 페이지 이동까지 완료했다.

이제 이동된 페이지인 orderSuccess에 응답데이터를 뿌려주어 주문완료 페이지를 완성하면 된다.

 

useLocation은 React Router 라이브러리에서 제공하는 훅으로
현재 애플리케이션의 경로(location) 정보를 가져올 때 사용된다.

이 훅을 사용하면 현재 URL과 그와 관련된 정보에 접근할 수 있습니다.

  • location.pathname: 현재 경로에 대한 문자열을 반환
  • location.search: 현재 쿼리 파라미터에 대한 문자열을 반환
  • location.state: navigate 함수를 통해 전달된 상태(state)에 대한 정보가 담겨 있다.

우리의 위의 3개 중 location.state를 활용해 navigate함수를 통해 전달된 상태인 responseData를 얻으면 된다!

 

1. useNavigate와 마찬가지로 useLocation 훅을 사용하여 현재 경로의 정보를 가져온다.

const location = useLocation();

 

2.  그 정보 중에서 state 속성을 사용하여 데이터를 추출

  const responseData = location.state?.responseData || {};

Optional Chaining (?.)을 사용하여 location.state가 정의되어 있을 때에만 responseData를 추출하고, 그렇지 않으면 빈 객체로 설정한다. 

3. 추출한 데이터 화면에 렌더링하기

 <p>{responseData.cust_name} / {responseData.phone_number} / {responseData.email}</p>
 <p>{responseData.fullAddress} {responseData.detailAddress} </p>

위와 같이 페이지에서 전달받은 responseData의 속성들을 사용하여 사용자에게 필요한 정보를 표시하는데 활용하면, 끝!

 

결제완료 페이지

 

PIU 창업동아리 캠페인 플젝으로 웹사이트를 만들고 있다

굿즈 주문하기 > 주문완료 페이지로 넘어가는 중에 에러가 ~~~

AxiosError 400 Bad Request라고 나왔다

이 오류는 클라이언트 측에서 보낸 요청이 서버에서 처리할 수 없거나 잘못된 경우 발생한다고 한다

 

백엔드 api 정의서와 내가 보낸 값들을 비교해봤다. 역시나 잘못된 부분이 ㅎㅎ

const formData = {
            "cust_name": name,
            "email": email,
            "phone_number": phoneNumber,
            "cust_pwd": passwordConfirm,
            "address": address,
            "product_info": productInfo,
            "agree": agree,
            "depositor_name": depositorName,
        }

내가 보내는 값들이다.

잘못된 부분

1. agree type 불일치

agree 값이 bool 값이어야 하는데 나는 문자열로 'agree' / 'disagree'를 보내고 있었다. 

이 부분은 백엔드랑 소통하기 전에 임의로 처리해둔 거였는데 수정한다는 것을 잊고 있었던. . 나는 바보~

2. address 객체의 필드 불일치

내가 보내고 있던 값
백에서 요청한 값

address 객체에 담긴 필드가 3개여야 하는데 4개가 보내지고 있었다. 콘솔로 찍어보니 4개가 짜잔 ^^..

 

사실 콘솔로만 이것저것 찍었을 때는 왜 에러가 나는지 잘 몰랐는데 postman을 사용해보니 어디가 잘못된 부분인지 확실하게 알 수 있었다.

올바른 request

이렇게 필드가 3개일 때 status 201로 올바른 요청과 응답이 돌아오는 것을 볼 수 있다.

bad request.. ~

하지만 이렇게 address 객체 필드가 4개일 때 400 Bad Request가 뜨는 것을 볼 수가 있었다.

요청을 보내는 값들 하나하나 신중하게 확인해야 한다는 것을.. 그리고 백엔드와의 소통이 중요하다는 것을 ..

매번 느끼지만 또 또 느낄 수 있게 에러가 뿅하고 나타나주었다 따봉에러야 고마워 ~

 

Axios에러를 개발하면서 자주 봤는데 기록하지 않으니 그때 왜그랬더라.. 하며 같은 실수를 반복하는 것 같다

기록이 중요하다는 걸 새삼 느끼고,, 귀찮아도 자주 기록하려고 노력해야겠다

+ Recent posts