본문 바로가기
Back-end/Python

[GraphQL] 무작정 시작하기 (2) - Mutation

by 허도치 2020. 4. 14.

2020/04/13 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (1) - Schema & Query

 

 

 

0. 서론

 지난 포스트에서 GraphQL에 대해서 간략하게 알아보고 데이터를 조회하는 서버를 간단하게 만들어보았다. 이는 데이터의 기본적인 처리 프로세스인 CRUD(Create, Read, Update, Delete) 중 Read(조회)만 구현한 것이다. GraphQL에서 Read(조회)는 쿼리(Query)가 처리하며, Create(입력), Update(수정), Delete(삭제)는 뮤테이션(Mutation)이 처리한다.

 그래서 이번 포스트에서는 데이터 변조를 위한 뮤테이션(Mutation)들을 구현해보도록 하겠다.

 

 

 

1. 프로젝트 준비

2020/04/13 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (1) - Schema & Query

 

[GraphQL] 무작정 시작하기 (1) - 설치 및 실행

0. 서론 최근 Javscript로 2048 웹게임을 만들고 있는데, 게임 결과를 저장할 수 있게 Database(MongoDB)를 연동하였다. 결과 조회는 평소처럼 클라이언트에서 목록을 요청하면, 서버에서는 미리 작성된 쿼리를 통..

heodolf.tistory.com

- 이전 포스트를 참조.

 

 

 

2. Mutation 소스 작성
2-1. 데이터 입력(Create) Mutation - api/mutation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# api/mutation.py
import datetime
import graphene
from api.models import RankModel, RankType
 
# Create Mutation 정의
class CreateRank(graphene.Mutation):  
  # 입력받을 파라미터 Field 정의
  class Arguments:
    mode = graphene.String(required=True)
    name = graphene.String(required=True)
    score = graphene.Int(required=True)
    is_mobile = graphene.Boolean(default_value=False) # 생량 가능
 
  # 반환 Field 정의
  rank = graphene.Field(RankType)
  success = graphene.Boolean()
  
  # 실행할 Mutation 정의
  def mutate(root, info, **kwargs):
    # MongoDB Model 생성
    model = RankModel(
      mode=kwargs.get("mode"),
      name=kwargs.get("name"),
      score=kwargs.get("score"),
      is_mobile=kwargs.get("is_mobile"),
      reg_dttm=datetime.datetime.now().strftime("%Y%m%d%H%M%S"# 현재시간
    )
    # MongoDB에 저장
    model.save()
    
    # 결과 반환
    return CreateRank(
      rank=model,
      success=True
    )
 
# Mutation Field 정의
class Mutation(graphene.ObjectType):
  create_rank = CreateRank.Field()
 
cs

1) [ 7 ln ] - Mutation Class 생성.

2) [ 9~13 ln ] - 입력받을 Mutation의 파라미터를 정의.

   : 이곳에 정의되지않은 파라미터를 받을 경우 오류가 발생함.

3) [ 15~17 ln ] - 처리 결과를 반환할 Field 정의.
   : 반드시 한 개 이상 존재해야함.

4) [ 20~36 ln ] - mutate 함수에 데이터 입력(Create) 프로세스를 정의.
   : [ 22~28 ln ] - 입력받은 파라미터로 MongoDB Model 객체 생성.
   : [ 30 ln ] - MongoDB에 저장.
   : [ 33~36 ln ] - 결과값을 반환.
     : 3)에서 정의한 반환타입을 파라미터로 받는 Mutation을 반환.

5) [ 39~40 ln ] - Mutation Field 정의.
   : 위에서 생성한 Mutation Class를 Field로 설정.
   : 신규로 생성할 때마다 이곳에 추가.


* mutate 함수에 정의한 프로세스에 따라 Update, Delete를 구현할 수 있음.

 

2-2. Schema에 Mutataion 추가 - api/schema.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# api/schema.py
import graphene
 
from api.models import RankModel, RankType
from api.mutation import Mutation
 
# Query Field 정의
class Query(graphene.ObjectType):  
  '''중략'''
 
# Schema 생성
schema = graphene.Schema(
  query=Query,
  mutation=Mutation, # Mutation 추가
  types=[
    RankType
  ]
)
cs

1) [ 14 ln ] - 앞서 정의한 Mutation을 Schema에 추가.

 

 

 

3. Flask App 실행
3-1. 실행

$ python app.py

 

3-2. 실행 결과

 

3-3. GraphQL View 실행

1) 브라우저 실행 후 http://localhost:3000/graphql 로 접속.

2) 오른쪽 상단에 Docs를 클릭하면 Schema의 정보를 확인 할 수 있음.
   : Query와 Mutation이 등록된 것이 확인됨.

 

 

 

4. Mutation 테스트
4-1. 목록 확인

1) 현재 4개의 데이터가 출력되는 것이 확인됨.

 

4-2. 데이터 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 데이터 추가 Mutation                                                 
mutation {
  createRank(
    mode: "2x2",
    name: "heo",
    score: 100
  ){
    success
    rank {
      id
      mode
      name
      score
      isMobile
      regDttm
    }
  }
}
cs

1) [ 2 ln ] - Muatation 시작.

2) [ 3~7 ln ] - CreateRank Mutation에 저장할 데이터를 입력.

3) [ 7~17 ln ] - 결과 출력.
   - [ 8 ln ] - success Field 출력 .
   - [ 9~16 ln ] - rank Field의 상세 Field들을 출력.

