You will be fine

<GraphQL> 2. Query와 Type with Spring Boot

by BFine
반응형

 

가. Alias 와 Fragment

 a. Alias

  -  graphql은 쿼리에 별칭을 추가하여 데이터를 가져올 수가 있다.

  -  아래처럼 alias : 필드 형태로 데이터를 원하는 명칭으로 쉽게 변환이 가능하다.

 

 b. Fragment

  -  하나의 쿼리에서 return 필드들이 중복되는 경우가 발생할수도 있는데 그때마다 써주면 보기도 힘들고 만들기도 힘들수도 있다.

  -  그래서 graphql에서는 재사용이 가능한 Fragment 키워드를 제공하고 있다.

 

나. Operation Type

 a. query

  - 위에는 쿼리를 명시하지 않고 사용했는데 상용환경에서는 query 명시해주는게 모호함을 줄일수 있다.

  - echoMember를 두번 호출하는 쿼리를 하나로 요청했지만 인터셉터로 확인해보면 echoMember 밖에 나오지 않는다.

  - query 와 query 명(Operation name)을 명시해주면 인터셉터에서 해당 명칭으로 확인 할 수 있는 것을 볼 수 있다. 

 

 b. mutation

  -  DB에 Member를 저장 할 수 있도록 Repository를 추가해보았다.

  -  query가 데이터 읽는 작업 유형이라면 mutaion은 서버에 영향을 주는 쓰기라고 볼 수 있다.

@RequiredArgsConstructor
@Component
public class MemberMutationResolver implements GraphQLMutationResolver {

    private final MemberRepository memberRepository;

    public long createMember(String name, Integer age){
        Member member = Member.builder()
                .name(name)
                .age(age)
                .build();

        Member save = memberRepository.save(member);
        return save.getId();
    }
}

  - query와 동일하게 MutaionResolver 의 마크 인터페이스를 implements 해주어야 한다.  

  -  주의할부분은 query와는 다르게 mutation 키워드를 생략하면 유효성 오류가 발생한다. 

  -  하나 이상의 mutation을 보내는 경우 순차처리하기 때문에 race condition은 발생하지 않는다.

 

다. Type

 a. Scalar Type 

  -  graphql에서 제공해주는 것으로 자바에서는 기본형 변수와 느낌이 비슷하다.

  -  Int, Float, String, Boolean, ID 를 제공하고 있다.

     => 여기서 ID 타입은 고유 식별자로 String 형태를 가진다. (non human readable)

 

 b. enum

  -  graphql 은 열거형 타입도 제공하기 때문에 Java의 enum과 대응해서 사용할 수 있다.

public enum MemberType {
    GOLD,SILVER,BRONZE
}

  -  enum Type이나 Object Type 경우 직접 argument로 줄 수 없기 때문에 변수형태로 전달해야한다.

  -  따로 Java에서 enum으로 변환하지 않아도 graphqlResolver가 자동으로 번역을 해주기 때문에 그대로 사용할 수 있다는 장점이 있다.

 

 c. interface

  -  graphql 은 interface Type 지원하기 때문에 좀 더 유연한 쿼리가 가능하다.

      => java의 interface와 동일하게 생각할수도 있지만 상속에 가깝다. 

  -  하다보니 만들고 있는 예제가 이상해진 느낌이 들지만 interface가 어떤 느낌인지만 살펴보자

  -  interface 타입의 하위 타입들은 모두 Manager의 name을 필드로 가져가면서 자기만의 필드를 가질 수 있다.

@SuperBuilder
public class Manager {
    protected String name;
}
@SuperBuilder
public class BronzeManager extends Manager{
    protected int career;
}
@SuperBuilder
public class SilverManager extends Manager {
    private int score;
}

 -  schema와 대응하도록 클래스를 생성하고 상속하도록 했다.

@Slf4j
@RequiredArgsConstructor
@Component
public class MemberQueryResolver implements GraphQLQueryResolver {

    private final MemberRepository memberRepository;
    private final ModelMapper modelMapper;

    public List<MemberDTO> getMemberList(){
        List<Member> memberList = memberRepository.findAll();
        return memberList
                .stream().map(member -> modelMapper.map(member, MemberDTO.class))
                .collect(Collectors.toList());
    }

    public MemberDTO getMember(Long id){
        Member member = memberRepository.findById(id).orElse(null);
        MemberDTO memberDTO = modelMapper.map(member, MemberDTO.class);
        memberDTO.setManager(SilverManager.builder().name("kim").score(5).build());
        return memberDTO;
    }

}

 -  MemberDTOManager를 추가해서 return 할수있도록 변경했다. (Member Type의 grahpql schema도 추가)

 -  여기까지 하고 실행해보면 아래의 오류를 만날 수 있다.

Caused by: graphql.kickstart.tools.SchemaClassScannerError: Object type 'BronzeManager' implements a known interface, but no class could be found for that type name. Please pass a class for type 'BronzeManager' in the parser's dictionary.
@Bean
public SchemaParserDictionary schemaParserDictionary(){
    return new SchemaParserDictionary()
            .add(BronzeManager.class)
            .add(SilverManager.class);
}

  -  위와 같이 SchemaParserDictionary에 추가를 해주어야한다.

  -  다시 실행해서 테스트 해보면 

  -  아래에 ... on 타입명 형태로 명시해서 요청을 하면 내가 원했던 상속 받은 타입의 추가 정보까지 얻을 수 있다. 

  -  여기서 재미있는 부분은 

  -  실제 구현체로 사용하지 않았던 BronzeManager로 호출해보니 빈값이 return 되는 것을 볼수 있다. (name도 값이 없다.)

     => 번거롭게 SchemaParserDictionary 추가하도록 했는지 이해가 안됐는데 테스트해보니 클래스를 파싱할때 값이 아닌

           실제 클래스로 하기 때문에 필요한 것으로 이해할 수 있었다. 

 

 d. input

  -  지금까지는 scalar type으로 argument를 전달했는데 REST API POST 형태처럼 객체로도 전달이 가능하다.

  -  custom 객체를 인수로 전달하는 경우에 하용하는 것이 input 이다.

 

참고 : https://graphql.org/learn

반응형

블로그의 정보

57개월 BackEnd

BFine

활동하기