일단 시작.

GraphQL - mutation 실행 시 필드 값이 null로 들어오는 오류 본문

STUDY/Trouble Shooting

GraphQL - mutation 실행 시 필드 값이 null로 들어오는 오류

꾸양! 2024. 4. 18. 17:43

1. Issue Description


프로젝트에서 정의했던 mutation 테스트 시 필드에 null이 들어오는 이슈 발생

▶ mutation 정의

type Mutation {
    signup(input: SignupRequestDto!): ApiResponseDto!
}

input SignupRequestDto {
    email: String!
    password: String!
    nickname: String!
}

type ApiResponseDto {
    message: String!
}

▶ Altair에서 테스트 했던 mutation

mutation {
  signup(
    input: { 
      email: "string", 
      password: "string", 
      nickname: "string"
      }
    ) 
  {
    message
  }
}

▶ 발생한 예외

jakarta.validation.ConstraintViolationException: 
signup.signupRequestDto.password: 비밀번호 칸은 비울 수 없습니다., 
signup.signupRequestDto.email: 이메일 칸은 비울 수 없습니다., 
signup.signupRequestDto.nickname: 닉네임 칸은 비울 수 없습니다.

 

2. 원인 추론


(1) validation의 문제인지 다른 문제인지 확인

@valid 삭제하고 다시 테스트

// @Valid 삭제한 코드
@MutationMapping
public ApiResponseDto signup(@Argument("input") SignupRequestDto signupRequestDto) {
        
    System.out.println(signupRequestDto.getEmail());
    System.out.println(signupRequestDto.getPassword());
    System.out.println(signupRequestDto.getNickname());

    memberService.signup(signupRequestDto);
    return new ApiResponseDto("회원가입이 완료되었습니다.");
}

결과

null
null
null

→ validation의 문제는 아닌 것 같다.

 

(2) Debugging 하기

디버깅을 해보니 Spring Boot Request까지는 필드가 들어온다. 요청했던 json 형식의 text가 key, value 값으로 잘 나뉘어지는 것도 확인할 수 있었다. 다만, Request와 자바로 정의한 Dto를 서로 맵핑해줄 때 갑자기 null이 되어버렸다.

→ 맵핑에서 문제가 생기는 것으로 판단
→ 이제 문제: 맵핑에서 문제가 왜 생기는지를 모른다. 맵핑하려면 뭐가 필요한데 내가 그걸 안해줬을 가능성이 높다.

 

(3) 구글링

→ 구글링을 해봐도 잘 나오지를 않았다.

  • 검색어 : graphql null mutation
  • 대부분 스키마 파일과 리졸버에서 정의한 메서드와 인자의 이름이 일치하지 않는 경우가 많았다. 하지만 나에게 적용할 수 있는 케이스는 아니었다.

 

(4) Github에서 나랑 비슷한 사양의 코드 찾기

GraphQL이 Spring Boot 2와 3에서 코드가 서로 다르고(정확히는 3부터 boot starter에 GraphQL 라이브러리가 생겼는데 이 라이브러리가 전에 2에서 외부에서 끌어쓰던 라이브러리와는 코드가 일부 다른 것으로 알고 있다.) 라이브러리도 다양하기 때문에 내 사양에 맞는 코드를 찾는 것이 중요했다.

▶ 검색어 : Spring Boot 3 GraphQL

코드를 찾고 나랑 뭐가 다른지 비교하다가 DTO에 @Data 어노테이션이 있는 것을 발견했다.

@Data 어노테이션이란?

@Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 합쳐 놓은 어노테이션이다. 주로 DTO에 많이 쓴다고 한다.

내 DTO에는 @Getter 어노테이션만 달아준 상태였다.

→ 기존 @Getter 어노테이션을 지우고 @Data 어노테이션으로 바꾸어 다시 테스트

매핑 성공!!

하지만!

  1. @Data의 모든 기능이 필요한 것이 아니었고
  2. 어떤 어노테이션으로 인해 맵핑이 가능했는지 알아야 하므로 @Data를 제거하고 @Data가 가지고 있는 여러 어노테이션을 넣으면서 테스트 했더니 @Setter 어노테이션이 달리니까 맵핑이 가능했다.

 

3. 결과


나중에 Spring DTO mapping Setter로 검색해서 알아본 결과,

Spring에서는 클라이언트에서 JSON 형식으로 Request를 보낼 때, Setter와 기본 생성자를 사용하여 DTO와 맵핑한다.

는 사실을 알 수 있었다.