TIL - 20260222

juni·2026년 2월 22일

TIL

목록 보기
275/316

0222 NestJS 심화 (5/N): GraphQL과 데이터베이스 쿼리


✅ 1. GraphQL이란 무엇인가? (REST API와의 차이점)

  • GraphQL은 API를 위한 쿼리 언어(Query Language)이자, 해당 쿼리를 실행하기 위한 서버 측 런타임입니다. 페이스북(현 메타)에서 개발했습니다.

  • REST API의 문제점:

    1. Over-fetching: 특정 엔드포인트가 항상 정해진 구조의 모든 데이터를 반환하므로, 클라이언트가 필요로 하는 것보다 더 많은 데이터를 받아오는 문제. (e.g., 사용자 목록에서 이름만 필요한데, 주소, 나이 등 모든 정보가 딸려옴)
    2. Under-fetching: 원하는 데이터를 완성하기 위해 여러 번의 요청을 보내야 하는 문제. (e.g., 게시글 정보를 가져온 후, 각 게시글의 작성자 정보를 얻기 위해 다시 여러 번 요청)
  • GraphQL의 해결책:

    • "필요한 것만 물어보고, 정확히 그것만 받는다."
    • 클라이언트가 원하는 데이터의 구조를 쿼리로 직접 정의하여 서버에 보내면, 서버는 정확히 그 구조에 맞는 데이터만 응답합니다.
    • 이를 통해 여러 번의 요청을 단 한 번의 요청으로 처리할 수 있습니다.
구분REST APIGraphQL
엔드포인트여러 개 (e.g., /users, /posts)단 하나 (e.g., /graphql)
데이터 구조서버가 정의 (고정적)클라이언트가 정의 (유연함)
요청 방식HTTP 메서드(GET, POST 등)로 동작 정의항상 POST 요청, 쿼리 내용으로 동작 정의

✅ 2. NestJS와 GraphQL 통합 (@nestjs/graphql)

  • NestJS는 @nestjs/graphql@nestjs/apollo 모듈을 통해 GraphQL 서버를 매우 쉽고 구조적으로 구축할 수 있도록 지원합니다.

➕ 2-1. 주요 개념 (Code-First 접근 방식)

  • Code-First: GraphQL 스키마를 직접 작성하는 대신, TypeScript 클래스와 데코레이터를 사용하여 코드를 작성하면, NestJS가 이를 기반으로 자동으로 GraphQL 스키마를 생성하는 방식입니다.
  1. @ObjectType():
    • GraphQL 스키마의 타입을 정의하는 클래스에 사용됩니다. (e.g., Movie 타입)
  2. @Field():
    • 해당 타입이 가지는 필드를 정의합니다. 데이터 타입(e.g., Int, String)을 명시할 수 있습니다.
  3. @Resolver():
    • 특정 타입에 대한 쿼리(데이터 조회)와 뮤테이션(데이터 변경)을 처리하는 메서드를 담고 있는 클래스입니다. Spring의 @Controller와 유사한 역할입니다.
  4. @Query():
    • 데이터를 조회하는 리졸버 메서드에 사용됩니다.
  5. @Mutation():
    • 데이터를 생성, 수정, 삭제하는 리졸버 메서드에 사용됩니다.
  6. @Args():
    • 쿼리나 뮤테이션에 전달되는 인자를 받기 위해 사용됩니다.

➕ 2-2. GraphQL 서버 설정 및 예시

// movie.dto.ts - GraphQL 타입을 위한 DTO
import { ObjectType, Field, Int } from '@nestjs/graphql';

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

  @Field()
  title: string;
}

// movies.resolver.ts
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
import { Movie } from './dto/movie.dto';

@Resolver(() => Movie) // 이 리졸버가 Movie 타입을 처리함을 명시
export class MoviesResolver {
  constructor(private readonly moviesService: MoviesService) {}

  @Query(() => [Movie]) // 'movies'라는 쿼리를 정의하고, 반환 타입은 Movie 배열
  movies(): Promise<Movie[]> {
    return this.moviesService.findAll();
  }

  @Mutation(() => Movie) // 'createMovie'라는 뮤테이션을 정의
  createMovie(@Args('title') title: string): Promise<Movie> {
    return this.moviesService.create(title);
  }
}

✅ 3. GraphQL과 TypeORM 연동

  • GraphQL 리졸버는 비즈니스 로직을 서비스에 위임하고, 서비스는 TypeORM 리포지토리를 통해 데이터베이스와 상호작용합니다. 이 구조는 REST API와 동일합니다.

  • 리졸버의 역할: 클라이언트의 GraphQL 쿼리를 해석하여, 적절한 서비스 메서드를 호출하고 그 결과를 반환하는 "번역가"이자 "중개자"입니다.

// movies.service.ts
@Injectable()
export class MoviesService {
  constructor(
    @InjectRepository(MovieEntity) // TypeORM 엔티티의 리포지토리 주입
    private readonly movieRepository: Repository<MovieEntity>,
  ) {}

  async findAll(): Promise<MovieEntity[]> {
    // DB에서 모든 영화 조회
    return this.movieRepository.find();
  }

  async create(title: string): Promise<MovieEntity> {
    const newMovie = this.movieRepository.create({ title });
    // DB에 새 영화 저장
    return this.movieRepository.save(newMovie);
  }
}```

*   **GraphQL Playground**: NestJS는 개발 환경에서 GraphQL API를 쉽게 테스트하고 문서를 확인할 수 있는 **GraphQL Playground**라는 도구를 자동으로 제공합니다.

---

### 📌 요약

*   **GraphQL**은 클라이언트가 **필요한 데이터의 구조를 직접 정의**하여 요청하는 API 쿼리 언어로, **Over-fetching과 Under-fetching 문제를 해결**합니다.
*   NestJS는 **`@nestjs/graphql`** 모듈과 **Code-First** 접근 방식을 통해, TypeScript 클래스와 데코레이터만으로 **GraphQL 스키마를 자동으로 생성**하고 서버를 쉽게 구축할 수 있습니다.
*   GraphQL의 **리졸버(`@Resolver`)**는 클라이언트의 쿼리와 뮤테이션을 받아, 기존에 만들어 둔 **서비스(Service)****TypeORM 리포지토리(Repository)**를 호출하여 비즈니스 로직을 처리합니다.
*   결과적으로, NestJS에서는 데이터베이스 로직을 재사용하면서, 기존의 REST API 엔드포인트와 GraphQL 엔드포인트를 **동시에 제공**하는 유연한 아키텍처를 쉽게 구현할 수 있습니다.

0개의 댓글