graphql codegenerator를 활용한 msw 자동화 세팅

rainlee·2023년 5월 18일
3
post-thumbnail

💡 해당 포스팅은 grahpqlgraphql codegenerator, msw 에 대해 상세하게 다루지 않습니다. 해당 정보는 링크를 참조해주세요.

Overview

msw란?

msw를 간략히 설명하자면 실제 API를 사용하는 것처럼 네트워크 수준에서 Mocking하는 방법입니다. 백엔드 개발자와 개발 전 합의하에 정의된 스펙을 바탕으로 스키마를 확정짓는다면 API 완성여부와 상관없이 백,프론트 양쪽의 개발자가 병렬적으로 개발이 가능해집니다.

codegen

실제 msw를 활용했을때 사용성은 만족했습니다. 다만 msw 사용하기 위한 보일러플레이트 코드들이 반복되어 이를 자동생성하여 활용할 수 있으면 좋겠다고 생각했고, graphql을 사용하는 개발환경이라 이를위해 graphql code generator를 활용하였습니다.

Project Core Specification

  • Framework & Language: Next.js, typescript
  • GraphQL Client: Apollo Client v3.5

자동화할 코드패턴은?

msw를 사용하려면 작성해야 되는 코드는 크게 4가지가 있습니다.

  1. schema 작성
  2. 호출 Document
  3. endpoint link 및 resolver
  4. response mocking data

위 패턴의 코드들을 plugin을 사용하여 자동화를 해봅시다.
저는 위 4가지를 각 네이밍이 고정된 파일로 지정하고 이를 활용할 계획입니다. 각 역할을 하는 여러개의 파일을 생성하므로 파일명을 통일하여 이를 구분하기로 결정했습니다.

  1. schema 작성 => mock.schema.gql 파일 생성
  2. 호출 Document => *.graphql.doc.ts 파일 생성
  3. endpoint link 및 resolver => *.graphql.ts 생성
  4. response mocking data => mockData.ts 생성

local schema 정의

mock.schema.gql 파일에 정의된 스키마는 실제 스키마에는 없으나 최종적으로 구현될 스키마를 작성합니다. 이를 통해 mocking API, Document등을 만드는 토대가 됩니다.

#e.g
type Query {
  #...query
  getFruit(input: FruitInput!): [Fruit]
}

input FruitInput {
  color: String
}

type Fruit {
  apple: String
  banana: String
}

plugin 작성

  • 플러그인에 대한 정보를 작성. 스키마 옵션에 로컬 스키마 넣는거 옵션 보여주기 등을 통해 옵션 설정 및 짜놓은 구조에 대한 설명을 넣을 것

Document

endpoint에 물리 스키마가 존재 할 경우, Document까지 생성해주지만 msw를 사용하면 Document는 생성하지 않습니다. 왜냐하면 당연하게도 실제로 존재하지 않는 query 혹은 mutation을 불러오기 입니다. 이 때문에 반복되는 코드작성을 줄이기 위해 plugin을 사용해 봅시다.
document-node 을 이용하여 local schema를 읽어 document를 생성하게 만듭니다. 저 같은경우는 실제 스키마도 불러와 리얼리티를 좀 더 높였습니다. 또한 한곳에서 mocking할 schema가 많아질경우 분별력이 사라져 이를 구분하기 위해 각 도메인 디렉토리에 스키마를 위치하여 불러오게 구성하였습니다.

import type { CodegenConfig } from '@graphql-codegen/cli'
 
const config: CodegenConfig = {
  schema: ['END_POINT_HERE', `mocks/${domain}/*.schema.gql`],
  documents: `mocks/**/${domain}/**/*.gql`,
  generates: {
     //... other
     preset: 'near-operation-file',
      presetConfig: {
        baseTypesPath: `~mocks/types/${domain}/__types__`,
        extension: '.graphql.ts',
    },
  }
}
export default config

주요 옵션에 대해 설명하자면

  • schema: 코드 생성에 사용되는 GraphQL 스키마
  • documents: GraphQL 문서 파일의 위치와 패턴을 지정합니다.
  • generates: 파일 경로를 키로, 생성 옵션을 값으로 지정합니다.

codegen script로 실행하면 위와같은 형태로 코드가 생성합니다. 첫번째 자동화가 완료되었습니다!

msw

Documents도 만들었으니 이제 msw를 자동화해봅시다.
generates 옵션에 link의 이름과 endpoint 작성하면 msw를 사용하기 위해 일일히 작성하던 코드를 함수로 불러올 수 있습니다.

