728x90

출처

Node.js 설치

[bluesanta@localhost ~]$ cd ~
[bluesanta@localhost ~]$ curl -sL https://rpm.nodesource.com/setup_18.x -o nodesource_setup.sh
[bluesanta@localhost ~]$ sudo bash nodesource_setup.sh
   
2025-04-21 16:22:50 - Repository is configured and updated.
2025-04-21 16:22:50 - You can use N|solid Runtime as a node.js alternative
2025-04-21 16:22:50 - To install N|solid Runtime, run: dnf install nsolid -y
2025-04-21 16:22:50 - Run 'dnf install nodejs -y' to complete the installation.
[bluesanta@localhost ~]$ sudo dnf install nodejs openssl -y
[bluesanta@localhost ~]$ node --version
v18.20.8
[bluesanta@localhost ~]$ sudo npm install -g configurable-http-proxy

jupyterhub 설치

[bluesanta@localhost ~]$ su - 
[root@localhost ~]# pip3.11 install sudospawner

jupyterhub 관리자 계정 생성

[bluesanta@localhost ~]$ sudo groupadd jupyterhub
[bluesanta@localhost ~]$ sudo useradd -g jupyterhub -s /bin/bash -m jupyterhubapp

jupyterhub 관리자 계정 로그인

[bluesanta@localhost ~]$ su - jupyterhubapp
암호:
[jupyterhubapp@localhost ~]$ 

jupyterhub 실행

[jupyterhubapp@localhost ~]$ jupyterhub
[I 2025-04-21 16:59:44.638 JupyterHub app:3354] Running JupyterHub version 5.3.0
[I 2025-04-21 16:59:44.638 JupyterHub app:3384] Using Authenticator: jupyterhub.auth.PAMAuthenticator-5.3.0
[I 2025-04-21 16:59:44.638 JupyterHub app:3384] Using Spawner: jupyterhub.spawner.LocalProcessSpawner-5.3.0
[I 2025-04-21 16:59:44.638 JupyterHub app:3384] Using Proxy: jupyterhub.proxy.ConfigurableHTTPProxy-5.3.0
   
[I 2025-04-21 16:59:44.987 JupyterHub proxy:477] Adding route for Hub: / => http://127.0.0.1:8081
16:59:44.988 [ConfigProxy] info: Adding route / -> http://127.0.0.1:8081
16:59:44.988 [ConfigProxy] info: Route added / -> http://127.0.0.1:8081
16:59:44.989 [ConfigProxy] info: 201 POST /api/routes/ 
[I 2025-04-21 16:59:44.989 JupyterHub app:3778] JupyterHub is now running at http://:8000

jupyterhub_config.py 생성

[jupyterhubapp@localhost ~]$ cd /etc/jupyterhub/
[jupyterhubapp@localhost jupyterhub]$ jupyterhub --generate-config
Writing default config to: jupyterhub_config.py
[jupyterhubapp@localhost jupyterhub]$ ls
jupyterhub_config.py

jupyterhub 사용자 계정 만들기

[bluesanta@localhost ~]$ sudo useradd -g jupyterhub -s /bin/bash -m user01
[bluesanta@localhost ~]$ sudo passwd user01
user01 사용자의 비밀 번호 변경 중
새 암호:
새 암호 재입력:
passwd: 모든 인증 토큰이 성공적으로 업데이트 되었습니다.

jupyterhub_config.py 파일 수정

[bluesanta@localhost ~]$ sudo vi /etc/jupyterhub/jupyterhub_config.py
c.JupyterHub.hub_connect_ip = '0.0.0.0'
c.JupyterHub.port = 8000

c.Authenticator.whitelist = set({'user01'})
c.Authenticator.allow_all = True

c.PAMAuthenticator.admin_users = set({'jupyterhubapp'})
# c.PAMAuthenticator.admin_groups = set({'jupyterhub'})

jupyterhub 서비스 등록

jupyterhub.service 파일 생성

[bluesanta@localhost ~]$ sudo vi /etc/systemd/system/jupyterhub.service
[Unit]
Description=JupyterHub
After=syslog.target network.target

[Service]
Type=simple
PIDFile=/run/jupyter.pid
User=root
WorkingDirectory=/etc/jupyterhub
ExecStart=/usr/local/bin/jupyterhub -f /etc/jupyterhub/jupyterhub_config.py

[Install]
WantedBy=multi-user.target

jupyterhub 서비스 등록

