project-login;

noungheeya·2022년 11월 2일
0

project

목록 보기
4/10

useQuery 사용했는데 왜 작동이 안되지....😭

지금 만들고 있는 프로젝트에서는 로그인이 중요한 기능이 아니어서 이메일과 비밀번호 이름만 입력받아 더미 데이터에 있으면 오케이 하도록 계획했다. react-query의 useQUery 사용하다 기본적인 규칙을 놓친 게 있다는 것을 알게 되었다.

서버에서 가져온 데이터를 querykey를 통해 쉽게 관리할 수 있어서 react-query를 사용한거지만...


짜잔! 이런 문구가 뜬다..
내용을 보면 컴포넌트 함수 내에서 사용해야 되는데 잘못된 곳에서 사용했다는 의미다.

const fetchUser = async() => {
  return await axios.get(`http://localhost:4000/users`)
};

export const Login = (loginData: Login) => {
  const nav = useNavigate() **잘못된 사용**
  return useQuery(['loginUser'], fetchUser, {
    onError: (e: any) => {
     alert(e.message)
    },
    onSuccess: ({ data }) => {
      const result = data.find((user: Login) => {
        user.email === loginData.email && user.password === loginData.password})
      
      if(result) nav('/')
      else alert('이메일 및 패스워드가 확인되지 않습니다 다시 입력해주세요!')
    }
  })
}

->로그인 폼 컴포넌트

hook의 규칙

로그인을 한 뒤 성공하면 메인 페이지로 가기 때문에 연결성을 위해 함수 컴포넌트 내에서가 아닌 useQuery를 리턴하는 함수 내에서 useNavigate를 선언해 주었다. 그런데 이런한 로직은 기본적인 react Hook 규칙에서 어긋난다.

최상위에서 호출해야함!

const [ value, setValue ] = useState('cake')
if(value !== ' ') {
	useEffect(() => {
     alert(value)
    }, [value])
}

만약 최상위가 아닌 조건부나 중첩된 함수 내에서 사용할경우, hook을 읽지 못하고 건너뛰어 버그를 발생시킨다. 만일 조건부를 사용하고 싶다면 아래와 같이 사용해야한다.

const [ value, setValue ] = useState('cake')

useEffect(() => {
	if(value !== ' ') {
   		alert(value)
    }
}, [value])

오직 React 함수 내에서 Hook을 호출!

다른 함수에서 사용하고 싶은경우는 customHook함수 내에서 사용가능하다

hook의규칙

내가짠 코드 바꾸기

  • 첫번째 방법으로는
    : useQuery를 반환하는 함수내에서 지우고 LoginForm 컴포넌트에서 작성하면된다
  • 두번째는 javascript함수를 customHook으로 바꾼다

그러나... 아직 문제는 해결되지 않았다.

로그인을 할 때 데이터를 조회해야 하니깐 get을 써야 되는 게 아닐까? 하지만 실무에서 보안상의 이유로 post를 사용한다고 한다.
여기서는 백엔드에서 만들어 놓은 서버가 없기 때문에
post를 사용할 수는 없고 get을 사용해서 흉내만 냈다.

react-query에서는 get을 useQuery를 사용하고 나머지 데이터를 변경해야 하는 post, patch, delete는 useMutation을 사용해야 한다.

react-query를 생각하지 않고 단순히 axios를 사용해서 서버 데이터를 가져온다 했을 때 reactdom를 렌더링 한 후 호출하는 것이 권장된다. 그래서 렌더링 직후 일어나는 useEffect에서 사용을 하는데 이렇게 하면 아주 작동이 잘 된다.

useEffect(() => {
	const res = axios.get(url)
    res.then(() => ...).catch(() => ...)
}, [])

useLogin()은 useQuery를 리턴하는 customHook함수

하지만 useQuery는 저 메시지가 그대로 뜬다. 그러나 이것도 useNavigate를 잘못 선언한 것과 같은 이유다!
react-query가 서버에서 받아오 데이터를 관리하는 것을 도와주는 라이브러리이지만 useQuery도 reactHook이다 그래서 hook의 규칙이 적용되어야 한다!!
-> 최상단으로 올려줌

로그인 폼이 나타났을 때 정상적으로 데이터를 가져올 수 있게 되었다!

그러나 여기서는 get을 사용해야 되는데 submit 이벤트에 전달해줘야 하는 함수에는 useQuery를 호출할 수 없다.

export default function LoginForm({onClick}: Props) {
  const [ login, setLogin ] = useState({email: "", password: ""})
  const queryClient = useQueryClient()
  
  const{ data } = useLogin() // useQuery를 리턴하는 customHook
     //먼저 useQuery를 통해 전체 값을 받아옴 //
  
  const changeValue = (e: ChangeEvent<HTMLInputElement>) => {
    setLogin({ ...login, [e.target.name]: e.target.value })
  }

  const submitValue = async(e: ChangeEvent<HTMLFormElement>) => {
    e.preventDefault()
    const result = data?.data.filter((user: Login) => {
        return user.email === login.email && user.password === login.password})  // 받아온 데이터에서 입력값을 필터한 후 
      
    if(result !== undefined) {
      queryClient.setQueryData(['@user'], result) // 값이 존재하면 그 값을 쿼리키에 세팅해줌
      window.location.replace('http://localhost:3000/')
    }
    else alert('이메일 및 패스워드가 확인되지 않습니다 다시 입력해주세요!')
    
  } // submit이벤트에 전달하는 함수

  
  return (
    <>
      ...
  )
}

LoginForm 컴포넌트가 최상단에 useQuery를 호출하여 데이터를 가져온 후 submit 이벤트가 발생하면 입력값을 토대로 서버에서 가져온 데이터에서 값을 찾은 후 queryKey;'@user'에 다시 세팅해 주었다(getQueryData).

하지만!! 로그인한 순간 리렌더링이 발생하기 때문에 로직이 처음부터 재실행 된다. 그래서 로그인 로그아웃은 querkey보다는 localStorage를 관리해야 로그인 상태를 유지할 수 있고 로그아웃 까지 할 수 있었다.

export default function LoginForm({onClick}: Props) {
  const [ login, setLogin ] = useState({email: "", password: ""})
  const{ data } = useLogin() 
  
  const changeValue = (e: ChangeEvent<HTMLInputElement>) => {
    setLogin({ ...login, [e.target.name]: e.target.value })
  }

  const submitValue = async(e: ChangeEvent<HTMLFormElement>) => {
    e.preventDefault()
    const result = data?.data.filter((user: Login) => {
        return user.email === login.email && user.password === login.password})
      
    if(result !== null) {
      window.localStorage.setItem('key', result[0].id)
      window.localStorage.setItem('email', result[0].email)
      window.localStorage.setItem('name', result[0].name)
      window.location.replace('http://localhost:3000/')
      
    }
    else alert('이메일 및 패스워드가 확인되지 않습니다 다시 입력해주세요!')
    
  }

  
  return (
    <>
      ...
    </>
  )
}
profile
귀여븐 엥팁이지롱😊

0개의 댓글