-
GraphQL Query & MutationLanguages/GraphQL 2023. 8. 13. 14:14
GraphQL은 REST API와 달리 클라이언트단에서 Query와 Mutation을 작성하여 호출합니다.(덕분에 Frontend에서 처리해야 할 작업이 많습니다...)
기존 REST API는 URL과 Method를 조합해 다양한 Endpoint가 있는 반면 GQL은 하나의 Endpoint을 가지기 때문에 Endpoint에 따라 DB SQL 쿼리가 달라지지 않고 GQL 스키마의 타입에 따라 쿼리가 달라집니다.(GQL 스키마의 타입을 GQL Code Generator을 통해 Front로 가져와 확인하는 것이 편합니다.) 그래서 GQL의 장점인 한 번의 API 호출로 여러 처리가 가능합니다.
Query & Mutation
GQL에선 Query와 Mutation으로 나누는데 구조는 비슷합니다. 다만 Query는 데이터 Read (REST API에서 GET) Mutation은 데이터 변조 (REST API에서 POST, PUT, DELETE)에서 사용됩니다.
Fragment는 반복되는 쿼리를 재사용 가능한 단위로 나눈 것으로 쿼리 내부에서 필드들을 반복적으로 사용할 때, Recursive Query를 사용해야 할 때 사용합니다.
Variables는 쿼리 문자열에 동적인 arguments를 쿼리에서 없애고, 이를 별도로 전달하는 방법입니다.
여러 예시를 통해 작성방법을 보겠습니다.
Query & Mutation Example
예시로 GQL Code Generator로 가져온 두 개의 데이터 모델을 사용하겠습니다.
/* * GraphQL Code Generator Schema Example */ export type Maybe<T> = T | null | undefined; export interface Scalars { String: string, Boolean: boolean, } export interface MemberModel { memberId?: Maybe<Scalars['String']>, memberName?: Maybe<Scalars['String']>, memberDesc?: Maybe<Scalars['String']>, memberEnable?: Maybe<Scalars['Boolean']>, } export interface MemberModelInput { memberId?: Maybe<Scalars['String']>, memberName?: Maybe<Scalars['String']>, memberDesc?: Maybe<Scalars['String']>, memberEnable?: Maybe<Scalars['Boolean']>, } export interface ProjectModel { projectId?: Maybe<Scalars['String']>, projectName?: Maybe<Scalars['String']>, projectDesc?: Maybe<Scalars['String']>, projectEnable?: Maybe<Scalars['Boolean']>, } export interface Query { memberModelQuery?: Maybe<Array<Maybe<MemberModel>>>, projectModelQuery?: Maybe<ProjectModel>, } export interface QueryMemberModelQueryArgs { memberId: Scalars['String'] } export interface Mutation { MemberModelDelete: Scalars['Boolean'], MemberModelSave?: Maybe<MemberModel>, } export interface MutationMemberModelDeleteArgs { memberId: Scalars['String'] } export interface MutationMemberModelSaveArgs { MemberModel: MemberModelInput }
Query Example
Query는 기본적으로 query name과 받을 Model로 이루어져 있습니다. Code Generator를 통해 가져온 스키마의 항목과 query 필드를 맞춥니다.
import * as Types from "./types"; const memberQuery = 'memberModelQuery'; const projectQuery = 'projectModelQuery'; const basicQuery = ` { ${memberQuery} { memberId, memberName, memberDesc, memberEnable } } `; ...
query의 전달 인자가 있을 경우는 query에 해당 인자를 전달함으로써 query를 요청합니다.
... // Arguments Query Example const argumentsQuery = ` { ${memberQuery}(memberid: memberid) { memberId, memberName, memberDesc, memberEnable } } `; ...
한 번의 요청으로 여러 Data를 요청하는 케이스에선 여러 query를 묶어서 요청합니다.
... // Multiple Query Example const multipleQuery = ` { ${memberQuery} { memberId, memberName, memberDesc, memberEnable } ${projectQuery} { projectId, projectName, projectDesc, projectEnable, } } `; ...
Fragment을 사용하는 케이스에선 반복되는 쿼리를 fragment로 나누어 query 내부에서 spread operator로 사용합니다.
... // Fragment Query Example const fragment = ` fragment memberFragment on MemberModel { memberId, memberName, memberDesc, memberEnable } `; const fragmentQuery = ` { ${memberQuery} { ...memberFragment } } ${fragment} `; ...
Operation name은 사용자가 query에 맞는 name을 지정합니다. 디버깅을 할 때나 서버사이드 로깅에 유용하므로 사용을 권장합니다. Operation name은 query가 수행하는 작업의 내용과 스키마의 name을 조합하여 해당 query가 어떤 역할을 하는지 명시하면 좋습니다.
... // Operation name Query Example const variablesQuery = ` query getMemberQuery { ${memberQuery} { memberId, memberName, memberDesc, memberEnable } } `; ...
Variables을 사용하는 케이스에선 query를 통해 api를 요청할 때 variables을 같이 담아서 전송합니다.
... // Variables Query Example const variablesQuery = ` query getMemberQuery($memberId: String!) { ${memberQuery}(memberid: $memberid) { memberId, memberName, memberDesc, memberEnable } } `; const variables: Types.QueryMemberModelQueryArgs = { memberId: 'memberId' } ...
Mutation Example
Mutation의 작성도 Query와 비슷합니다. Upsert case와 Delete case를 각각 하나씩 보겠습니다.
import * as Types from "./types"; const upsertMemberMutation = 'MemberModelSave'; const deleteMemberMutation = 'MemberModelDelete'; // Mutation Upsert Example const upsertMutation = ` mutation upsertMemberMutation($MemberModel: MemberModelInput!) { ${upsertMemberMutation}(MemberModel: $MemberModel) { memberId } } `; const upsertVariables: Types.MutationMemberModelSaveArgs = { MemberModel: { memberId : 'memberId', memberName : 'memberName', memberDesc : 'memberDesc', memberEnable: true, } } // Mutation Delete Example const deleteMutation = ` mutation deleteMemberMutation($memberId: String!) { ${deleteMemberMutation}(memberId: $memberId) } `; const deleteVariables: Types.MutationMemberModelDeleteArgs = { memberId: 'memberId' } ...
Query와 마찬가지로 Operation name을 지정하여 작성할 수 있고 케이스에 따라 Variables을 사용할 수 있습니다.
Fragment도 케이스에 따라 사용할 수 있습니다.
... // Fragment Mutation Example const fragment = ` fragment memberFragment on MemberModel { memberId, memberName, memberDesc, memberEnable } `; const fragmentMutation = ` mutation upsertMemberMutation($MemberModel: MemberModelInput!) { ${upsertMemberMutation}(MemberModel: $MemberModel) { ...memberFragment } } ${fragment} `;
Mutation도 Query처럼 여러 필드를 포함할 수 있지만 차례대로 실행(한 번에 호출로 여러 Mutation을 보내면 첫 번째 Mutation의 동작이 완료된 후에 두 번째 Mutation이 동작)을 합니다.
마무리
REST API의 환경에서 작업을 하다 GraphQL 환경을 접하니 Query 작성에 따라 Data를 원하는 구조대로 받을 수 있고 한 번의 호출로 여러 처리를 할 수 있는 장점이 있었습니다. 케이스에 맞게 Query와 Mutation을 잘 사용하여 GraphQL의 장점을 잘 활용 해야 겠습니다.
참고 자료
'Languages > GraphQL' 카테고리의 다른 글
GraphQL Code Generator (0) 2023.08.15