CustomContext 생성하기

GraphQL의 요청을 핸들링하는 GraphQLServletContextBuilder를 implements하여 grpahQL요청에 대해 커스텀Context를 반환하도록 만들 수 있습니다.

예를들어 요청의 헤더에 접근하여 Context에 특정 헤더값을 저장하는 식으로의 custom이 가능합니다. 이번 예시에서는 헤더에 api-key가 포함되어있고 이 key를 분리하여 context도 저장하고있는 context를 만들어보려고 합니다.


CustomContext 정의

GraphQLServletContext를 implements하여 총6개의 메서드들을 정의해주면 됩니다.

@Getter
@RequiredArgsConstructor
public class CustomGraphQLContext implements GraphQLServletContext {

    private final String apiKey;
    private final GraphQLServletContext context;

    @Override
    public List<Part> getFileParts() {
        return context.getFileParts();
    }

    @Override
    public Map<String, List<Part>> getParts() {
        return context.getParts();
    }

    @Override
    public HttpServletRequest getHttpServletRequest() {
        return context.getHttpServletRequest();
    }

    @Override
    public HttpServletResponse getHttpServletResponse() {
        return context.getHttpServletResponse();
    }

    @Override
    public Optional<Subject> getSubject() {
        return context.getSubject();
    }

    @Override
    public DataLoaderRegistry getDataLoaderRegistry() {
        return context.getDataLoaderRegistry();
    }
}

기본적인 ServletContext의 정보를 갖고있을 context 변수와 예시로 헤더값에 포함된 api-key를 저장할 필드를 포함한 커스텀context를 정의 해주면 됩니다.


GraphQLServeltContextBuilder

실제로 GraphQLServletContext를 생성하는 Builder도 구현해주어야 우리가 생성한 CustomContext로 Context가 생성이 됩니다.

@Component
@RequiredArgsConstructor
public class CustomGraphQLContextBuilder implements GraphQLServletContextBuilder {

    @Override
    public GraphQLContext build(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse) {

        String apiKey = httpServletRequest.getHeader("api-key");

        DefaultGraphQLServeltContext context = DefaultGraphQLServletContext.createServletContext()
                .with(httpServletRequest)
                .with(httpServletResponse)
                .build();

        return new CustomGraphQLContext(apiKey, context);
    }

    @Override
    public GraphQLContext build(Session session, HandshakeRequest handshakeRequest) {
        throw new IllegalStateException("Unsupported");
    }

    @Override
    public GraphQLContext build() {
        throw new IllegalStateException("Unsupported");
    }
}

build() 메서드 3개를 Override해 정의 해주어야 하는데, session방식은 사용하지 않기 때문에 Exception처리 해주었고 build의 매개변수인 httpServletRequest의 getHeader를 통해 api-key의 값을 꺼내고 만들어둔 CustomGraphQLContext에 apiKey를 포함하여 return하면 실제 QueryResolver를 처리하는 Context는 우리가 만든 CustomContext가 처리하게 됩니다.

그래서 이전에 설명한 Environment의 getContext()메서드를 통해서 특정 필드에 접근도 가능합니다.

@Component
@Slf4j
public class WetayoQuery implements GraphQLQueryResolver {
    //... 각종 Service들과 Constructor

    public List<RouteStationGraphQLDto> getStations(Double x, Double y, Double distance, DataFetchingEnvironment e) {
        CustomGraphQLContext context = e.getContext();
        log.info(context.getApiKey());  //헤더의 api-key  값 출력

        if(!context.getApiKey().equals("123456789")) throw new UnAuthorizedAccessExeption("유효하지 않은 api key");

        //비즈니스 로직

        return routeStationDtos;
    }
}

위와 같이 Controller의 메서드에서 context의 필드에도 접근,조회가 가능하게 된다. 하지만 위의 방법은 단일 책임 원칙에 위배되고 AOP스럽지도 못하기 때문에 썩 그렇게 좋은 코드는 아니고 예시로만 봐주면 될 것 같습니다.