import type { CodegenConfig } from '@graphql-codegen/cli';
 
 const config: CodegenConfig = {
   // ...
   generates: {
     'path/to/file.ts': {
       // plugins...
       config: {
         link: {
           name: 'stripe',
           endpoint: 'https://api.stripe.com/graphql'
         }
       },
     },
   },
 };
 export default config;

이를 설정하고 불러온다면 아래코드 처럼link를 생성하고 query 혹은 mutation을 작성하는 과정을 생성할 수 있습니다. 또한 ts파일로 생성되어 타입이 있기때문에, 타입추론을 통해서 res data가 올바른 값이 들어가는지도 체크가 가능합니다.

const exam = graphql.link('GRAPHQL_ENDPOINT');
export const mockGetTestQuery = (
  resolver: ResponseResolver<
    GraphQLRequest<GetTestQueryVariables>,
    GraphQLContext<GetTestQuery>,
    any
  >,
) =>
  exam.query<GetTestQuery, GetTestQueryVariables>(
    'GetTest',
    resolver,
  );

중간 정리를 하자면..

위의 plugin을 사용해 endpoint link 및 resolver 작성을 자동화 했습니다. 지금으로도 많은 코드작성시간을 단축할 수 있습니다. 여태까지의 과정을 정리하자면 논의된 스키마를 작성하여 불러오고, 호출할 query를 자동화 했고, msw 세팅을 자동화하였습니다.

1. schema 작성 => mock.schema.gql 파일 생성
2. 호출 Document => *.graphql.doc.ts 파일 생성
3. endpoint link 및 resolver => *.graphql.ts 생성
4. response mocking data => mockData.ts 생성

하지만 이대로 끝내긴 아쉽습니다. 왜냐하면 response data는 직접 지정해줘야되기 때문입니다. 이 부분도 자동화하여 어떤값을 넣을지 고민하지 않아도 되게 만들어보죠.

mock data

ts-mock-data를 활용하여 세팅을 해봅시다. 세세한 항목도 세팅할 수 있는데 이 부분은 기회가 된다면 다른 포스팅에서 다루도록 하겠습니다.
해당 plugin은 casual or faker 라이브러리를 사용해 mocking data를 생성할 수 있는데, 저는 리얼리티를 위해 한글데이터를 사용하고 싶어 default 라이브러리인 casual를 사용하지 않고 faker를 사용하여 구성했습니다.

'mocks/mockData.ts' = {
config: {
          prefix: 'mock',
          typesFile: `mocks/types/__types__`,
          generateLibrary: 'faker', 
          locale: 'ko',
        },
}

드디어 data도 자동화를 완성하였습니다! 아래는 생성된 코드 예시입니다. overrides 할 프로퍼티가 존재할경우 넣어주고 아닐경우는 자동생성된 데이터가 나옵니다.

export const mockReturnTestType = (overrides?: Partial<ReturnTestType>): ReturnTestType => {
  return {
    a: overrides && overrides.hasOwnProperty('a') ? overrides.a! : [9798],
    b: overrides && overrides.hasOwnProperty('b') ? overrides.b! : '비밀과',
    c: overrides && overrides.hasOwnProperty('c') ? overrides.c! : '법률로',
  };
};

실제 작동은..!

모든 plugin을 세팅했으며 이제 원하는대로 작동하는지 확인을 해야될 시간입니다! 작동순서는

  1. mocking API를 호출할 페이지에 진입한다.
  2. request를 보내고, msw가 이를 인터셉터하여 의도한 response 값을 내려준다.
  3. 실제 화면에 해당 데이터를 사용하는지 확인한다.

정의한 local schema는 아래와 같습니다

type Query {
  getTest(input: TestInput!): [ReturnTestType]
}

input TestInput {
  id: String!
}

type ReturnTestType {
  a: [Int!]!
  b: String!
  c: String!
}

이중에 개발자도구의 network tab을 확인하여 데이터가 왔는지 확인해보죠.

input 값의 id를 보내고,

데이터도 설정한 대로 왔네요! 😃

마치며..

codegen plugin 같은 경우 공식문서에도 간단한 가이드라인만 제시하고, 세팅하는 샘플 자료가 많이 없어서 글을 쓰게됐습니다. 제목에 자동화라는 거창한 타이틀을 달았지만, 수작업을 해야하는 상황은 존재해서 사실 완벽한 자동화는 아닙니다. 다만 기존 msw를 활용할때 이 부분은 반복되서 자동화하면 좋겠다라고 생각한 코드들의 패턴을 정리하고 발견해 이를 적용하니 좋았습니다.
혹시나 저와 같은 고민이 있으셨던분들에게 도움이 됐길바라며 이만 마치겠습니다.

참고링크

0개의 댓글