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 부분만 남겨서 강제접속을 시도하니 정상적으로 연결되었다!
유익한 자료 감사합니다.