AppModule
설정오늘은 NestJS
의 AppModule
설정에 대해 정리했다.
이 모듈은 프로젝트의 루트 모듈로, 다양한 기능 모듈들을 불러오고 전역 설정을 담당한다.
ConfigModule
)ConfigModule.forRoot()
을 사용하여 환경 변수 설정을 전역으로 적용했다. Joi
를 활용해 환경 변수의 유효성을 검증했다. ACCESS_SECRET_KEY
: JWT 인증을 위한 시크릿 키 ACCESS_EXPIRES_IN
: 액세스 토큰 만료 시간 (기본값: 1분) TypeOrmModule
)TypeOrmModule.forRootAsync()
를 활용하여 데이터베이스 설정을 비동기적으로 적용했다. ConfigService
를 이용해 .env
파일에서 값을 가져와 설정을 구성했다. SnakeNamingStrategy
: typeorm-naming-strategies
를 사용하여 DB 컬럼을 snake_case
로 변환 synchronize
: DB_SYNC
환경 변수 값을 기반으로 동기화 여부 결정 logging: true
설정으로 SQL 로그 확인 가능 다양한 도메인 기능을 AppModule
에서 가져와 사용했다.
UsersModule
, PostsModule
, CommentsModule
: 사용자, 게시글, 댓글 기능 AuthModule
: 인증 및 JWT 기반 보안 기능 AchievementsModule
, UsersAchievementsModule
: 업적 시스템 관련 모듈 GamesModule
: 마피아 게임의 핵심 기능 StatisticsModule
: 게임 및 유저 통계 관리 EventEmitterModule
) EventEmitterModule.forRoot()
가 주석 처리되어 있지만, 이벤트 기반 처리가 필요할 때 활성화 가능 JwtModule
) ACCESS_SECRET_KEY
와 ACCESS_EXPIRES_IN
을 활용하여 JWT 인증 설정을 적용할 수 있도록 준비됨 ServeStaticModule
) ServeStaticModule
을 이용해 정적 파일을 제공할 수 있도록 설정 가능 ✅ 환경 변수 검증을 통해 안전한 설정이 가능함을 이해했다.
✅ TypeORM
을 설정할 때 forRootAsync()
를 활용하면 ConfigService
와 함께 비동기적으로 설정을 적용할 수 있음을 확인했다.
✅ JwtModule
과 ServeStaticModule
같은 추가적인 기능을 활성화할 경우의 사용법을 익혔다.
다음으로는 웹소켓과 연동하여 GamesModule
에서 실시간 데이터 처리를 어떻게 구현할지 조사해볼 계획!
app.module.ts
import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AchievementsModule } from './achievements/achievements.module';
import { UsersAchievementsModule } from './user-achievements/users-achievements.module';
import { GamesModule } from './games/games.module';
import { StatisticsModule } from './statistics/statistics.module';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import Joi from 'joi';
import { UsersModule } from './users/users.module';
import { PostsModule } from './posts/posts.module';
import { CommentsModule } from './comments/comments.module';
import { AuthModule } from './auth/auth.module';
const typeOrmModuleOptions = {
useFactory: async (
configService: ConfigService,
): Promise<TypeOrmModuleOptions> => ({
namingStrategy: new SnakeNamingStrategy(),
type: 'mysql',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
entities: [__dirname + '/**/entities/*.{ts,js}'],
synchronize: configService.get('DB_SYNC'),
logging: true,
}),
inject: [ConfigService],
};
@Global()
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().required(),
DB_NAME: Joi.string().required(),
DB_SYNC: Joi.boolean().required(),
ACCESS_SECRET_KEY: Joi.string().required(), // 액세스 시크릿 키 검증 추가
ACCESS_EXPIRES_IN: Joi.string().default('1m'), // 액세스 만료시간 검증 추가
}),
}),
// EventEmitterModule.forRoot(), // 이벤트 시스템 활성화
TypeOrmModule.forRootAsync(typeOrmModuleOptions),
// JwtModule.registerAsync({
// imports: [ConfigModule],
// inject: [ConfigService],
// useFactory: async (configService: ConfigService) => ({
// secret: configService.get<string>('ACCESS_SECRET_KEY'),
// signOptions: {
// expiresIn: configService.get<string>('ACCESS_EXPIRES_IN'),
// },
// }),
// }),
// ServeStaticModule.forRoot({
// rootPath: join(__dirname, '..', 'dist', 'public'),
// serveRoot: '/', // 루트 URL에서 정적 파일 제공
// }),
UsersModule,
PostsModule,
CommentsModule,
AuthModule,
AchievementsModule,
GamesModule,
StatisticsModule,
UsersAchievementsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}