> For the complete documentation index, see [llms.txt](https://jakekwak.gitbook.io/nestjs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jakekwak.gitbook.io/nestjs/graphql/resolvers.md).

# Resolvers

## Resolvers

일반적으로 리졸버 맵을 수동으로 작성해야 합니다. 반면, `@nestjs/graphql` 패키지는 데코레이터가 제공하는 메타 데이터를 사용하여 리졸버 맵을 자동으로 생성합니다. 라이브러리 기본 사항을 배우기 위해 간단한 작성자 API를 작성합니다.

## Schema first

[이전](https://app.gitbook.com/graphql/quick-start) 챕터에서 언급했듯이, 스키마 첫번째 접근 방식에서는 SDL에서 유형을 수동으로 정의해야합니다 ([여기](http://graphql.org/learn/schema/#%20유형%20언어)).

```java
type Author {
  id: Int!
  firstName: String
  lastName: String
  posts: [Post]
}

type Post {
  id: Int!
  title: String!
  votes: Int
}

type Query {
  author(id: Int!): Author
}
```

GraphQL 스키마에는 `author(id: Int!): Author`라는 단일 쿼리가 포함되어 있습니다. 이제 `AuthorResolver`를 만들어 보자.

```typescript
@Resolver('Author')
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query()
  async author(@Args('id') id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty()
  async posts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}
```

> info **힌트** `@Resolver()`데코레이터를 사용하는 경우 클래스를 `@Injectable()`으로 표시할 필요가 없습니다. 그렇지 않으면 필요합니다.

`@Resolver()`데코레이터는 쿼리와 돌연변이 (`@Query()`와 `@Mutation()`데코레이터 모두)에 영향을 미치지 않습니다. 이 특정 클래스 내부의 각 `@ResolveProperty()`에는 부모 (이 경우에는 `Author` 유형) (`Author.posts`관계)가 있음을 Nest에게만 알려줍니다. 기본적으로 클래스 상단에`@Resolver()`를 설정하는 대신 메소드에 가깝게 수행할 수 있습니다.

```typescript
@Resolver('Author')
@ResolveProperty()
async posts(@Parent() author) {
  const { id } = author;
  return await this.postsService.findAll({ authorId: id });
}
```

그러나 하나의 클래스 안에 여러 개의 `@ResolveProperty()`가 있다면, 반드시 `@Resolver()`를 추가해야 합니다.

일반적으로 우리는 메소드 이름으로 `getAuthor()` 또는 `getPosts()`와 같은 것을 사용합니다. 데코레이터의 괄호 사이에서 실제 이름을 이동하면 쉽게 할 수 있습니다.

```typescript
@Resolver('Author')
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query('author')
  async getAuthor(@Args('id') id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty('posts')
  async getPosts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}
```

> info **힌트** `@Resolver()`데코레이터는 메소드 수준에서도 사용할 수 있습니다.

## Typings

[이전](https://app.gitbook.com/graphql/quick-start) 챕터에서 타이핑 생성 기능 (`outputAs: 'class'` 포함)을 활성화 했다고 가정하면 응용 프로그램을 실행하면 다음 파일이 생성됩니다.

```typescript
export class Author {
  id: number;
  firstName?: string;
  lastName?: string;
  posts?: Post[];
}

export class Post {
  id: number;
  title: string;
  votes?: number;
}

export abstract class IQuery {
  abstract author(id: number): Author | Promise<Author>;
}
```

클래스를 사용하면 **데코레이터**를 사용하여 유효성 검사 목적에서 매우 유용합니다 ([여기](https://app.gitbook.com/techniques/validation) 참조). 예를 들면 다음과 같습니다.

```typescript
import { MinLength, MaxLength } from 'class-validator';

export class CreatePostInput {
  @MinLength(3)
  @MaxLength(50)
  title: string;
}
```

> warning **알림** 입력 및 파라미터의 자동 유효성 검사를 활성화하려면 `ValidationPipe`를 사용해야합니다. 유효성 검사 [여기](https://app.gitbook.com/techniques/validation) 또는 파이프 [여기](https://app.gitbook.com/pipes)에 대해 자세히 알아보십시오.

그럼에도 불구하고 데코레이터를 자동으로 생성된 파일에 직접 추가하면 연속된 각 변경에 따라 **제거됩니다**. 따라서 별도의 파일을 작성하고 생성된 클래스를 간단히 확장해야합니다.

```typescript
import { MinLength, MaxLength } from 'class-validator';
import { Post } from '../../graphql.ts';

export class CreatePostInput extends Post {
  @MinLength(3)
  @MaxLength(50)
  title: string;
}
```

## Code first

코드 우선 접근 방식에서는 SDL을 직접 작성할 필요가 없습니다. 대신 데코레이터 만 사용합니다.

```typescript
import { Field, Int, ObjectType } from 'type-graphql';
import { Post } from './post';

@ObjectType()
export class Author {
  @Field(type => Int)
  id: number;

  @Field({ nullable: true })
  firstName?: string;

  @Field({ nullable: true })
  lastName?: string;

  @Field(type => [Post])
  posts: Post[];
}
```

`Author`모델이 생성되었습니다. 이제 누락된 `Post` 클래스를 만들어 봅시다.

```typescript
import { Field, Int, ObjectType } from 'type-graphql';

@ObjectType()
export class Post {
  @Field(type => Int)
  id: number;

  @Field()
  title: string;

  @Field(type => Int, { nullable: true })
  votes?: number;
}
```

모델이 준비되었으므로 리졸버 클래스로 이동할 수 있습니다.

```typescript
@Resolver(of => Author)
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query(returns => Author)
  async author(@Args({ name: 'id', type: () => Int }) id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty()
  async posts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}
```

일반적으로 우리는 메소드 이름으로 `getAuthor()` 또는 `getPosts()`와 같은 것을 사용합니다. 실명을 데코레이터로 옮겨서 쉽게 할 수 있습니다.

```typescript
@Resolver(of => Author)
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query(returns => Author, { name: 'author' })
  async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty('posts')
  async getPosts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}
```

일반적으로 이러한 객체를 `@Args()`데코레이터로 전달할 필요는 없습니다. 예를 들어, 식별자 유형이 문자열이면 다음과 같이 구성하면됩니다.

```typescript
@Args('id') id: string
```

그러나 `숫자`유형은 예상되는 GraphQL 표현 (`Int` 대 `Float`)에 대한 충분한 정보를 `type-graphql`에 제공하지 않으므로 타잎 레퍼런스를 **명시적으로** 전달해야 합니다.

또한 전용 `AuthorArgs`클래스를 만들 수 있습니다.

```typescript
@Args() id: AuthorArgs
```

다음 본문으로:

```typescript
@ArgsType()
class AuthorArgs {
  @Field(type => Int)
  @Min(1)
  id: number;
}
```

> info **힌트** `@Field()`와 `@ArgsType()`데코레이터는 모두 type-graphql 패키지에서 가져오고, `@Min()`은 클래스 유효성 검사기에서 가져옵니다.

또한 이러한 클래스는 `ValidationPipe`([여기](https://app.gitbook.com/techniques/validation) 읽기)에서 매우 잘 작동합니다.

## Decorators

전용 데코레이터를 사용하여 다음과 같은 주장을 언급할 수 있습니다. 아래는 제공된 데코레이터와 이들이 나타내는 일반 아폴로 파라미터를 비교한 것입니다.

| `@Root()` and `@Parent()`  | `root`/`parent`              |
| -------------------------- | ---------------------------- |
| `@Context(param?: string)` | `context` / `context[param]` |
| `@Info(param?: string)`    | `info` / `info[param]`       |
| `@Args(param?: string)`    | `args` / `args[param]`       |

## Module

여기에서 완료되면, 예를 들어 새로 작성된 `AuthorsModule` 내부에 `AuthorResolver`를 등록해야 합니다.

```typescript
@Module({
  imports: [PostsModule],
  providers: [AuthorsService, AuthorResolver],
})
export class AuthorsModule {}
```

`GraphQLModule`은 메타 데이터를 반영하고 클래스를 올바른 리졸버 맵으로 자동 변환합니다. 알아야 할 유일한 것은 이 모듈을 어딘가에 가져와야 한다는 것입니다. 따라서 Nest는 `AuthorsModule`이 실제로 존재함을 알게됩니다.

> info **힌트** GraphQL 쿼리에 대한 자세한 내용은 [여기](http://graphql.org/learn/queries/)를 참조하십시오.
