요약

JWT로그인을 구현해보면서 클라이언트에 어떻게 저장시킬지 고민했고 redux를 사용하고자 했습니다. 하지만 axios 설정과 redux hook을 사용하면서 문제가 발생했고  해결했던 과정을 정리하였습니다

 

 

https://github.com/axios/axios

https://www.npmjs.com/package/jsonwebtoken

 

JWT인증 방식을 구현하던 중 클라이언트에 토큰 값을 어떻게 관리할지 고민을 하게 되었습니다. 사용자 쿠키에 저장하는 등 여러가지 방법이 있었는데 그 중에서 Redux를 활용하여 사용자 토큰을 관리하는 동시에 현재 로그인 상태를 확인하는 기능을 구현하고자 했습니다. 그렇게 구현을 하던 중 axios(서버로 요청을 보내고 받을 수 있는 모듈)의 코드가 중복되는 부분을 피하고 관리하기 쉽게 하고자 axios 공식 문서를 참고하여 구현하였습니다.

 

구현 과정

기존 코드

redux로 단순한 로그인 정보 확인과 axios 자체를 이전에 사용했던 부분에서 가져와 단순한 복사붙여넣기로 구현했습니다

 

// login.js
       axios.post(`http://localhost:5000/users/login`, data) //로그인 정보 전송하고 값을 받음
        .then(function (response) {
        // handle success
        alert(response.data.message);
        if(response.data.loginSuccess) { // true, false
            setuserLogin(response.data.loginSuccess)
            dispatch(user(true)); //토큰값이 저장되어야 함
        }
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        })
        .then(function () {
            // always executed
        });
        
// signup.js
     axios.post(`http://localhost:5000/post/add`, data)
      .then(function (response) {
      // handle success
          alert("저장되었습니다");
      })
      .catch(function (error) {
          // handle error
          console.log(error);
      })
      .then(function () {
          // always executed
      });

이때 axios 요청을 보내는 서버 주소를 변경, 관리하기 어렵고, 토큰까지 관리하게 된다면 토큰을 전송하는 코드를 매번 추가해야되기 때문에 많은 곳에서 중복코드가 발생하게 됩니다. 따라서 토큰, 서버 주소를 관리하기 위해 따로 파일을 만들어서 저장시키고자 했습니다

수정한 코드

// axios.js
import axios from"axios"
import { useSelector , useDispatch} from 'react-redux';
import { nowLogin as token } from "./features/userSlice";

const instance = axios.create({
    baseURL: 'http://localhost:5000' //default 주소로 설정함
  })

instance.defaults.headers.common['token'] = useSelector(token);  // #1
// 추가 코드없이 token을 헤더에 포함시킴

export default axios //#2


//login.js
const onSubmit = (data) => {
        axios.post(`/users/login`, data)
        .then(function (response) {
        // handle success
        alert(response.data.message);
        if(response.data.loginSuccess) {
            setuserLogin(response.data.loginSuccess)
            dispatch(user(response.data.token));
        }
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        })
}

이후 서버에서 auth를 통한 인증 구현을 하게 될텐데 그때마다 요청시 토큰을 헤더에 포함시키지 않고 보낼 수 있으며 추가적으로 중복적인 요청을 계속 발생시키게 된다면 axios.js에서 코드를 추가하여 사용할 수 있습니다  

 

문제 해결 과정

위 수정코드 #1, #2 문제가 발생했는데 #1은 어디서 에러가 나는지 알려주기 때문에 구글링으로 쉽게 찾을 수 있었습니다

스택오버플로우에서 쉽게 찾을 수 있었고 좋은 답변을 통해공식문서에서도 확인할 수 있었습니다

 

따라서 다시 돌아가보니 React Function이 아닌 일반 JS 파일에서 redux hook을 요청한 것을 알게 되었습니다. 따라서 우선 주석처리를 해주었고 로그인하는 곳에서 instance.defaults.headers.common['token'] = useSelector(token)을 옮겨주었습니다

//   axios.js
import axios from"axios"

//REACT hook 사용 불가 = redux를 사용할 수 없음
// import { useSelector , useDispatch} from 'react-redux';
// import { nowLogin as token } from "./features/userSlice";
//instance.defaults.headers.common['token'] = useSelector(token);  // #1

const instance = axios.create({
    baseURL: 'http://localhost:5000' //default
  })

export default axios //#2

 

수정하고나서 실행은 되었지만 토큰 관리가 안됐고 추가적인 에러가 발생하였습니다. 동작은 하는데 원하는 동작을 하지 않아서 어디서 에러가 발생하는지 찾는게 먼저였고 찾는 방법은 여러가지가 있을텐데 단순히 제대로 작동하고 있나 console로 출력해보는 것으로 확인해보았습니다                          

이때 axios 요청이 입력해놓은 서버 주소가 아닌 다른 곳으로 가고 있어서 어디가 문제인지 찾아보았습니다. (5000 포트가 아닌 3000으로 가고 있음)

 

alert로 테스트해보며 실제 동작을 확인해보았고 axios로 요청을 보내는 함수 자체에 문제가 아닌 module axios의 문제라고 생각하게 되었습니다

import axios from "../../axios" 


const onSubmit = (data) => {
        axios.post(`/users/login`, data)
        .then(function (response) {
        // handle success
        alert(response.data.message);
        if(response.data.loginSuccess) {
            setuserLogin(response.data.loginSuccess)
            dispatch(user(response.data.token)); //토큰값을 redux에 저장 , 로그인 확인
            axios.defaults.headers.common['token'] = response.data.token;
        }
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        })
}

이후 확인해본 결과 만들어놓은 axios.js가 아니라 node_modules에 있는 axios를 사용하게 되면서 따로 분리해두었던 코드가 사용되지 않는 코드로 간주되었던 것이었습니다. 따라서 axios의 기본적으로 설정되어 있는 default 주소로 전송되며 실행되지 않았던 것이 문제였고 경로를 수정하여 마무리 하였고 이후 정상 동작을 확인하였습니다

 

회고

이전에 한번 만들어봤던 코드를 통해 구현을 진행해서 실력이 부족했을 때 따로 신경쓰지 않았던 부분들을 또 다시 방치하게 되버릴 뻔 했으나 피드백을 통해 확인할 수있었습니다. 이후 발생한 두가지 문제에 대해서 구글링할 수 있는 것이 시간을 정말 줄여줄 수 있다는 것을 깨달았지만 #2처럼 눈에 보이지 않고 내가 원하는 동작을 하지 않는 코드를 수정하기 위해문제 해결 능력을 키우는 방법은 코드를 작성하는 흐름에 익숙해지는 것과 많은 문제들을 실제로 확인해봐야 생길 수 있다는 것을 깨닫게 되었습니다

복사했습니다!