# Resolvers

## Resolvers

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

## Schema first

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

```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/)를 참조하십시오.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jakekwak.gitbook.io/nestjs/graphql/resolvers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
