<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;
}
}
- MemberDTO에 Manager를 추가해서 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 이다.
'공부 > GraphQL' 카테고리의 다른 글
<GraphQL> 5. 비동기 처리 / N+1 문제 with Spring Boot (0) | 2022.04.06 |
---|---|
<GraphQL> 4. 동물원 예제 만들기 with Spring Boot (0) | 2022.04.02 |
<GraphQL> 3. Exception Handling with Spring Boot (0) | 2022.03.27 |
<GraphQL> 1. 설정하기 with Spring Boot (2) | 2022.03.14 |
<GraphQL> 0. GraphQL 이란 (0) | 2022.03.14 |
블로그의 정보
57개월 BackEnd
BFine