[bluesanta@localhost ~]$ sudo systemctl enable jupyterhub.service 
Created symlink /etc/systemd/system/multi-user.target.wants/jupyterhub.service → /etc/systemd/system/jupyterhub.service.

jupyterhub 서비스 갱신

[bluesanta@localhost ~]$ sudo systemctl daemon-reload

8000 포트 개방

[bluesanta@localhost ~]$ sudo firewall-cmd --permanent --zone=public --add-port=8000/tcp
success
[bluesanta@localhost ~]$ sudo firewall-cmd --reload
success
728x90
728x90

출처

application.properties

#File upload settings
# support multipart uploads (default: true)
spring.servlet.multipart.enabled=true
# Threshold at which files are written to memory (default: 0B)
#spring.servlet.multipart.file-size-threshold=0B 
# Temporary storage space for uploaded files
spring.servlet.multipart.location=d:/usr/local/cnssm/resources
# Maximum size of the file (default: 1MB)
spring.servlet.multipart.max-file-size=10144MB
# Maximum size of request (default: 10MB)
spring.servlet.multipart.max-request-size=10144MB

FileStorageProperties.java

package com.bx.config.property;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "spring.servlet.multipart")
public class FileStorageProperties {

	private String location;

	public String getLocation() {
		return location;
	}

	public void setLocation(String location) {
		this.location = location;
	}
}

FileUploadController.java

package com.bx.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.bx.common.util.ExcelUtil;
import com.bx.config.property.FileStorageProperties;
import com.bx.service.FileStorageService;

@Controller
public class FileUploadController {

	@Autowired
	private FileStorageService fileStorageService;

	@Autowired
	private FileStorageProperties fileStorageProperties;

