본문 바로가기

# GraphQL/TypeGraphQL

[TypeGraphQL] @InputType, @ArgsType

노드의 분류

GraphQL에서는 요청응답을 기준으로 노드의 타입을 크게 2가지로 나눌 분류할 수 있습니다.

  • InputType
  • OutputType

ObjectType

OutputType은 사용자에게 데이터를 반환활 수 있는 타입이며, ObjectType을 포함한 Primitive ValueScalar Type이 포함됩니다.


InputType

반대로 InputType은 사용자가 데이터를 전달할 수 있는 타입이지만, OutputType같이 종류의 집합이 아니라 ObjectType처럼 단일로 존재할 수 있는 노드입니다. ObjectType이 여러 필드를 묶어놓듯이 InputType은 여러 아규먼트를 묶습니다.

TypeGraphQL에서 InputType을 생성하려면 다음 데코레이터 중 하나를 사용해야 합니다.

  • @InputType
  • @ArgsType

이 중에서 @ArgsType은 표준이 아니라, TypeGraphQL에서 지원하는 특별한 기능입니다. 둘 중 어느쪽을 사용해도 InputType을 생성할 수 있지만 약간의 차이점은 있습니다.


@InputType

@InputType은 실제로 GraphQLInputType을 생성하고 flat되지 않은 입력을 받는데 사용합니다. 타입스크립트로 비유하자면 다음과 같은 형태입니다.

function foo(in: { x:string; y:string; z:string; }){
    console.log(in.x);
    console.log(in.y);
    console.log(in.z);
};

@Arg(name, ()=>T)를 사용하여 함수의 인자로 등록할 수 있습니다.

@InputType()
class PositionInput {
    @Field(() => Float)
    x!: number;

    @Field(() => Float)
    y!: number;

    @Field(() => Float)
    z!: number;
}

@Resolver()
class QueryResolver {
    @Query(() => String)
    echo(@Arg("position", () => PositionInput) pos: PositionInput): string {
        return `(${pos.x}, ${pos.y}, ${pos.z})`;
    }
}

@ArgsType

반면에 @ArgsTypeTypeGraphQL에서 제공하는 문법설탕입니다. 가상의 타입이기 때문에 실제로 GraphQLInputType을 생성하지는 않지만, 각각의 필드를 @Arg로 나누어 등록하므로 flat된 입력을 받는데 사용할 수 있습니다. 타입스크립트로 비유하자면 다음과 같은 형태입니다.

function bar(x: string, y: string; z: string){
    console.log(x);
    console.log(y);
    console.log(z);
};

@Args(()=>T)를 사용하여 함수의 인자로 등록할 수 있습니다.

@ArgsType()
class PositionInput {
    @Field(() => Float)
    x!: number;

    @Field(() => Float)
    y!: number;

    @Field(() => Float)
    z!: number;
}

@Resolver()
class QueryResolver {
    @Query(() => String)
    echo(@Args(() => PositionInput) pos: PositionInput): string {
        return `(${pos.x}, ${pos.y}, ${pos.z})`;
    }
}

이것은 각각의 입력을 @Arg로 받는 것과 동일합니다.

@Resolver()
class QueryResolver {
    @Query(() => String)
    echo(@Arg("x", () => String) x: string,
         @Arg("y", () => String) y: string,
         @Arg("z", () => String) z: string): string {
        return `(${x}, ${y}, ${z})`;
    }
}

동시에 적용하기

또는 둘 다 한꺼번에 적용할 수 있습니다.

@InputType()
@ArgsType()
class PositionInput {
    @Field(() => Float)
    x!: number;

    @Field(() => Float)
    y!: number;

    @Field(() => Float)
    z!: number;
}

데이터 입력받기

사용자가 GQL에서 사용되는 데이터를 전달하는 방법은 2가지가 있습니다.

  • literal : 쿼리에 데이터를 하드코딩
  • input variables : 요청 헤더에 데이터를 삽입

literal

쿼리에 직접 데이터를 삽입하는 방식입니다.

query($a: Int! = 1, $b: Int! = 17, $c: Int! = 255) {
    func(a: $a, b: $b, c: $c) {
        error
        data
    }
}

데이터가 쿼리 그 자체에 있기 때문에 body를 봐야 데이터를 알 수 있습니다. 요청을 보면 이런 느낌입니다.

{
    body : `
        query($a: Int! = 1, $b: Int! = 16, $c: Int! = 256) {
            func(a: $a, b: $b, c: $c) {
            error
            data
            }
        }
    `,

    header : {

    }
}

input-variables

반면에 이 방식은 데이터를 header에 삽입하여 쿼리와 데이터를 분리합니다. body의 쿼리를 보아도 데이터가 직접 나오지 않습니다. 아래의 쿼리를 사용해야 한다면 r, g, b를 요청 헤더에 각각 삽입해야 합니다.

query($a: Int!, $b: Int!, $c: Int!) {
    func(a: $a, b: $b, c: $c) {
        error
        data
    }
}

요청을 보면 이런 느낌입니다.

{
    body : `
        query($a: Int!, $b: Int!, $c: Int!) {
            func(a: $a, b: $b, c: $c) {
            error
            data
            }
        }
    `,

    header : {
        a: 1,
        b: 16,
        c: 256
    }
}

예제 다운로드

이 포스팅에 사용된 전체 코드는 여기에서 확인할 수 있습니다.