4) 실행 결과.
   : 데이터가 정상적으로 저장되면 요청한 결과값이 출력됨.
   : [4-1]을 다시 실행해보면 목록에 추가된 것을 확인할 수 있음.

 

 

 

5. Update, Delete Mutation 소스 작성
5-1. 데이터 수정(Update) Mutation - api/mutation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# api/mutation.py
 
'''중략'''
 
# 상세 입력 Field 정의
class InuptUpdateRankData(graphene.InputObjectType):
  score = graphene.Int()
  is_mobile = graphene.Boolean()
    
# Update Mutation 정의
class UpdateRank(graphene.Mutation):  
  # 입력받을 파라미터 Field 정의
  class Arguments:
    mode = graphene.String(required=True)
    name = graphene.String(required=True)
    data = InuptUpdateRankData(required=True)
    
  # 반환 Field 정의
  rank = graphene.Field(RankType)
  success = graphene.Boolean()
 
  # 실행할 Mutation 정의
  def mutate(root, info, mode, name, data):
    # 수정할 MongoDB Model 조회
    model = RankModel.objects(
      mode=mode, 
      name=name
    ).first()
    
    # 입력받은 파라미터로 수정      
    if data.score is not None:
      model.score = data.score
    
    if data.is_mobile is not None:
      model.is_mobile = data.is_mobile
      
    model.upd_dttm = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    
    # MongoDB에 저장
    model.save()
    
    # 결과 반환
    return UpdateRank(
      rank=model, 
      success=True
    )
 
'''중략'''
 
cs

1) [ 6~8 ln ] - 상세 입력 Field 정의.
   : InputObjectType을 사용하면 파라미터를 Json형태로 받을 수 있음.

2) [ 13~16 ln ] - 입력받을 파라미터 Field를 정의.
   : [ 16 ln ] - data를 상세 입력 Field로 정의.

3) [ 23~46 ln ] - mutate 함수에 데이터 수정(Update) 프로세스를 정의.
   : [ 25~28 ln ] - MongoDB에서 mode와 name 조건에 일치하는 데이터를 1개 조회.
   : [ 31~37 ln ] - 조회된 데이터를 입력받은 데이터로 수정.
   : [ 40 ln ] - MongoDB에 저장.
   : [ 43~45 ln ] - 결과 반환.


* Mutation Field 추가는 데이터 삭제(Delete)에서 처리.

 

5-2. 데이터 삭제(Delete) Mutation - api/mutation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# api/mutation.py
 
'''중략'''
 
# Delete Mutation 정의
class DeleteRank(graphene.Mutation):
  # 입력받을 파라미터 Field 정의
  class Arguments:
    mode = graphene.String(required=True)
    name = graphene.String(required=True)
    
  # 반환 Field 정의
  success = graphene.Boolean()
 
  def mutate(root, info, mode, name):
    # MongoDB에서 삭제
    RankModel.objects(
      mode=mode, 
      name=name
    ).delete()
    
    # 삭제되었는지 확인
    success = RankModel.objects(
      mode=mode, 
      name=name
    ).first() == None
    
    # 결과 반환
    return DeleteRank(
      success=success
    )
 
# Mutation Field 정의
class Mutation(graphene.ObjectType):
  create_rank = CreateRank.Field()
  update_rank = UpdateRank.Field()
  delete_rank = DeleteRank.Field()
cs

1) [ 15~31 ln ] - mutate 함수에 데이터 수정(Update) 프로세스를 정의.
   : [ 17~20 ln ] - MongoDB에서 mode와 name 조건에 일치하는 데이터 삭제.
   : [ 23~26 ln ] - 삭제되었는지 확인.
   : [ 29~31 ln ] - 결과 반환.

2) [ 34~37 ln ] - Mutation Field 정의.
   : [ 35 ln ] - 데이터 입력(Create) Mutation Field 추가.
   : [ 36 ln ] - 데이터 수정(Update) Mutation Field 추가.
   : [ 37 ln ] - 데이터 삭제(Delete) Mutation Field 추가.

 

 

 

6. Mutation 테스트
6-1. 입력, 수정, 삭제 일괄 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
mutation {                                                                                
  # 데이터 입력(Create)
  createRank(
    mode: "100x100",
    name: "dochi",
    score: 1,
    isMobile: false
  ){
    success
    rank {
      id
      mode
      name
      score
      isMobile
      regDttm
    }
  }
  
  # 데이터 수정(Update)
  updateRank(
    mode: "100x100",
    name: "dochi",
    data: {
       score: 999999,
        isMobile: true
    }
  ){
    success
    rank {
      id
      mode
      name
      score
      isMobile
      regDttm
      updDttm
    }
  }
  
  # 데이터 삭제(Delete)
  deleteRank(
    mode: "100x100",
    name: "dochi"
  ){
    success
  }
}
cs

1) [ 3~18 ln ] - 데이터 입력(Create) Mutation 실행.

2) [ 21~39 ln ] - 데이터 수정(Update) Mutation 실행.

3) [ 42~47 ln ] - 데이터 삭제(Delete) Mutation 실행.


* GraphQL은 위와 같이 하나의 쿼리에 여러개의 이벤트를 발생시킬 수 있음.

 

 

 

마치며

- 지금까지 CRUD를 간략하게 구현해보았다. 목록을 조회할 때 페이징처리나 정렬, 예외처리 등 아직 부족한 점이 많기는 하지만 앞으로 좀 더 공부해가면서 살을 붙여나가도록 하겠다.
- 다음 포스트에서는 데이터를 조회할 때 페이징처리하여 조회하는 방법에 대해서 알아보도록 하겠다.

댓글