	@RequestMapping(value = "/bx/uploadFile", method = RequestMethod.POST)
	public String uploadFile(@RequestParam("file") MultipartFile file, ModelMap modelMap) {
		String fileName = fileStorageService.storeFile(file);
		modelMap.put("fileName", fileName);
		
		try {
			if (fileName != null && (fileName.endsWith(".xls") || fileName.endsWith(".xlsx"))) {
				FileInputStream fis = new FileInputStream(new File(fileStorageProperties.getLocation() + "/" + fileName));
				Workbook workbook = new XSSFWorkbook(fis);
				
				Sheet sheet = workbook.getSheetAt(0); // 첫 번째 시트 가져오기
				
				// 행 반복 (첫 번째 행부터 마지막 행까지)
				for (Row row : sheet) {
					// 열 반복 (첫 번째 열부터 마지막 열까지)
					for (Cell cell : row) {
						// 셀 값 출력
						System.out.print(ExcelUtil.getCellValue(cell) + "\t");
					}
					System.out.println(); // 행 끝나면 줄바꿈
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return "bx/uploadOk";
	}

	@RequestMapping(value = "/uploadMultipleFiles", method = RequestMethod.POST)
	public String uploadMultipleFiles(@RequestParam("files") MultipartFile[] files, ModelMap modelMap) {
		Arrays.asList(files).stream().map(file -> uploadFile(file, modelMap)).collect(Collectors.toList());

		return "bx/uploadOk";
	}

}

FileStorageService.java

package com.bx.service;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import com.bx.common.exception.FileNotFoundException;
import com.bx.common.exception.FileStorageException;
import com.bx.config.property.FileStorageProperties;

@Service
public class FileStorageService {

	private final Path fileStorageLocation;

	@Autowired
	public FileStorageService(FileStorageProperties fileStorageProperties) {
		this.fileStorageLocation = Paths.get(fileStorageProperties.getLocation()).toAbsolutePath().normalize();

		try {
			Files.createDirectories(this.fileStorageLocation);
		} catch (Exception ex) {
			throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
		}
	}

	public String storeFile(MultipartFile file) {
		// Normalize file name
		String fileName = StringUtils.cleanPath(file.getOriginalFilename());

		try {
			// Check if the file's name contains invalid characters
			if (fileName.contains("..")) {
				throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
			}

			// Copy file to the target location (Replacing existing file with the same name)
			Path targetLocation = this.fileStorageLocation.resolve(fileName);
			Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

			return fileName;
		} catch (IOException ex) {
			throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
		}
	}

	public Resource loadFileAsResource(String fileName) {
		try {
			Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
			Resource resource = new UrlResource(filePath.toUri());
			if (resource.exists()) {
				return resource;
			} else {
				throw new FileNotFoundException("File not found " + fileName);
			}
		} catch (MalformedURLException ex) {
			throw new FileNotFoundException("File not found " + fileName, ex);
		}
	}
}

FileNotFoundException.java

package com.bx.common.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends RuntimeException {

	private static final long serialVersionUID = 1L;
	
	public FileNotFoundException(String message) {
		super(message);
	}
	
	public FileNotFoundException(String message, Throwable cause) {
		super(message, cause);
	}
}

FileStorageException.java

package com.bx.common.exception;

public class FileStorageException extends RuntimeException {
	
	private static final long serialVersionUID = 1L;

	public FileStorageException(String message) {
		super(message);
	}
	
	public FileStorageException(String message, Throwable cause) {
		super(message, cause);
	}
}

HTML

<form method="post" action="<c:url value="/cnssm/uploadFile"/>" enctype="multipart/form-data">

  <div>
    file : <input type="file" name="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
  </div>
  
  <input type="submit">

</form>
728x90
728x90

구출처

OpenCV 패키지 설치

(venv) D:\workspace.python\cubotino>pip install opencv_python opencv_contrib_python_headless opencv_contrib_python opencv_python_headless numpy==1.26.4

main.py

import cv2

def detect_faces(image_path):
    # 이미지 파일을 로드합니다.
    image = cv2.imread(image_path)

    # 이미지를 회색으로 변환합니다. (얼굴 인식은 흑백 이미지에서 수행되므로)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 얼굴 인식을 위해 얼굴 검출기를 로드합니다.
    print(f'cv data path = {cv2.data.haarcascades}')
    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

    # 이미지에서 얼굴을 검출합니다.
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

    # 검출된 얼굴 주위에 사각형을 그립니다.
    for (x, y, w, h) in faces:
        cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

    # 결과 이미지를 화면에 출력합니다.
    cv2.imshow('Face Detection', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 얼굴을 인식할 이미지를 지정합니다.
image_path = 'face.jpg'

# 얼굴 인식 함수를 호출합니다.
detect_faces(image_path)

실행

D:\workspace.python\cubotino\venv\Scripts\python.exe D:\workspace.python\cubotino\main.py 
cv data path = D:\workspace.python\cubotino\venv\Lib\site-packages\cv2\data\

728x90
728x90

[File] > [New Project...] 메뉴 선택

실행 환경 설정

main.py 소스

# This is a sample Python script.

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.


def print_hi(name):
    # Use a breakpoint in the code line below to debug your script.
    print(f'Hi, {name}')  # Press Ctrl+F8 to toggle the breakpoint.


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    print_hi('PyCharm')

# See PyCharm help at https://www.jetbrains.com/help/pycharm/

실행

상단에 Run 버튼 선택

실행 결과

D:\workspace.python\Example1\venv\Scripts\python.exe D:\workspace.python\Example1\main.py 
Hi, PyCharm
728x90
728x90

출처

Entity 객체

Entity 부모 클래스 ( node_modules\typeorm\decorator\entity\Entity.d.ts )

import { EntityOptions } from "../options/EntityOptions";
/**
 * This decorator is used to mark classes that will be an entity (table or document depend on database type).
 * Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
 */
export declare function Entity(options?: EntityOptions): ClassDecorator;
/**
 * This decorator is used to mark classes that will be an entity (table or document depend on database type).
 * Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
 */
export declare function Entity(name?: string, options?: EntityOptions): ClassDecorator;

User 클래스 ( entities/user.entity.ts )

import { BaseEntity, Column, Entity, PrimaryGeneratedColumn} from 'typeorm';

@Entity({ name: 'users' })
export class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  public id: number;

  @Column()
  public roleId: number;

  @Column()
  public avatarId: number;

  @Column()
  public nickname: string;

  @Column()
  public email: string;

  @Column()
  public phone: string;

  @Column()
  public phoneWithCountryCode: string;

  @Column()
  public bio: string;

}

Service 클래스 ( user.service.ts )

Controller에서 요청 받은 내용을 Service 객체를 통해서 데이터 저장 및 검색한 결과를 반환

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { User } from './entities/user.entity';

@Injectable()
export class UsersService {

  constructor(
    @InjectRepository(User)
    private readonly _usersRepository: Repository<User>,
  ) { }

  public async detailUser(id: number): Promise<any> {
    const queryBuilder = this._usersRepository.createQueryBuilder('users');
    queryBuilder.where('users.id = :userId', { userId: id });
    const user = await queryBuilder.getOne();
    return user;
  }
}

Controller 클래스 ( user.controller.ts )

Controller는 http 요청을 처리 하고 클라이언트에 응답을 반환하는 하는 클래스

import { Controller, Get, Param } from '@nestjs/common';
import { User } from './entities/user.entity';
import { UsersService } from './user.service';

@Controller()
export class UsersController {
  constructor(
    private readonly _userService: UsersService
  ) { }

  @Get('detail/:id')
  public async findOne(@Param('id') id : number) {
    return this._userService.detailUser(id);
  }

}

Module 클래스 ( user.module.ts )

Module 데코레이터가 붙어있는 클래스로 Nest가 전체 어플리케이션의 구조를 정의한 메타데이터 제공

  • providers : Service 클래스
  • controllers : Controller 클래스
  • imports : 
  • exports : 
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './user.controller';
import { User } from './entities/user.entity';
import { UsersService } from './user.service';

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

메인 Module 클래스 ( app.module.ts )

imports에 UsersModule 추가

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

import { configValidationSchema } from 'config/validation/config-validation';
import databaseConfig from '../config/database.config';
import { UsersModule } from './v1/users/user.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [ '.env.' + process.env.NODE_ENV ],
      ignoreEnvFile: process.env.NODE_ENV === 'prod',
      validationSchema: configValidationSchema,
      load: [ databaseConfig ],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ ConfigModule ],
      useFactory: (configService: ConfigService) =>
          configService.get('database'),
      inject: [ ConfigService ],
    }),
    UsersModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

