A progressive Node.js framework for building efficient and scalable server-side applications.
Nest framework TypeScript starter repository.
$ npm install
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
- eslintrc.js
- 개발자들이 특정한 규칙을 가지고 코드를 짤 수 있도록 도와주는 라이브러리
- 타입스크립트 쓰는 가이드라인을 제시
- 문법에 오류나면 알려주는 역할 등등..
- prettierrc
- 주로 코드 형식을 맞추는데 사용 (작은 따옴표를 쓸지, 큰 따옴표를 쓸지 등등)
- nest-cli.json
- nest 관련 설정
- soureRoot 를 src로 설정해주는 등
- package.json
- 패키지의 이름, 버전, 빌드방식, 라이브러리 버전 등 패키지 정보
- src 폴더
- 로직들이 들어가있음
- main.ts가 어플리케이션 생성을 하고, 루트모듈인 AppModule을 생성한다. 3000번 포트에서 앱을 실행한다.
nest g module boards
- nest: using nestcli
- g: generate
- module: chematic that i want to create
- boards: name of the schematic
- 데코레이터를 통해 컨트롤러를 정의할 수 있다.
- Handler(@Get, @Post, @Put)를 정의할 수 있다.
- nest cli 사용해서 컨트롤러 생성 가능하다.
nest g controller boards --no-spec
- --no-spec: 테스트 코드 없이
- 실행순서
- cli는 먼저 boards 폴더를 찾는다
- controller 파일 생성
- boards 폴더 안에 module 폴더를 찾는다
- module 폴더 안에 controller 추가(등록)
- requesBody 선언 - Body 데코레이터 이용
@Post() create( @Body('title') title, @Body('description') description){ return this.boardsService.create(title, description); } )
- 경로변수는 다음과 같이 받을 수 있다. (localhost:5000?id=~~)
@Get('/:id') find(@Param('id') id: string): Board{ return this.boardsService.findById(id); }
- 경로변수가 여러개라면 다음과 같이 작성 가능하다.
find(@Param() params: stiring[])
nest g service boards --no-spec
- boards.service.ts 생성
@Injectable()
export class AppService{
getHello(): string {
return 'Hello World!';
}
}
- @Injectable() 데코레이터가 있어서, 다른 컴포넌트에서 이 Service를 사용할 수 있게 된다
- boards 모듈, 컨트롤러에 추가된다
- boards 컨트롤러에서 사용 가능하도록 컨트롤러 constructor를 통해 주입해준다.
@Controller('boards') export class BoardsController { boardsService: BoardsService; constructor(boardsService: BoardsService) { this.boardsServie = boardsService; } } // 간단하게 다음과 같이 작성가능(타입스크립트 기능, private을 써주면 인수인 파라미터가 암묵적으로 프로퍼티로 선언됨, private인 이유는 이 클래스 안에서만 사용하기 위해서) constructor(private boardsService: BoardsService){}
: Nest의 기본 개념으로, 기본 Nest 클래스는 서비스, 리포지토리, 팩터리, 헬퍼 등 프로바이더로 취급될 수 있습니다. 주요 아이디어는 종속성으로 주입할 수 있다는 것으로, 객체는 서로 다양한 관계를 가지고, 인스턴스들간의 연결하는 기능은 Nest 런타임 시스템에 위임됩니다.
- 등록 방식
: module 파일에서 등록을 통해 해당 모듈에서 사용하고자 하는 Provider를 입력해줄 수 있다.
@Module({ controller: [BoardsController], providers: [BoardsService] })
: 인터페이스는 타입만을 체크하고, 클래스는 타입뿐 아니라 인스턴스 생성이 가능
export interface Board {
id: string;
title: string;
description: string;
status BoardStatus;
}
export enum BoardStatus {
PUBLIC = 'PUBLIC',
PRIVATE = 'PRIVATE'
}
- 클래스 형태로 생성한다.
export class CreateBoardDto{
title: string;
description: string;
}
- Pipe는 @Injectable() 데코레이터로 주석이 달린 클래스
- Data Transformation, Data Validation에 사용
- 여기서 실패하면 에러발생
- Data Transformation
- 입력 데이터를 원하는 형식으로 변환, 변환 실패 시 에러발생 ('7' -> 7)
- Data Validation
- 유효성 검사 (10자 이내인데 이상값 들어오면 에러)
- Nest는 메서드가 호출되기 전에 파이프를 삽입하고, 파이프는 메서드를 향하는 인수를 수신하고 작동
- 사용방법
- Handler-level Pipes
// 이 핸들러에만 파이프가 작동 @Post() @UsePipes(pipe) createBoard(){}
- Parameter-level Pipes
@Post() create( @Body('title', ParameterPipe) title, // 파이프 적용 @Body('description') description) // 파이프 미적용 {}
- Global-level Pipes
- 어플리케이션 레벨의 파이프
- 클라이언트에서 들어오는 모든 요청에 적용
async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(GlobalPipes); await app.listen(3000); } bootstrap();
- Nest가 제공하는 6가지 파이프 존재
- ValidationPipe
- ParseIntPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- DefaultValuePipe
@Get(':id') findOne(@Param('id', ParseIntPipe) id: number) // 문자열로 받으면 에러발생, number로 변환, 유효성 검사 {return;}
- Custom Pipe
export class ---Pipe implements PripeTrasform { transform(value: any, metaData: ArgumentMetadata) {} }
- Handler-level Pipes
- postgresSQL, pgAdmin(데이터베이스를 보는 툴)을 설치한다.
- node.js에 실행되고 TypeScript로 작성된 객체 관계형 매퍼 라이프러리입니다.
- ORM이란 데이터베이스의 데이터를 객체로 사용할 수 있도록 지원하는 기술, 객체지향 프로그래밍 언어에서 데이터베이스와의 상호작용을 간단하게 하기위함
- 모델을 기반으로 테이블을 자동생성 가능하다
- 테이블간의 매핑, 간단한 CLI명령 제공
const boards = Board.find({ title: 'Hello', status: "PUBLIC"});
- 설치
2. @nestjs/typeorm
3. typeorm
4. pg
참고: https://docs.nestjs.com/techniques/database - 설정
그리고 app.module.ts에 import 추가
export const typeormConfig: TypeOrmModuleOptions = { type: 'postgres', host: 'localhost', port: 5432, username: 'postgres', password: 'postgres', database: 'board-app', entities: [__dirname + '/../**/*.entity.{js,ts}'], // 엔티티를 기반으로 테이블을 생성하기 떄문에, 엔티티가 있는곳을 설정해줘야 한다. synchronize: true, };
@Module({ imports: [BoardsModule, TypeOrmModule.forRoot(typeormConfig)] })
- Entity
export class Board extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() description: string; @Column() status: BoardStatus; }
- Repository
Service에 주입
@EntityRepository(Board) export class BoardRepository extends Repository<Board>{}
constructor( @InjectRepository(BoardsRepository) private boardsRepository: BoardsRepository) {}
- remove vs delete
- remove는 값이 있는지를 확인한 후, 없다면 에러가 난다. 쿼리 두번호출
- delete는 값이 없다면 무시, 있다면 삭제
- Transaction
- TypeOrm에서는 QueryRunner, TransactionManager 두 방식이 있다.
- QueryRunner: 커밋과 롤백을 명시할 수 있어서 섬세한 트랜잭션이 가능하다.
const connection = getConnection(); const queryRunner = connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const newBoard = this.boardsRepository.create({ title: 'New Board', description: 'Board description', }); await queryRunner.manager.save(newBoard); // 추가 작업들 // 다른 엔티티를 저장하거나 수정할 수 있습니다. await queryRunner.commitTransaction(); } catch (err) { await queryRunner.rollbackTransaction(); } finally { await queryRunner.release(); }
- TransactionManager: 간단하게 적용가능
@Transaction() async createBoard( @TransactionManager() manager: EntityManager, ): Promise<void> { const newBoard = manager.create(Board, { title: 'New Board', description: 'Board description', }); await manager.save(newBoard); }
const board = {
title: title
}
// 다음 코드로 치환가능
const board = {
title
}
const title = createBoardDto.title;
const description = createBoardDto.description;
// 다음 코드로 치환가능
const {title, description} = createBoardDto;
- 접두사(prefix) readOnly는 읽기전용이다. 외부에서 접근 가능하지만 값은 변경할 수 없다.
- 클래스는 인터페이스와 다르게 런타임에서 작동한다.