[TIL] 23.05.03.

2023. 5. 3. 01:25개발일기

오늘

지난주와 마찬가지로 이제껏 배운 리액트 내용을 바탕으로 게시판을 이어서 구현했다. 기본적인 CRUD는 react-query와 json-server를 통해 내부 DB로 간단하게 구현을 완료한 상태였으며 회원 가입과 로그인은 서버에 API로 구현하기로 했다.

KPT

Keep

  • 로그인, 회원가입 기능을 구현할 때 서버 API 명세서를 먼저 보고 작업했습니다. 명세서를 먼저 살펴보고 구현하니 어떤 요청을 보내야 하는지 명확해져서 비교적 간단하게 구현할 수 있었습니다.

Problem

  • 서버 요청이후 응답 에러 메세지에 따라서 적절한 에러메세지를 보여주기 위해서 컨트롤 하는게 어려웠다. axios로 서버와 통신할 때 요청, 응답 전에 interceptors를 구현한 다음 intercepter를 사용했다. 이때 instance는 axios 라이브러리의 create메소드를 사용해서 생성하였다. 요청 이후 에러가 발생하면 리턴으로 Promise객체를 reject해서 던져주면 interceptors 다음 단계인 jsx 파일에서 받아올수 있는데 그냥 에러 메세지만 리턴을 처리해서 jsx에서 정당한 응답으로 인식하는 문제가 생겼었습니다.
  • 사용자가 로그인/로그아웃 상태인가에 따라 접근할 수 있는 페이지를 달리 해야한는 요구사항을 구현한고 있었다. 로그인시 접근 가능한 페이지, 로그아웃시 접근가능한 페이지를 구분한 다음 각각의 페이지가 랜더링 될때 useEffect를 통해 쿠키에 토큰이 있는지 확인한 다음 토큰이 있으면 로그인을 한 사용자라고 판단하고 로그인후 이용 가능한 페이지로 이동하는 형태로 기능을 구현하였다. 이 과정에서 두가지 문제가 발생했다.
  • 첫번째는 로그인 여부에 따라 사용자에게 alert로 ‘이미 로그인된 사용장입니다. 메인 화면으로 이동합니다.’ 라는 형태의 안내메세지를 띄워주고 이동을 하는 과정에서 alert가 여러번 나타나는 문제가 발생했다.
  • 두번째는 토큰이 위조될 경우를 생각해서 위조되거나 탈취되거나 만료등으로 요청 중간에 문제가 생기면 검증을 한다음 세션이 종료되었으니 재 로그인을 안내해주고 혹시 위조 토큰일 수있으니 쿠키를 지워주고 로그인 페이지로 이동시켜주는 과정에서 발생했다. 매번 인터벌로 여부를 체크할 수 없기 때문에 판단의 기준점은 사용자가 새로운 요청을 해서 컴포넌트가 재 랜더링 되었을때로 하였다. useQuery를 통해 쿠키의 토큰을 서버에 보내서 검증로직을 거치고 그에 따른 결과값이 isError가 트루가 나오면 세션을 만료 처리하는 형태로 하였다. 하지만 이렇게 하니까 중간에 토큰의 문제가 발생해서 사용자에게 안내해주고 재로그인을 위해 로그이 페이지로 이동한 다음 다시 정상적으로 로그인을 하게 되면 최초 로그인 때도 false로 판단하고 재로그인을 요청하는 상황이 생겼다.
  • 배포하는 과정에서 문제가 생겼다. api를 통해 토큰을 받고 사용자를 등록하는 곳은 http 프로토콜을 가진 외부 서버였는데 현재 프론트단의 프로젝트가 돌아가고 있는 서버와 도메인이 일치하지 않고, https, http 프로토콜을 모두 호출하고 있어서 CORS오류와 Mixed context에러가 발생했다.
  • 두 문제를 해결하기 위해 가장 정확한 방법은 백앤드와 서버단을 수정하는 것이엇지만 이미 주어진 서버를 변경할수 없어서 고민이었다.

