TypeORM 및 PostgreSQL을 사용하여 Nest.js CRUD REST API를 구축하는 방법

Nest.js는 다른 Node.js 프레임워크와 마찬가지로 견고하고 확장성 있는 백엔드 서비스를 구축하기 위한 광범위한 도구 모음을 제공합니다. 그럼에도 불구하고, Nest.js 환경에서 생성, 조회, 수정 및 삭제(CRUD) 작업을 효율적으로 수행하는 방법을 이해하는 것은 매우 중요합니다. 이는 API 개발의 가장 기본적이고 핵심적인 부분입니다.

이 글에서는 TypeORM과 PostgreSQL 데이터베이스를 활용하여 Nest.js 기반의 CRUD REST API를 구축하는 과정을 상세히 안내합니다.

Nest.js 시작하기

가장 먼저 Nest.js 명령줄 인터페이스 도구를 설치해야 합니다.

 npm i -g @nestjs/cli 

새 프로젝트를 생성하려면 다음 명령어를 실행하세요.

 nest new crud-app 

CLI 도구는 선호하는 패키지 관리자를 선택하도록 요청하며, 여기서는 npm을 사용합니다.

CLI는 애플리케이션을 실행하는 데 필요한 모든 필수 구성 파일과 초기 종속성을 갖춘 기본적인 Nest.js 프로젝트를 자동으로 생성합니다.

프로젝트 디렉토리로 이동한 후 개발 서버를 시작합니다.

 cd crud-app
npm run start

이 프로젝트의 전체 코드는 다음 GitHub 저장소에서 확인할 수 있습니다.

PostgreSQL 데이터베이스 생성

이 튜토리얼에서는 클라우드 PostgreSQL 인스턴스를 사용하지만, 로컬 PostgreSQL 데이터베이스를 설정하여 사용할 수도 있습니다. PostgreSQL은 Windows, macOS, Linux 환경에서 모두 설치 가능합니다.