실행

728x90
728x90

WebStorm - npm 실행

WebStorm 이용해서 npm 실행하는 설정 방법을 정리 해보았습니다.

포트 3001 변경

3000포트를 Docker가 사용하고 있어서 3001번으로 포트를 변경했습니다.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3001);
}
bootstrap();

[Edit Configurations...] 메뉴 선택

Run/Debug Configurations 창의 왼쪽 상단 + 버튼 선택

[npm] 메뉴 선택

아래의 화면과 동일하게 설정

  • Command : run
  • Scripts : start:dev
  • Package manager : npm

Before launch 항목에서 + 버튼 선택 -> [Launch Web Browser] 메뉴 선택

Browser : Chrome 설정, URL : http://localhost:3001/ 설정

설정값 저장

실행

브라우져로 확인

 

 

 

 

728x90
728x90

출처

TypeORM 설치

D:\workspace.nodejs\nestjs_example>npm i --save @nestjs/typeorm typeorm mysql2

src/app.module.ts 내용 - 환경 파일 사용하지 않고 연결하기

내용 추가

import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [

    TypeOrmModule.forRoot({
      /*
      type: 'mysql',
      host: 'localhost',
      port: 8200,
      username: 'bluex',
      password: 'bluex',
      database: 'iloveart',
      */
      type: 'mysql', // process.env.DB_CONNECTION,
      host: process.env.DB_HOST,
      port: +process.env.DB_PORT,
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_DATABASE,
      
      entities: [],
      synchronize: true,
    }),
  ],

전제 내용

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';

