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

GONI·2021년 12월 5일
0

GraphQL

목록 보기
2/4
post-thumbnail

이런저런 바빴다는 이유(핑계)로 꽤나 오랫동안 멀리했던 graphql을 리마인드 해보고 싶어서 다뤄보게 되었다. 이번에 구현할 미니 프로젝트의

- 기능

  1. 로그인
  2. 로그아웃
  3. 게시물 등록
  4. 댓글 작성
  5. 게시물 좋아요

그리고?

- 사용한 기술 스택 목록

  • FE : React + TypeScript
  • BE : GraphQL

주저리주저리는 생략하고 바로 진행한 일지를 기록해보자.


1. Query

일단 apollo-server를 이용하여 서버를 구현해보자.

const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req }) => ({ req }),
})

context는 req요청을 받을 수 있게 설정해준다.
(ex. req.headers)

typeDefs.js 파일에는 역시 apollo-server의 gql을 사용하여

input RegisterInput {
    username: String!
    password: String!
    confirmPassword: String!
    email: String!
}
type User {
    id: ID!
    email: String!
    token: String!
    username: String!
    createdAt: String!
}

요러케 유저 타입도 정의하고

type Mutation {
    register(registerInput: RegisterInput): User!
    login(username: String!, password: String!): User!
}

REST API를 대체하기 위한 mutation도 정의해준다
(게시물 관련 Query 및 Mutation은 생략)

type을 사용하여 정의하는 것이 가장 깔끔한 것 같지만 굳이 input을 사용한 이유는 두 타입이 서로 다르게 동작한다는 것을 알려주기 위함이라고 하는 것 같다 (맞나?)


2. Mutation

본격 typeDefs를 선언된 타입을 정의해주기 위한 Mutation을 세세하게 정의해주기 위한 resolver를 정의해주도록 하자

먼저 회원가입은 bcrypt및 jwt를 사용하여 진행하였다.

Mutation: {
    register: async(
      _,
      { registerInput: { username, email, password, confirmPassword } }
    ) => {
    	password = await bcrypt.hash(password, 12)
		const token = jwt.sign(
        {
          id: user.id,
          email: user.email,
          username: user.username,
        },
        SECRET_KEY,
        { expiresIn: "1h" }
    )
}

여기서 mutation()안에 들어가는 변수는 총 4가지이다.

register(parent, args, context, info)

  • parent: 부모 resolver에서 반환되는 객체이다
  • args: Query에서 정의한 변수가 들어가는 곳이다
  • context: 앞서 context를 정의했다면 이 부분에서 request 요청을 처리할 수 있게 된다.
  • info : 아직 사용해 본 적이 없음...

3. 인증 미들웨어

기존 node 미들웨어와 같이 진행하였지만, 앞에서 언급했던 context를 사용할 수 있는 시간이 왔다.

module.exports = (context) => {
    const authHeader = context.req.headers.authorization
    if (authHeader) {
      const token = authHeader.split("Bearer ")[1]
      if (token) {
        try {
          const user = jwt.verify(token, SECRET_KEY)
          return user
        } catch (error) {
          throw new AuthenticationError("Invalid/Expired token")
        }
      }
      throw new Error("token형식이 올바르지 않습니다.")
    }
    throw new Error("header가 존재하지 않습니다.")
}

차이점은 바로 헤더 및 토큰을 context.req.headers를 통해 받아온다는 것이다. 그 부분을 제외하면 사실상 node에서 구현했던 코드와 동일...? 하다...!
(물론 서버 부분에서 context: ({ req }) => ({ req }) 를 작성해줘야 받아올 수 있다!)

token을 생성할 때 id, email, username 이 세 가지를 이용하였으므로, token이 있을 경우 이 세가지를 포함한 객체를 리턴해 줄 것이다. 그리고 미들웨어를 적용시켜 줄 때는

createPost: async (_, { body }, context) => {
    ...
    
    const user = authMiddleware(context)
    
    ...
}

위 코드와 같이 context를 정의한 뒤 사용해 주면 끝!
참 쉽죠?



서버에서 graphql을 정의하는 것은 구조만 익숙하다고 했다면 사실 기존 node를 사용할 때와 다른 것은 없었다. 이 요청들을 client에서 불러오거나 받아올 때 graphql의 진가가 들어나는 것 같다!

React를 이용한 client-side는 다음 편에...

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

0개의 댓글