이번에 node.js가 아니라 nest.js를 배우게 되었다.
그 이유는 취업을 하게 되었고 언어를 nest.js를 써서 학습하게 되었고 학습하면서 그중 가장 기본축에 속하는 폴더 구조를 이해하면서 nest.js의 과정을 이해해보고자한다.
우선 faq라는 정보를 주고 받는 데이터의 폴더를 구상한다면 구조는 아래와 같습니다.
src/
└── faq/
├── dto/
│ ├── create-faq.dto.ts
│ ├── update-faq.dto.ts
│ └── get-faq.dto.ts
│
├── entities/
│ └── faq.entity.ts
│
├── faq.controller.ts
├── faq.service.ts
└── faq.module.ts
위의 폴더 구조는 학습 과정에서 최대한 이해하기 쉽고 단조롭게 구성됩니다.
크게 보면 faq 파일안에 dto 파일, entities 파일, contoller , service, module 로 구성이 됩니다.
이 폴더가 클라이언트 요청에 따라 실행되는 과정을 시각화 해보자면
[Client 요청]
↓
[FaqController]
- POST /faq → this.faqService.create(dto)
- GET /faq → this.faqService.findAll()
↓
[FaqService]
- 로직 처리
- this.faqRepository.save(), find(), delete()
↓
[FaqRepository] (자동 생성된 TypeORM Repository)
- DB 직접 접근
- 테이블 구조는 FaqEntity 기반
↓
[FaqEntity]
- 테이블 스키마 정의 (@Entity)
[FaqModule]
- 위 모든 구성요소를 하나의 모듈로 등록 및 연결
위와 같이 진행이 됩니다 이제 하나하나 파일을 이해해봅시다.
-dto란?
Data Transfer Object의 약자 로 데이터를 전달& 검증 하는 역할을 합니다.
DTO는 "요청받은 데이터를 구조화하고 검증하는 역할"입니다.
즉, 유효성 검사의 틀 + 데이터 구조 정의서 역할을 동시에 합니다.
요약하자면 클라이언트가 서버에게 요청을 할 경우 가장 먼저 서버에서 이 정보가 틀에 맞는지 틀을 가져다 대고 그 틀이 dto라고 생각하면 이해하기 쉽습니다.
코드를 예를 들자면
export class CreateFaqDto {
@IsString()
@ApiProperty()
question: string
@IsString()
@ApiProperty()
answer: string
}
위와 같은 구조로 dto가 구성이 되어있고 클라이언트가 Faq를 만들기 위해 요청 할경우 CreateFaqDto가 실행되어 해당 요청 사항에 question, answer이라는 응답이 잘 작성 되었는지? 두 데이터 모두 string 즉 문자열로 작동이 되었는지 확인을 합니다.
이 Dto과정은 controller파일에서 거의 실행 됩니다.
- Controller
예를 들어 이용자가 faq에 정보를 새로 등록하고자 합니다. 정보를 서버에 보내면 해당 정보는 Controller에 가장 먼저 도착하게 됩니다.
이 정보가 CreateFaqDto라는 임의로 지정한 틀에 맞는지 확인을 하고 틀리다면 에러를 맞다면 해당 정보를 Service 파일로 보내게 됩니다. 위와 같이 Controller는 DB에 직접 접근하지 않으면서 client의 요청이 있다면 가장 먼저 데이터를 받아 Service 파일에 보내는 역할을 합니다. 코드를 예로 들자면 아래와 같습니다.
@Controller('faq')
export class FaqController {
constructor(private readonly faqService: FaqService) {}
@Post()
create(@Body() dto: CreateFaqDto) {
return this.faqService.create(dto)
}
}
- Service
Service는 Repository를 생성하여 DB에 직접적으로 접근하는 역할을 합니다.
Controller가 전달한 데이터를 가져와 db에 직접적으로 dto틀에 맞춰서 create, read, update, delete등의 역할을 합니다.
예시 코드로 표현하면 아래와 같습니다.
@Injectable()
export class FaqService {
constructor(
@InjectRepository(Faq)
private faqRepository: Repository<Faq>
) {}
async create(dto: CreateFaqDto): Promise<Faq> {
const faq = this.faqRepository.create(dto)
return this.faqRepository.save(faq)
}
}
- Repository
db에 정보를 가지고 있는 데이터 입니다. service에서 db에 접근할 때 사용됩니다. 예제는 위의 service 코드에 repsitory를 어떤식으로 활용하는지 표기 되어 있습니다.
- Entity
데이터베이스(DB)의 전체적인 틀을 가지고 있는 공간입니다. entity를 직접적으로 데이터를 불러내는 클래스로 사용하면 안됩니다.
해당 데이터를 맵핑하여 repository를 통해 데이터를 맵핑하여 활용할 수 있습니다.
entity의 코드를 예로 들자면
@Entity()
export class Faq {
@PrimaryGeneratedColumn()
id: number
@Column()
question: string
@Column()
answer: string
}
다음과 같이 예로 들수 있습니다.
-Module
위의 데이터들을 하나로 합쳐서 하나의 코드로 연결하는 역할을 하면서 Nest.js에게 어떤 컨트롤러, 서비스, 엔티티를 이 모듈에서 쓸 건지 알려주는 설정 파일이다.
예로든 Faq데이터들을 하나로 합쳐서 FaqModule로 표현한다, 예로 들면 아래와 같다.
@Module({
imports: [TypeOrmModule.forFeature([Faq])],
controllers: [FaqController],
providers: [FaqService]
})
export class FaqModule {}
다음과 같이 nest.js의 실행 과정을 풀어 보았는데 아직은 조금 복잡해 보인다..! 빨리 익숙해져야지
'코딩 > Node.js' 카테고리의 다른 글
Transform에 대한 이해, Plain object 와 class instance (1) | 2025.05.08 |
---|