본문 바로가기
개발/Spring

[Spring] Springboot GraphQL 게시판 CRUD 만들기1

by onethejay 2022. 5. 19.
728x90

이전 포스팅(GraphQL 실습해보기)에서 Node.js를 통해 GraphQL을 실습했습니다.

이번에는 Spring에서 사용할 수 있는 Spring GraphQL 게시판 CRUD 서비스를 구현해보려고 합니다.
2022년 5월 18일 현재 Springboot 2.7이상 3.0 이하 베타에서만 가능합니다.
2022년 5월 19일 2.7.0이 정식 릴리즈 되었습니다. 베타가 아닌 정식 버전으로 사용 가능합니다.

spring.io의 DocumentationSample 프로젝트를 기반으로 합니다.

프로젝트 생성

먼저 Spring Initializer로 프로젝트를 생성합니다. (start.spring.io에서도 가능합니다.)

Springboot 버전은 2.7.0, 의존성은 아래 5가지를 선택합니다.

  • Lombok
  • Spring Web
  • H2 Database
  • Spring Data JPA
  • Spring GraphQL

프로젝트가 열리면 build.gradle의 dependencies를 아래 내용으로 변경합니다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-graphql'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.modelmapper:modelmapper:3.1.0'  // 추가
    implementation group: 'com.h2database', name: 'h2', version: '1.3.176'  //h2 변경 

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework:spring-webflux'
    testImplementation 'org.springframework.graphql:spring-graphql-test'
}

application.properties 파일은 yml파일로 변경하고 resources 아래에 graphql 폴더를 생성해줍니다.

application.yml 파일은 아래 내용을 추가합니다.

server:
  port: 8080

spring:
  jackson:
    property-naming-strategy: SNAKE_CASE

  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console

  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
    generate-ddl: true
    defer-datasource-initialization: true

  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb

graphql 폴더 안에 schema.graphqls 파일을 생성하고 아래 내용을 추가합니다.

type Query {
    boardList: [Board]
    board(id: ID): Board
}

type Board {
    id: ID
    title: String
    content: String
}

resources 아래에 import.sql 파일을 생성하고 아래 쿼리를 추가합니다.

INSERT INTO BOARD(ID, TITLE, CONTENT) VALUES ('1', '제목1', '내용1');
INSERT INTO BOARD(ID, TITLE, CONTENT) VALUES ('2', '제목2', '내용2');
INSERT INTO BOARD(ID, TITLE, CONTENT) VALUES ('3', '제목3', '내용3');
INSERT INTO BOARD(ID, TITLE, CONTENT) VALUES ('4', '제목4', '내용4');

위에서 생성한 쿼리가 들어갈 테이블과 연결될 JPA Entity를 생성합니다.

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name="BOARD")
@Entity
public class BoardEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
}

Entity 까지 생성했다면 서버를 시작합니다.

BoardController 생성

GraphQL의 Query, Mutation Resolver를 담당할 컨트롤러를 구성합니다.

먼저 web 패키지 안에 BoardController.java를 생성합니다. 테스트를 위해 컨트롤러의 테스트 파일도 같이 생성합니다.

import com.example.springbootgraphql.entity.BoardEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;

@RequiredArgsConstructor
@RestController
public class BoardController {

    @QueryMapping
    public BoardEntity board(@Argument Long id) {
        return null;
    }

    @QueryMapping
    public List<BoardEntity> boardList() {
        return Collections.emptyList();
    }
}

테스트 파일 생성까지 완료한 프로젝트의 구성입니다.

이어서 컨트롤러 테스트 파일 안에 게시글 1개를 가져오는 테스트 코드를 생성합니다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureGraphQlTester;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.GraphQlTester;

@SpringBootTest
@AutoConfigureGraphQlTester
class BoardControllerTests {

    @Autowired
    private GraphQlTester graphQlTester;


    @DisplayName("1. 게시글 1개 가져오기")
    @Test
    void test_1(){
        this.graphQlTester.documentName("board")
                .variable("id", 1L)
                .execute()
                .path("board.title")
                .entity(String.class)
                .isEqualTo("제목1");
    }
}

테스트를 실행하면 document를 찾을 수 없다고 에러가 발생합니다.

테스트를 위해서는 this.graphQlTester.documentName에 입력한 파라미터에 맞는 graphql 파일을 테스트 resources에 생성해주어야 합니다.

Test graphql 파일 생성

test/resources/graphql-test 폴더를 생성하고 테스트를 위한 board.graphql 파일을 생성합니다.

# board.graphql
query ($id: ID){
    board(id: $id) {
        id
        title
        content
    }
}

test/resources에 board.graphql 파일까지 생성했다면 다시 테스트 코드를 호출합니다.

여전히 테스트에 통과할 수 없지만 아까와는 다른 에러 로그가 나타났습니다.
로그에는 board의 title을 확인하니 null이 있다고 합니다.

이 로그를 통해 BoardController.java의 board 메서드까지 접근했다는 것을 생각해볼 수 있습니다.

확실하게 확인하기 위해 콘솔 로그를 추가하고 다시 호출해봅니다.

@RequiredArgsConstructor
@RestController
public class BoardController {

    @QueryMapping
    public BoardEntity board(@Argument Long id) {
        System.out.println("호출되었습니다.");
        return null;
    }

    @QueryMapping
    public List<BoardEntity> boardList() {
        return Collections.emptyList();
    }
}

예상이 맞았습니다. 테스트 코드를 통해 컨트롤러의 board 메서드까지 접근했고 콘솔 로그를 출력했습니다.

테스트 통과를 위해 board 메서드가 BoardEntity를 return하도록 수정합니다.

@RequiredArgsConstructor
@RestController
public class BoardController {

    @QueryMapping
    public BoardEntity board(@Argument Long id) {
        System.out.println("호출되었습니다.");
        return new BoardEntity(1L, "제목1", "내용1");
    }

    @QueryMapping
    public List<BoardEntity> boardList() {
        return Collections.emptyList();
    }
}

다시 테스트를 진행합니다.

정상적으로 테스트를 통과하였습니다. 그러나 해당 방법은 단지 통과를 위한 방법이므로 추가적인 작업이 필요합니다.

다음에 업로드할 포스팅들을 통해 API 컨트롤러가 아닌 GraphQL 컨트롤러 및 서비스를 하나씩 만들어 보겠습니다.

728x90

댓글