Try

  • 처음에는 응답 결과에서 메세지를 확인해서 하나하나 분기 처리해주었는데 아무리 생각해도 좋은 방법이 아닌 것 같았다. 이것처럼 간단한 게시판은 문제가 크게 없겠지만 개발자가 예상하지 못한 에러가 발생할 경우 로직에서 처리를 하지 못하고 정상으로 처리되는 경우가 충분히 발생할 수 있을 것 같았다. 분명히 오류가 발생함에도 오류를 인식하지 못하는 이유에 대해서 쫓아가며 디버깅을 해보니 jsx단에서 React-query의 useMutation을 사용하여 요청을 하고, 해당 요청의 결과를 onSuccess, onError로 받게 되는데 이때 Promise객체를 기준으로 인식한다는 내용을 보고 찾아서 수정하니 에러를 인식할 수 있게 되었습니다.
  • 첫번재 문제는 비교적 간단했다. 해당 로그인 여부를 체크하는 로직을 페이지 단위로 넣어어야 하는데 컴포넌트 단위로 넣어두어서 내부에 동일 로직을 가진 컴포넌트가 페이지에 중복되어 리턴되고 있었기 때문에 생긴 문제였다. 각 alert가 도대체 어느 컴포넌트에서 호출되는건지 쫓아가며 확인하다가 찾을 수 있었다.
  • 두번째 문제는 처음엔 useEffect에 의존성 배열로 useQuery에서 반환하는 isError를 넣어서 isError의 값이 변경되면 체크하도록 진행하였다. 하지만 이렇게 하더라도 최초 랜더링때 무조건 한번 검증하는 과정을 피할 순 없었다. 또 최초 랜더링때는 아직 useState에서 반환하는 isError가 true도 false도 아닌 undefiend로 넘어오고 있었는데 이 값이 falsy 한 값이기 때문에 useEffect내부의 if 조건에서 걸려서 자꾸 다른 페이지로 이동하게 되었던것으로 보였다.
  • 최초 랜더링때는 체크를 하지 않고, 에러 여부의 값을 useQuery를 통해 가져오고 난 다음에만 확인하는 형태로 로직을 변경할 필요가 있었다. 이문제를 해결하기 위해서 컴포넌트 내부에서 사용하는 isFirst state를 선언하고 초기값을 boolen으로 주었다. 이 값이 false 일때만 체크하는 형태로 로직을 변경하니까 최초의 한번은 if 안의 내부 로직을 타지 않고 실행 되었다.
const [isFirst, setIsFist] = useState(true)
    // 리액트 쿼리 관련
    const hasToken = getCookie("token")
    const { isError } = useQuery('chkToken', ()=>(chkToken(hasToken)), {
        refetchOnMount : 'always',
        retry : 1,
        enabled: hasToken!==''?true:false
    })

    useEffect(() => {
        if(!isFirst&&isError){
            alert('로그인 세션이 만료되었습니다. 재로그인 해주세요.')
            removeCookie("token")
            navigate('/'); //로그인 페이지로 이동
        }
        setIsFist(true) // 최초 랜더링때는 세션여부를 체크하지 않기 위해서 추가
    },[isError])

 

  • 이미 해당 과제를 만료하신 분들에게 여쭤보니 cors를 막기 위해 사람들이 사용하는 proxy 서버가 있다고 햇다. 폭풍 검색해서 몇군데의 public proxy server를 찾았지만 대부분 짧은 시간만 요청이 되거나, 혹은 무료였지만 유로로 전환되거나 get요청만 허용해주는 형태였다. 나는 서버와 post 요청을 하고있었기 때문에 현재 배포한 heroku를 포기하고 s3로 재배포 해야하나 고민했었다.
  • 하지만 누가 서버비를 제출했다고 해서 기존의 proxy 우회를 통해 CORS를 해결하는형태로 걔속해서 시도햇다.
  • 찾다보니 우연히 일정시간동안 테스트키를 발급해주고 이용가능한 proxy server를 알게되었고 배포결과에서 정상적으로 우회해서 token을 검증요청을 서버에 할 수있었다.

배포된 웹사이트 바로가기

구현화면

'개발일기' 카테고리의 다른 글

[회고] 부트캠프 항해99 마치며  (0) 2023.07.06
[TIL] 23.04.27.  (2) 2023.04.28
[TIL] 23.04.26.  (1) 2023.04.26
[TIL] 23.04.24.  (0) 2023.04.24
[WIL] 23.04.17. - 23.04.22.  (0) 2023.04.23