본문 바로가기

# GraphQL/GraphQL.js

타입스크립트 GraphQL 재귀호출 recursion




재귀적으로 반환할 수 있을까?


어떤 상황에서는 재귀적인recursive 반환이 필요할 수 있습니다.

아래의 시나리오를 살펴봅시다.



위의 스키마 정의 언어SDL로 표현되는 재귀적 가산기가 있습니다.


add로 숫자를 더할때마다 history에 추가되고,

total은 지금까지의 모든 숫자를 더해서 반환해야 합니다.



예시 출력 :


이 가산기를 어떻게 GraphQL로 표현할 수 있을까요?




(ERROR)  정의되기 전에 사용하지 마세요!


평범한 방법으로 adder를 만들려고 시도하면,

십중팔구 다음과 같은 에러가 뜹니다.



Block-scoped variable 'adder' used before its declaration.ts(2448)

선언이 끝나고 사용하세요.


Variable 'adder' is used before being assigned.ts(2454)

할당이 끝나고 사용하세요.


adder의 선언도중에 adder를 사용하니 발생하는 에러입니다.

정상적인 방법으로는 재귀를 사용할 수 없는 것 같습니다.


해결방법은 말로만 하면 간단합니다.

field에 쓰인 adder의 계산시점을 최대한 늦추는 것입니다.


먼저 올바르지 않은 사례부터 살펴보겠습니다.





(ERROR)  꼼수쓰지 마세요! - 1


adder 선언문을 앞으로 빼내고 할당문은 뒤에 두면 어떨까요?


그냥쓰면 아래처럼 오류표시가 발생하고,

느낌표 두개를 붙이면 오류표시를 무시할 수 있습니다.




예상대로 어림도 없었네요.


이것만으론 필드에 쓰인 adder의 계산시점이 뒤로 밀리지 않기 때문에,

당시 adder의 값인 undefined가 반환되었기 때문입니다.






(ERROR)  꼼수쓰지 마세요! - 2


뒷쪽에 adder를 반환하는 reflect 함수를 만들면 어떨까요?

let adder = new GraphQLObjectType({
    name: "adder",
    fields: {
        add: {
            type: reflectAdder(),
            ...
        }
        ...
    }
});


function reflectAdder(): GraphQLObjectType {
    return adder;
}


이것도 필드에 쓰인 adder의 계산시점이 밀리지 않았네요.

reflectAdder가 호출되었지만, 당시 adder값인 undefined가 반환됐습니다.





도큐먼트가 정답이다.


정답은 도큐먼트에 있었습니다.




필드는 GraphQLFieldConfigMap 또는 Thunk를 인자로 받고,

Thunk는 ( ) => GraphQLFieldConfigMap 형식의 함수입니다.



청크Thunk의 정의를 찾아보니... 

https://en.wikipedia.org/wiki/Thunk

In computer programming, a thunk is
컴퓨터 공학에서 청크란,

a subroutine used to inject an additional calculation into another subroutine. 
어떤 서브루틴 안에 인젝션되는 서브루틴을 말한다.

Thunks are primarily used to delay a calculation until its result is needed, 
청크는 어떤 결과값의 계산을 필요시점까지 늦추기 위해 주로 사용된다.

or to insert operations at the beginning or end of the other subroutine. 
또는 어떤 서브루틴의 시작과 끝에 삽입되어 사용된다.

우리가 찾고있던 방법론이 그대로 나와있네요!



즉, 익숙한 방식으로 짜고.

화살표 함수로 한번 감싸면 되지 않을까요?

let adder : GraphQLObjectType = new GraphQLObjectType({
    name: "adder",
    fields: () => ({ // <- 화살표 함수로 감쌀것
        add: {
            type: adder,
            args: {
                number: { type: GraphQLInt }
            },
            ...
        },
        ...
    })
})


예상은 옳았습니다.

재귀 쿼리도 정상적으로 작동하네요.




요약하자면

  • 하던대로 짜고,

  • 화살표 함수로 묶습니다.


알고보면 간단하지만,

그 방법을 찾기 어려운 이슈였습니다.



전체 코드는 아래에서 확인할 수 있습니다.

https://github.com/MyAeroCode/typescript-serverless-study/tree/master/src/ex-021