import { configValidationSchema } from 'config/validation/config-validation';
import databaseConfig from '../config/database.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [ '.env.' + process.env.NODE_ENV ],
      ignoreEnvFile: process.env.NODE_ENV === 'prod',
      //validationSchema: configValidationSchema,
      //load: [ databaseConfig ],
    }),
    TypeOrmModule.forRoot({
      /*
      type: 'mysql',
      host: 'localhost',
      port: 8200,
      username: 'bluex',
      password: 'bluex',
      database: 'iloveart',
      */
      type: 'mysql', // process.env.DB_CONNECTION,
      host: process.env.DB_HOST,
      port: +process.env.DB_PORT,
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_DATABASE,
      
      entities: [],
      synchronize: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

src/app.module.ts 내용 - 환경 파일 사용

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

import { configValidationSchema } from 'config/validation/config-validation';
import databaseConfig from '../config/database.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [ '.env.' + process.env.NODE_ENV ],
      ignoreEnvFile: process.env.NODE_ENV === 'prod',
      validationSchema: configValidationSchema,
      load: [ databaseConfig ],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ ConfigModule ],
      useFactory: (configService: ConfigService) =>
          configService.get('database'),
      inject: [ ConfigService ],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

config/validation/config-validation.ts 내용

import * as Joi from 'joi';

export const configValidationSchema = Joi.object({
  // DATABASE
  DB_CONNECTION: Joi.string().min(1).required(),
  DB_HOST: Joi.string().min(1).required(),
  DB_PORT: Joi.string().min(1).required(),
  DB_DATABASE: Joi.string().min(1).required(),
  DB_USERNAME: Joi.string().min(1).required(),
  DB_PASSWORD: Joi.string().optional(),
});

config/database.config.ts 내용

import { registerAs } from '@nestjs/config';

import { User } from '../src/v1/users/entities/user.entity';

export default registerAs('database', () => {
  return {
    'name': 'default',
    'type': process.env.DB_CONNECTION,
    'host': process.env.DB_HOST,
    'port': process.env.DB_PORT,
    'username': process.env.DB_USERNAME,
    'password': process.env.DB_PASSWORD,
    'database': process.env.DB_DATABASE,
    'entities': [
      // 'dist/**/*.entity{.ts,.js}'
      User
    ],
    'synchronize': false,
    'migrationsTableName': 'migrations_histories',
    'migrationsRun': true,
    'logging': false
    // 'ssl': false,
    // 'extra': {
    //   'ssl': {
    //     'rejectUnauthorized': false
    //   }
    // }
  }
});

src\v1\users\entities\user.entity.ts 내용

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

@Entity({ name: 'users' })
export class User {
  @PrimaryGeneratedColumn()
  public id: number;

  @Column()
  public roleId: number;

  @Column()
  public avatarId: number;

  @Column()
  public nickname: string;

  @Column()
  public email: string;

  @Column()
  public phone: string;

  @Column()
  public phoneWithCountryCode: string;

  @Column()
  public bio: string;

}
728x90
728x90

출처

패키지 설치

@nestjs/config 설치

D:\workspace.nodejs\nestjs_example>npm i --save @nestjs/config

cross-env 설치 - 실행 옵션(dev, prod)에 따라서 참조하는 환경 변수 파일을 선택하기 위한 패키지

D:\workspace.nodejs\nestjs_example>npm i cross-env

joi 설치 - 환경변수 유효성 검사를 위한 패키지

D:\workspace.nodejs\nestjs_example>npm i joi

환경 파일 생성

  • .env.dev
  • .env.debug
  • .env.prod

package.json 수정

cross-env 패키지를 이용해 환경변수를 할당한뒤 nest start 명령을 실행

    // 수정전
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/src/main",
    
    // 수정후  
    "start": "nest start",
    "start:dev": "cross-env NODE_ENV=dev nest start --watch",
    "start:debug": "cross-env NODE_ENV=debug nest start --debug --watch",
    "start:prod": "cross-env NODE_ENV=prod node dist/main",

.env.xxx 내용

# DATABASE
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dev-db
DB_USERNAME=dev-user
DB_PASSWORD=dev-passwd

src/app.module.ts 내용

내용 추가

import { ConfigModule } from '@nestjs/config';

import { configValidationSchema } from 'config/validation/config-validation';
import databaseConfig from '../config/database.config';

imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [ '.env.' + process.env.NODE_ENV ],
      validationSchema: configValidationSchema,
      load: [ databaseConfig ],
    }),
  ],

전체 내용

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

import { configValidationSchema } from 'config/validation/config-validation';
import databaseConfig from '../config/database.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [ '.env.' + process.env.NODE_ENV ],
      validationSchema: configValidationSchema,
      load: [ databaseConfig ],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

config/validation/config-validation.ts 내용

import * as Joi from 'joi';

export const configValidationSchema = Joi.object({
  // DATABASE
  DB_CONNECTION: Joi.string().min(1).required(),
  DB_HOST: Joi.string().min(1).required(),
  DB_PORT: Joi.string().min(1).required(),
  DB_DATABASE: Joi.string().min(1).required(),
  DB_USERNAME: Joi.string().min(1).required(),
  DB_PASSWORD: Joi.string().optional(),
});

config/database.config.ts 내용

import { registerAs } from '@nestjs/config';

export default registerAs('database', () => {
  return {
    'name': 'default',
    'type': process.env.DB_CONNECTION,
    'host': process.env.DB_HOST,
    'port': process.env.DB_PORT,
    'username': process.env.DB_USERNAME,
    'password': process.env.DB_PASSWORD,
    'database': process.env.DB_DATABASE,
    'entities': [
      'dist/**/*.entity{.ts,.js}'
    ],
    'synchronize': false,
    'migrationsTableName': 'migrations_histories',
    'migrationsRun': true,
    'logging': false
    // 'ssl': false,
    // 'extra': {
    //   'ssl': {
    //     'rejectUnauthorized': false
    //   }
    // }
  }
});
728x90

+ Recent posts