Post

Spring Boot GraphQL 구현해보기 (feat. API Gateway, MSA)

Skala 과정에서 마이크로 서비스 아키텍처 구조에 대해 새롭게 알게 되었습니다.
배운 것을 실제로 구현해보기 위해 이 여정을 시작하기로 했습니다.
처음 글부터 보러가기

계속해서 MSA 아키텍처를 구현하기 전에 GraphQL에 대해 학습을 진행해보겠습니다

GraphQL?

항목설명
특징• 클라이언트가 필요한 데이터만 선택하여 요청 (오버페칭 방지)
• 하나의 엔드포인트(/graphql)에서 모든 요청 처리
• 계층적 데이터 구조를 JSON 형식으로 반환
주요 기능Query: 데이터를 조회하는 요청
Mutation: 데이터를 변경(추가, 수정, 삭제)하는 요청
Subscription: 실시간 데이터 업데이트를 위한 WebSocket 기반 기능
장점• 클라이언트가 원하는 데이터만 요청 가능 (오버페칭 방지)
• 여러 리소스를 한 번의 요청으로 조회 가능 (언더페칭 방지)
• API 버전 관리 없이 스키마 수정만으로 확장 가능
단점HTTP 캐싱이 어렵다 (모든 요청이 POST로 전달됨)
복잡한 쿼리는 성능 저하 가능성 (N+1 문제 발생 가능)
보안 이슈 (클라이언트가 자유롭게 요청을 조작 가능하므로, 필드 단위 권한 관리 필요)

GraphQL 사용 예

라이브러리 추가

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>

schema.graphqls

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
# type으로 Response DTO를 설정할 수 있음
type UserInfoResponse {
  id: ID!
  username: String!
  email: String!
  phone: String!
}

# input으로 Request DTO를 설정할 수 있음
input RegisterUserRequest {
  username: String!
  password: String!
  email: String!
  phone: String!
}

# GET 요청과 비슷함. Request Body나 Value 없이 데이터만 받아 옴
type Query {
  user: [UserInfoResponse] # List 형식으로 받아옴
}

# POST 요청과 비슷함. Request Body나 Value를 넣어서 요청할 때 사용함
type Mutation {
  #
  registerUser(request: RegisterUserRequest): UserInfoResponse
}

위 코드는 실제로 제 토이 프로젝트에 적용해 본 코드입니다!

생각보다 많이 어렵지 않고, 이렇게 정의만 해두면 호출하고 싶은 변수를 골라서 호출 할 수 있다는 점이 굉장히 매력적인 것 같습니다.

Controller.java 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@QueryMapping
public Flux<UserInfoResponse> user() {
    log.info("Get all users");

    Flux<UserInfoResponse> responses = userServiceClient.getAllUsers();

    log.info(responses.toString());

    return responses;
}

@MutationMapping
public Mono<UserInfoResponse> registerUser(
        @Argument("request") RegisterUserRequest request ) {
    log.info("Register user");

    Mono<UserInfoResponse> response = userServiceClient.registerUser(request);

    log.info(response.toString());

    return response;
}

컨트롤러는 다음과 같이 작성됩니다.

물론 GraphQL에서 설정해 둔 DTO로만 소통할 수 있는 것은 아니고, 따로 DTO Class를 작성해주어야 합니다!
DTO 명이 꼭 서로 일치해야 하는 것은 아니지만, 필드명은 꼭!! 같아야합니다!

@Argument 어노테이션으로 Request를 받아야 함을 명시합니다. (REST API 에서의 @RequestBody 등과 비슷함)

저는 WebFlux를 적용하여 비동기 방식으로 호출되는 API 서버를 구축했기 때문에 Mono, Flux 같은 래퍼로 감싸주었는데
동기 방식으로 호출한다면 래퍼를 사용하지 않아도 정상적으로 출력되는 것 같습니다.(ResponseEntity로 감싸주지 않았는데도 정상적으로 값을 받았습니다)

여기서 MonoFlux를 처음 사용해봤는데,
Mono는 단건 호출, Flux는 다중건 호출을 할 때 사용합니다.


반환 값

Postman을 이용하여 API를 호출해보았습니다!
url만 입력하면 GraphQL 스키마까지 읽어와주는 기능이 있어서 정말 편리했습니다.

Muation 실행 시

GraphQL-Mutation

Query 실행 시

GraphQL-Query-All GraphQL-Query-Part

위 캡처화면을 보면 알 수 있듯이 입력 항목(Request) 뿐만 아니라 반환 항목(Response)까지 직접 설정해서 보내고 받을 수 있는 것을 알 수 있습니다.

REST API로 맨날 비슷한 API 반환값만 조금씩 다른거 만들다가 GraphQL를 사용해보니…아주…좋습니다……….

마치며

GraphQL 자체의 난이도는 엄청나게 어렵지 않았지만, Spring Cloud Gateway와의 연계성 때문에 가장 많은 시간을 쓴 것 같습니다..
이 부분은 바로 다음 글에서 자세하게 다뤄보겠습니다!

여러분은 시간 낭비 하지 마시고,,열받지 마시고,,,,,즐겁게 코딩하세요,,🥵



참고 자료

하나, ,

This post is licensed under CC BY 4.0 by the author.