2023.08.16

이얏호·2023년 8월 16일
0

nest.js에서 typeORM을 통한 postgreSQL(AWS RDS) 연동하기.

기본적인 골격은 mySQL을 연동할 때와 똑같다.

import { Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';
import { Product } from 'src/products/products.entity';

@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private readonly configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'postgres',
      host: this.configService.get<string>('DATABASE_HOST'),
      port: this.configService.get<number>('DATABASE_PORT'),
      username: this.configService.get<string>('DATABASE_USERNAME'),
      password: this.configService.get<string>('DATABASE_PASSWORD'),
      database: this.configService.get<string>('DATABASE_NAME'),
      entities: [Product],
      logging: this.configService.get<boolean>('DATABASE_LOGGING'),
      synchronize: this.configService.get<boolean>('DATABASE_SYNCHRONIZE'),
    };
  }
}

.env를 통해서 각 변수들을 사용하기 위해 다음과 같이 typeorm.config.service.ts를 생성해준다.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductsModule } from './products/products.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeOrmConfigService } from 'config/typeorm.config.service';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useClass: TypeOrmConfigService,
      inject: [ConfigService],
    }),
    ProductsModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

이후 app.module.ts에 TypeOrmModule.forRootAsync를 설정해준다.

app.module.ts가 실행 될 때, TypeOrmConfigService를 통하여 필요한 값들을 환경변수에서 읽어온다.

이렇게 작성을 하고 실행을 한다면 아래와 같은 오류가 발생한다.

no pg_hba.conf entry for host "??", user "??", database "??", no encryption

AWS의 RDS postgreSQL은 SSL/TLS를 통하여 클라이언트와 연결되는데, pg_hba.conf에서 인증 설정이 되어있지 않았기 때문에 발생하는 오류이다.

해결 방법은 두 가지이다.
첫 번째는, RDS가 아니라 로컬 서버에서 실행했을 경우, 혹은 파일 접근이 가능할 경우에는
pg_hba.conf 파일에

host all all 0.0.0.0/0

과 같이 모든 ip에서 접근이 가능하도록 추가해주는 방법이다.

다만 RDS의 경우 해당 파일에 접근하여 수정하는 것이 불가능하다.
(방법이 있을 수도 있지만 찾지 못하였다.)

그럴 경우엔 공식문서에서 설명중인
https://docs.aws.amazon.com/ko_kr/documentdb/latest/developerguide/connect_programmatically.html
global-bundle.pem을 다운로드 받아서 해당 부분을 풀어줄 수 있다.
global-bundle.pem을 다운로드 받은 뒤,
typeorm.config.service.ts 파일에

import * as fs from 'fs';

ssl: {
        ca: fs.readFileSync('global-bundle.pem'),
      },
      extra: {
        ssl: { rejectUnauthorized: false },
      },

해당 코드를 추가해준다.

아래는 전문이다.

import { Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';
import { Product } from 'src/products/products.entity';
import * as fs from 'fs';

@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private readonly configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'postgres',
      host: this.configService.get<string>('DATABASE_HOST'),
      port: this.configService.get<number>('DATABASE_PORT'),
      username: this.configService.get<string>('DATABASE_USERNAME'),
      password: this.configService.get<string>('DATABASE_PASSWORD'),
      database: this.configService.get<string>('DATABASE_NAME'),
      entities: [Product],
      logging: this.configService.get<boolean>('DATABASE_LOGGING'),
      synchronize: this.configService.get<boolean>('DATABASE_SYNCHRONIZE'),
      ssl: {
        ca: fs.readFileSync('global-bundle.pem이 위치한 경로'),
      },
      extra: {
        ssl: { rejectUnauthorized: false },
      },
    };
  }
}

다음과 같이 설정을 하면 해결!


추가 사항)
해당 코드의

ssl: {
        ca: fs.readFileSync('global-bundle.pem이 위치한 경로'),
      },

부분은
공식 문서에 따르면 TLS가 활성화 된 상태에서 Microsoft Windows에서 실행되어 PKCS7 파일이 필요한 경우에 사용되는 방법이라고 한다.
global-bundle.pem을 통해서 접근하는 방법이고,

extra: {
        ssl: { rejectUnauthorized: false },
      },

해당 부분은 위의 global-bundle.pem의 존재 여부와 상관 없이, 강제로 접속하는 방법이다.

즉 windows 환경에서는 둘 중 하나의 코드만 적어도 접근이 가능해지고,
mac환경의 경우, 맥 북이 없어서... 정확한 확인은 못해봤지만, 팀원의 경우 위쪽 global-bundle.pem을 참조하지 못하는 사람도 나왔다.(mac을 쓰는 팀원이 두 명인데 둘 중 한명에게만 발생했다.) 그래서 ssl부분을 지우고 extra 부분만 남겨서 강제접속을 시도하니 정상적으로 연결되었다!

profile
열심히 신나게 화이팅!

4개의 댓글

comment-user-thumbnail
2023년 8월 16일

유익한 자료 감사합니다.

1개의 답글
comment-user-thumbnail
2023년 8월 18일

좋은자료감사합니다잉

답글 달기
comment-user-thumbnail
2023년 8월 18일

덕분에 연결 성공했어요!

답글 달기