클라우드 PostgreSQL 인스턴스를 설정하려면:

  • ElephantSQL 웹사이트로 이동하여 회원 가입 후 계정 개요 페이지에 로그인합니다.
  • 페이지 왼쪽 상단에 있는 “새 인스턴스 만들기” 버튼을 클릭하여 새로운 애플리케이션 인스턴스를 생성합니다.
  • 인스턴스 이름을 지정하고, 무료 플랜을 선택한 다음, 원하는 리전을 선택하여 설정을 완료합니다.
  • 데이터베이스 인스턴스가 생성되면 설정 페이지로 이동하여 제공된 데이터베이스 연결 URL을 복사합니다.
  • 데이터베이스 연결 구성

    프로젝트 최상위 디렉토리에 `.env` 파일을 생성하고, 복사한 데이터베이스 연결 URL을 아래와 같이 추가합니다.

     DATABASE_URL="<your connection url here>" 

    이제 필요한 패키지들을 설치합니다.

     npm install pg typeorm @nestjs/typeorm @nestjs/config 

    다음으로, CLI 도구를 사용하여 데이터베이스 모듈을 생성합니다.

     nest g module database 

    `database/database.module.ts` 파일을 열고 다음 데이터베이스 구성 코드를 추가합니다.

     import { Module } from '@nestjs/common';
    import { ConfigModule, ConfigService } from '@nestjs/config';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { User } from '../users/models/user.entity';

    @Module({
      imports: [
        TypeOrmModule.forRootAsync({
          imports: [ConfigModule],
          inject: [ConfigService],

          useFactory: async (configService: ConfigService) => ({
            type: 'postgres',
            url: configService.get('DATABASE_URL'),
            entities: [User],
            synchronize: true
          }),
        }),
      ],
    })

    export class DatabaseModule {}

    이 데이터베이스 모듈은 연결에 필요한 매개변수인 데이터베이스 URL을 사용하여 TypeORM 모듈을 구성하여 데이터베이스 연결을 관리합니다.

    또한, User 엔터티를 정의하여 PostgreSQL 데이터베이스 테이블에 저장될 데이터의 구조와 속성을 설정합니다.

    이 단계에서는 아직 사용자 엔터티를 생성하지 않았으므로 코드에서 오류가 발생할 수 있습니다. 다음 단계에서 엔터티를 생성합니다.

    app.module.ts 파일 업데이트

    다음으로, 데이터베이스 모듈 설정을 포함하도록 애플리케이션 모듈을 업데이트합니다.

     import { Module } from '@nestjs/common';
    import { ConfigModule } from '@nestjs/config';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { DatabaseModule } from './database/database.module';

    @Module({
      imports: [
        ConfigModule.forRoot({
          envFilePath: '.env',
        }),
        DatabaseModule,
      ],

      controllers: [AppController],
      providers: [AppService],
    })

    export class AppModule {}

    사용자 모듈 정의

    사용자 모듈은 API의 CRUD 기능을 구현하는 데 필요한 로직을 캡슐화하고 관리하는 핵심 구성 요소입니다.

    다음 터미널 명령어를 실행하여 API의 사용자 모듈을 생성합니다.

     nest g module users 

    CLI 도구는 사용자 모듈을 생성하는 동시에 `app.module.ts` 파일도 자동으로 업데이트하여 새로운 모듈을 애플리케이션에 통합합니다.

    사용자 엔터티 생성

    TypeORM은 JavaScript 객체를 데이터베이스 테이블에 매핑하여 TypeScript를 사용하는 애플리케이션에서 데이터베이스 상호 작용을 간소화하는 ORM(객체 관계 매핑) 라이브러리입니다.

    TypeORM을 사용하여 User 엔터티를 생성하여 PostgreSQL 데이터베이스에서 사용자 데이터의 구조와 속성을 정의합니다.

    users 디렉토리 내에 새로운 `models/user.entity.ts` 파일을 만들고 아래 코드를 추가합니다.

     import { Entity, PrimaryGeneratedColumn, Column, } from "typeorm";

    @Entity()
    export class User {
        @PrimaryGeneratedColumn()
        id: number;

        @Column()
        name: string;

        @Column()
        email: string;
    }

    User 엔터티는 데이터베이스에 저장될 사용자 데이터의 구조를 정의합니다. 기본 키인 id와 이름, 이메일 컬럼을 포함합니다.

    CRUD API 서비스 생성

    이제 CRUD 작업에 필요한 로직을 처리할 API 서비스를 다음 명령어를 통해 생성합니다.

     nest g service users 

    `user-auth.service.ts` 파일을 열고 다음 코드를 추가합니다.

     import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import {User} from './models/user.entity';

    @Injectable()
    export class UsersService {
      constructor(
        @InjectRepository(User)
        private userRepository: Repository<User>,
      ) {}

      async findAll(): Promise<User[]> {
        return this.userRepository.find();
      }

      async findOne(id: number): Promise<User> {
        return this.userRepository.findOne({ where: { id } });
      }

      async create(user: Partial<User>): Promise<User> {
        const newuser = this.userRepository.create(user);
        return this.userRepository.save(newuser);
      }

      async update(id: number, user: Partial<User>): Promise<User> {
        await this.userRepository.update(id, user);
        return this.userRepository.findOne({ where: { id } });
      }

      async delete(id: number): Promise<void> {
        await this.userRepository.delete(id);
      }
    }

    `UsersService` 클래스는 CRUD 작업을 위한 API 메서드를 정의합니다. 모든 사용자 데이터 조회, 특정 ID를 가진 사용자 검색, 새 사용자 생성, 기존 사용자 정보 업데이트, 그리고 데이터베이스에서 사용자 정보 삭제 기능을 제공합니다.

    API 컨트롤러 정의

    사용자 관련 작업을 위한 API 엔드포인트를 관리할 컨트롤러를 생성합니다.

     nest g controller users 

    다음으로 `users.controller.ts` 파일에 다음 코드를 추가합니다.

     import { Controller, Get, Post, Body, Put, Param, Delete, NotFoundException, HttpCode } from '@nestjs/common';
    import { UsersService } from './users.service';
    import { User } from './models/user.entity';

    @Controller('api/users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}

      @Get()
      async findAll(): Promise<User[]> {
        return this.usersService.findAll();
      }

      @Post()
      @HttpCode(201)
      async create(@Body() user: User): Promise<User> {
        const createdUser = await this.usersService.create(user);
        return createdUser;
      }

      @Put(':id')
      async update (@Param('id') id: number, @Body() user: User): Promise<any> {
        await this.usersService.update(id, user);
        return { message: 'User updated successfully' };
      }

      @Delete(':id')
      async delete(@Param('id') id: number): Promise<any> {
        const user = await this.usersService.findOne(id);

        if (!user) {
          throw new NotFoundException('User does not exist!');
        }

        await this.usersService.delete(id);
        return { message: 'User deleted successfully' };
      }
    }

    컨트롤러는 사용자 작업을 위한 API 엔드포인트를 관리합니다. 모든 사용자 검색을 위한 GET 요청, 새 사용자 생성을 위한 POST 요청, 기존 사용자 업데이트를 위한 PUT 요청, 사용자 삭제를 위한 DELETE 요청을 처리합니다.

    `UsersService`를 활용하고 `User` 엔터티와 상호작용함으로써, 이 컨트롤러는 데이터베이스에 저장된 사용자 관련 작업을 관리하기 위한 완전한 API를 제공합니다.

    users.module.ts 파일 업데이트

    마지막으로, `users.module.ts` 파일을 아래와 같이 수정하여 `User` 엔터티와 데이터베이스 연결을 설정하는 TypeORM 모듈을 통합합니다.

     import { Module } from '@nestjs/common';
    import { UsersController } from './users.controller';
    import { UsersService } from './users.service';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { User } from './models/user.entity';

    @Module({
      imports: [TypeOrmModule.forFeature([User])],
      controllers: [UsersController],
      providers: [UsersService]
    })

    export class UsersModule {}

    마지막으로 개발 서버를 실행하여 Postman과 같은 도구를 사용하여 CRUD 작업을 테스트합니다.

     npm run start 

    서버는 3000번 포트에서 시작되며 API 요청은 http://localhost:3000/api/users 에서 보낼 수 있습니다.

    Nest.js를 이용한 백엔드 애플리케이션 개발

    간단한 REST API를 개발하든 복잡한 웹 애플리케이션을 개발하든, Nest.js는 신뢰성 있고 강력한 백엔드 시스템을 구축하는 데 필요한 포괄적인 기능들을 제공합니다.

    Nest.js는 Express.js와 비교했을 때 프로젝트 개발에 더욱 체계적인 접근 방식을 제공합니다. 조직화된 모듈식 디자인 패턴을 통해 복잡한 애플리케이션을 자신 있게 구축, 확장 및 유지 관리할 수 있습니다.