[React + GraphQL] 미니 프로젝트 일지(2)

GONI·2021년 12월 10일
0

GraphQL

목록 보기
3/4
post-thumbnail

앞서 1편에서 backend부분의 type, query, mutation정의를 완료하였다!
RestAPI같이 각각의 endpoint도 필요하지 않고, uri의 설정 및 세팅도 필요없고 매우 빠르게 작성할 수 있었다 (물론 더 숙련이 된다면...)

이번에는 client-side에서 graphql을 어떻게 사용했는지 다뤄보려고 한다.


- 로그인 및 회원가입

프로젝트를 제작한 뒤 찾아보니 graphql의 캐시를 이용해 local state로 token을 관리하며 로그인을 구현하는 방법이 따로 있었다... 하지만 그걸 몰랐기에 전에 사용했던 redux를 통해 로그인한 유저 정보 및 token을 관리하였다.

const initialState: AuthState = {
    user: null,
}

const reducer = handleActions<AuthState, UserType>(
    {
      [LOGIN]: (state, action) => {
        TokenService.setToken(action.payload.token)
        return { user: action.payload }
      },
      [LOGOUT]: () => {
        TokenService.removeToken()
        return { user: null }
      },
    },
    initialState
)

initialState와 reducer는 보는 코드 그대로, TokenService class를 통해 구현 로그인 시 localStorage에 토큰을 생성해주고 로그아웃 시 토큰을 제거해주었다.
(graphql에 더 초점을 맞추고 싶으니 redux는 패스...)

그러면, 먼저 로그인 부분을 보자

  • 참고로 해당 프로젝트는 redux를 사용하였기 때문에 Container-Presenter패턴을 적용하였고, 로그인 토큰의 존재 여부를 판단하여 Container컴포넌트에서 Redirect를 곧바로 진행해주었다.
if (token !== null) {
    return <Navigate to="/" />
}

항상 Container에서는 상태만 받아오고 판단은 Presenter부분에서 했는데, Container에서 상태판단까지 할 경우 Presenter가 훨~씬 깔끔해 질 수 있었다... 이걸 왜 생각하지 못했을까

  • 또 참고로, react-router-dom이 v6으로 업데이트 되면서 Redirect가 없어졌다고 해서 Navigate를 사용해보았다. Navigate가 Redirect를 대체한다,,, 는 아닌거같지만 정상적으로 작동이 되길래 적용했다.

그 후 gql을 불러와야 하는데, 컴포넌트가 길어지는 게 딱 질색이라 gql폴더를 만들어서 export하기로 했다.

import { gql } from "@apollo/client"

export const REGISTER_USER = gql`
  mutation register(
    $username: String!
    $email: String!
    $password: String!
    $confirmPassword: String!
  ) {
    register(
      registerInput: {
        username: $username
        email: $email
        password: $password
        confirmPassword: $confirmPassword
      }
    ) {
      id
      email
      username
      createdAt
      token
    }
  }
`

export const LOGIN_USER = gql`
  mutation login($username: String!, $password: String!) {
    login(username: $username, password: $password) {
      id
      email
      username
      createdAt
      token
    }
  }
`

register와 login을 gql user폴더에 밀어넣기
$(달러)표시를 통해 변수를 지정할 수 있다. 이러한 Query와 변수들을 이제 진짜 client-side에서 사용해보자

useMutation

mutation을 불러오기 위해 useMutation이라는 React hook을 사용할 수 있다.

const [loginUser, { loading, error }] = useMutation(LOGIN_USER, {
    variables: { username, password },
})

이 때 loginUser는 데이터를 fetch하고 싶은 부분에 넣으면 된다.

loading, error은 말그대로 로딩과 에러라서 로딩 부분 스타일링 및 에러 핸들링을 아래와 같이 본인이 편한 코드로 구현해 주면 된다.

if(error){
    return <p>Error</p>
}
if(loading){
    return <p>Loading...</p>
}

다음, variables부분은 변수이고 앞서 선언했던 mutation에

mutation login($username: String!, $password: String!) 

$부분으로 들어가게 된다. 퍼즐 끼워맞추는 느낌으로 간단하게 구현해 줄 수 있다!

그렇다면 회원가입은 총 4개의 변수를 받기 때문에 아래와 같이 variables부분에 4개의 변수를 넣어주면 되겠죠?

const [RegisterUser, { loading }] = useMutation(REGISTER_USER, {
    variables: { username, email, password, confirmPassword },
})

(참 쉽죠)

또 한 가지 방법은 variables를 fetch함수 부분에 곧바로 넣어주는 것이다.

const [loginUser, { loading, error }] = useMutation(LOGIN_USER)
...
<div 
  onClick={() => loginUser({variables: { username, password }})}>
    로그인
</div>

본인이 더 편한 방법을 사용하면 될 거 같다



이렇게 끝낼 수 있는 useMutation이라면 참 좋겠지만 아무리 그래도 데이터 fetching하는게 이렇게 간단하진 않겠지... 이 과정에는 치명적인 결함이 있다. 만약 우리가 mutation을 통해 데이터를 추가한다고 해도 화면에 바로 나타나지는 않고, 새로고침을 해주면 나타난다. 추가한 데이터를 바로 화면에 랜더링 해줘야 하는 경우 useQuery에서 사용할 수 있는 refetch함수를 이용하여 해결할 수 있다.

useQuery 사용은 다음 일지에...!

profile
오로지 나의 기억력을 위한 일지

0개의 댓글