Blog

[AI딥페이크] 4. 백엔드 프로그래밍 언어 및 개발 환경 구축 (NestJS)

Category
Author
PinOnMain
1 more property
NestJS의 DI, 모듈 등 핵심 이론을 배우고, Nest CLI를 사용해 백엔드 프로젝트와 CRUD API 뼈대를 구축합니다. 이어서, TypeORM으로 MySQL DB를 연동하고 ERD를 Entity로 구현하며, DTO와 ValidationPipe를 적용해 안정적인 API 서버를 완성합니다.
Table of Content

4일차: 백엔드 프로그래밍 (NestJS)

부제: Node.js, TypeScript, NestJS 핵심 개념과 RESTful API 서버 구축

Agenda: 4일차 학습 목표 및 일정

1.
NestJS 이론 (1): 핵심 개념, 아키텍처 (DI, Module)
2.
NestJS 실습 (1): Nest CLI 환경 구축 및 프로젝트 생성 (Resource)
3.
NestJS 이론 (2): 데이터베이스와 ORM (TypeORM)
4.
NestJS 실습 (2): DB 연동, Entity/Repository, 서비스 로직 구현
5.
NestJS 이론 (3): REST API 리팩토링 (DTO, Pipe)
6.
요약 및 5일차 예고 (FastAPI AI 연동 준비)
"안녕하십니까. 4일차, 백엔드 프로그래밍 강의에 오신 것을 환영합니다."
"어제(3일차)는 우리가 '프론트엔드', 즉 'React'를 다뤘습니다. 2일차에 설계했던 UI/UX 목업을 Vite 환경에서 실제 동작하는 컴포넌트(App.tsx, ImageUpload.tsx)로 만들었죠. useState, useEffect, React Router 같은 핵심 Hook과 라이브러리도 실습했습니다."
"오늘은 4일차, **'백엔드'**의 날입니다. 3일차에 만든 React 앱은 아직 '껍데기'에 불과합니다. 실제 '탐지' 버튼을 눌러도 아무 일도 일어나지 않죠. 왜냐하면 3일차에 만든 '클라이언트'가 요청을 보낼 '서버'가 아직 없기 때문입니다."
"오늘 8시간 동안, 우리는 3일차의 React와 통신하고, 2일차에 설계한 데이터를 처리할 **'메인 API 서버'**를 **'NestJS'**로 구축합니다."
"오늘 우리가 8시간 동안 다룰 내용을 캔버스의 Agenda로 정리했습니다. 4일차는 NestJS에 완전히 집중합니다."
1.
"먼저 **'NestJS 이론 (1)'**입니다. Node.js 개발의 '자유도'가 왜 문제(Problem)가 되는지, 그리고 '체계적인 아키텍처'를 강제하는 NestJS가 그 해답(Solution)이 되는 이유를 알아봅니다. 3일차에 배운 TypeScript가 여기서 어떻게 쓰이는지, 그리고 NestJS의 핵심 사상인 **'DI(의존성 주입)'**와 '모듈(Module)' 개념을 짚어봅니다."
2.
"다음은 **'NestJS 실습 (1)'**입니다. 3일차에 Vite로 React 프로젝트를 만들었듯이, 4일차에는 **'Nest CLI'**라는 강력한 도구로 백엔드 프로젝트 환경을 구축합니다. 특히 nest g resource라는 명령어로, 우리가 2일차에 설계한 API의 **'CRUD 뼈대'**가 10초 만에 자동으로 생성되는 것을 경험할 것입니다."
3.
"이어서 **'NestJS 이론 (2)'**입니다. 서버의 핵심은 '데이터 저장'이죠. 2일차에 우리가 그렸던 'ERD'를 어떻게 코드로 옮기는지, 즉 '데이터베이스와 ORM', 그중에서도 **'TypeORM'**의 개념을 배웁니다."
4.
"네 번째는 **'NestJS 실습 (2)'**입니다. 이론으로 배운 TypeORM을 실제 deepfake-backend 프로젝트에 적용합니다. AppModuleMySQL DB를 연동하고, 2일차 ERD를 Entity 클래스로 구현합니다. 그리고 Service 로직을, 단순 배열이 아닌 실제 DB에 데이터를 저장(Save)하고 조회(Find)하도록 수정합니다."
5.
"다섯 번째, **'NestJS 이론 (3) 및 실습'**입니다. API는 '기능'뿐만 아니라 '안정성'도 중요합니다. 클라이언트(React)가 엉뚱한 데이터를 보내지 못하도록, **'DTO(데이터 전송 객체)'**와 **'ValidationPipe(유효성 검사)'**를 적용하여 우리가 만든 API를 리팩토링합니다."
6.
"마지막으로 **'요약 및 5일차 예고'**입니다. 4일차에 만든 NestJS 서버는 '메인 서버'입니다. 하지만 실제 AI 모델(Python)을 돌리기엔 적합하지 않죠. 5일차에는 이 NestJS 서버가 호출할, AI 모델 전용 '마이크로서비스 서버(FastAPI)'를 구축할 준비를 합니다."
"오늘 4일차의 성과는 명확합니다. 2일차에 설계하고 3일차에 UI를 만든 우리 앱의 **'실제 두뇌(메인 서버)'**를 NestJS로 완성하고, **'실제 데이터베이스(MySQL)'**에 데이터를 읽고 쓰는 것을 Postman으로 확인하는 것입니다.
그럼, 첫 번째 모듈, 'NestJS 핵심 개념' 이론부터 시작하겠습니다."

모듈 1: NestJS 핵심 개념 (이론)

1. Why NestJS?

Problem: Node.js의 자유도. (Express, Koa)
개발자마다 다른 아키텍처, 코드 구조.
유지보수 및 협업의 어려움 발생.
Solution: NestJS (Opinionated Framework)
'체계적인 아키텍처'를 강제하는 프레임워크.
TypeScript를 기본 언어로 사용하여 '타입 안정성' 확보 (3일차 TS 경험).
모듈성(Modularity): 기능을 부품(Module)처럼 관리.
DI (의존성 주입): 강력한 객체지향(OOP) 설계 지원.
"자, 그럼 4일차 첫 번째 이론 모듈, **'NestJS 핵심 개념'**을 시작하겠습니다.
우리가 3일차에 React를 배웠고, 오늘부터는 백엔드로 NestJS를 배웁니다. 3일차 'React와 NestJS 모두 JavaScript(TypeScript) 기반이다'라고 말씀드렸죠.
그럼 이런 질문이 생깁니다. '둘 다 Node.js 런타임 위에서 돌아간다면, 왜 굳이 Express나 Koa 같은 더 가볍고 자유로운 프레임워크를 쓰지 않고, NestJS라는 무거운 프레임워크를 배워야 하는가?'"
"그 답이 바로 캔버스의 첫 번째 항목, **'Why NestJS?'**에 있습니다."
"**'Problem'**을 먼저 보시죠. **'Node.js의 자유도'**입니다.
ExpressKoa 같은 프레임워크는 '최소한의 기능(Middleware)'만 제공합니다. 이건 장점이기도 하지만, 프로젝트가 커지면 심각한 단점이 됩니다.
바로 **'개발자마다 다른 아키텍처, 다른 코드 구조'**를 갖게 되는 것이죠. A 개발자는 'Service'라는 폴더를 만들고, B 개발자는 'Logic'이라는 폴더를 만듭니다. A 개발자는 라우터에서 모든 비즈니스 로직을 처리하고, B 개발자는 로직을 분리합니다.
"이렇게 되면 어떻게 될까요? **'유지보수 및 협업의 어려움'**이 기하급수적으로 증가합니다. 새로운 개발자가 프로젝트에 투입되면, 그 프로젝트만의 '암묵적인 룰'을 파악하는 데만 몇 주를 낭비하게 됩니다."
"그래서 **'Solution'**으로 **'NestJS'**가 등장합니다.
NestJS는 'Opinionated Framework', 한국어로 번역하면 **'주장(의견)이 강한 프레임워크'**입니다.
이게 무슨 뜻이냐면, '개발자님, 자유롭게 하지 마시고, 우리가 수많은 대규모 프로젝트를 통해 검증한 '체계적인 아키텍처'를 강제로 따르세요.'라고 말하는 것입니다.
'라우팅은 반드시 Controller에 작성하세요.' '비즈니스 로직은 반드시 Service 클래스에 작성하세요.' '기능은 반드시 Module 단위로 캡슐화하세요.' ...라고 NestJS가 '강제'합니다."
"이 '강제성' 덕분에, A 개발자가 만든 NestJS 프로젝트와 B 개발자가 만든 NestJS 프로젝트의 구조가 90% 동일해집니다. 유지보수와 협업이 매우 쉬워지죠."
"NestJS가 제공하는 강력한 장점은 다음과 같습니다."
1.
"TypeScript 기본 사용: 3일차에 우리가 React를 .tsx로 작성했던 이유와 같습니다. '타입 안정성'을 확보하여 런타임 오류를 컴파일 시점에 잡습니다. 4일차의 NestJS는 이게 '선택'이 아닌 '필수'입니다."
2.
"모듈성(Modularity): '기능을 부품(Module)처럼 관리'합니다. 'User 관련 기능', 'Items 관련 기능'을 레고 블록처럼 캡슐화해서, 나중에 필요 없으면 그 모듈만 떼어내면 됩니다."
3.
"DI (의존성 주입): 이것이 NestJS의 핵심 철학입니다. '강력한 객체지향(OOP) 설계'를 지원합니다. 이 DI 개념은 잠시 후 '모듈 2'에서 아주 자세히 다루겠습니다."
"정리하자면, React가 '프론트엔드' 개발의 복잡성을 '컴포넌트'로 해결했다면, NestJS는 '백엔드' 개발의 복잡성을 '체계적인 아키텍처(모듈, DI)'로 해결하는 프레임워크입니다."
"그럼 이 NestJS를 개발할 때 사용하는 '핵심 도구'인 'NestJS CLI'에 대해 다음 장에서 알아보겠습니다."

2. NestJS CLI (Command Line Interface)

NestJS 개발의 '핵심 도구'.
npm install -g @nestjs/cli로 설치.
프로젝트 생성(nest new ...), 모듈/컨트롤러/서비스 자동 생성(nest g ...) 등.
'보일러플레이트(Boilerplate)' 코드를 자동으로 생성하여 개발 속도 향상.
"자, 앞서서 '왜' NestJS를 써야 하는지, 그 '철학'에 대해 알아봤습니다. NestJS가 '체계적인 아키텍처'를 강제한다고 했죠."
"그럼 이제 '어떻게(How)' 그 아키텍처를 쉽게 구축할 수 있는지 알아봐야 합니다."
"만약 NestJS가 'Controller는 여기, Service는 저기, Module은 여기에 만드세요'라고 말만 하고 도구를 주지 않는다면, 개발자들은 매번 손으로 items.controller.ts, items.service.ts... 이 모든 파일을 직접 만들어야 할 겁니다. 매우 번거롭겠죠."
"그래서 NestJS는 강력한 **'자동화 도구'**를 제공합니다. 이것이 바로 모듈 1의 두 번째 항목, **'NestJS CLI (Command Line Interface)'**입니다."
"NestJS CLI는 말 그대로 **'NestJS 개발의 핵심 도구'**입니다. 3일차에 Vite가 React 프로젝트 생성을 도와줬다면, NestJS에서는 CLI가 그 역할을 합니다."
"이 도구는 **'모듈 3: 실습 1'**에서 바로 사용할 것이기 때문에, 지금 '설치'까지는 미리 확인해 보겠습니다."
"3일차처럼 로컬 PC의 터미널을 열어주세요. npm install -g @nestjs/cli 명령어를 사용하면 '글로벌(g)'로, 즉 PC 전체에 Nest CLI 도구를 설치할 수 있습니다. (이미 설치하신 분은 넘어가셔도 됩니다.)" (설치 확인 시연)
"이 CLI가 왜 강력하냐면, 두 가지 핵심 기능을 제공하기 때문입니다."
1.
프로젝트 생성 (nest new ...):
nest new deepfake-backend
이 명령어 한 줄이면, 우리가 모듈 3에서 실습할 것처럼, main.ts, app.module.ts 등 NestJS 프로젝트에 필요한 모든 '기본 뼈대'가 자동으로 생성됩니다.
2.
자동 생성 (nest g ...):
g는 'generate'의 약자입니다.
nest g module usersusers.module.ts 파일 생성
nest g controller usersusers.controller.ts 파일 생성
nest g service usersusers.service.ts 파일 생성
... 그리고 module 파일에 이 controllerservice를 알아서 '등록'까지 해줍니다.
"결론적으로, NestJS CLI는 개발자가 직접 작성해야 하는 수많은 '보일러플레이트(Boilerplate)' 코드를 자동으로 생성해 줍니다.
개발자는 '파일 만들기', 'import 하기' 같은 귀찮은 작업 대신, '실제 비즈니스 로직'에만 집중할 수 있게 되어 개발 속도가 비약적으로 향상됩니다."
"우리는 '모듈 3' 실습에서 nest g resource items라는 명령어를 쓸 건데, 이건 방금 본 module, controller, service 생성을 한 번에 다 해주는 더 강력한 명령어입니다. 실습 때 직접 경험해 보시죠."
"자, 그럼 이 CLI가 자동으로 생성해 주는 '빌딩 블록', 즉 'Module', 'Provider(Service)', 'Controller'가 각각 무엇을 의미하는지 다음 장에서 자세히 알아보겠습니다."

3. NestJS의 빌딩 블록 (Building Blocks)

Modules:
@Module() 데코레이터.
애플리케이션의 '기능 단위' (레고 블록).
(예: UserModule, ItemsModule, AuthModule)
기능에 필요한 ControllersProviders를 '캡슐화'함.
Providers (Services):
@Injectable() 데코레이터.
'비즈니스 로직' (실제 기능)을 처리하는 클래스.
(예: DB에서 데이터 조회, 계산 수행, API 호출 로직)
'컨트롤러'로부터 '주입(Inject)'되어 사용됨.
Controllers:
@Controller() 데코레이터.
'라우터(Router)' 역할.
'HTTP 요청(Request)'을 수신하고, '응답(Response)'을 반환.
실제 로직은 'Provider(Service)'에게 위임함.
"자, 방금 전까지 우리는 'Nest CLI'라는 '자동화 도구'가 nest newnest g 같은 명령어로 '보일러플레이트' 코드를 자동으로 생성해준다고 배웠습니다.
그럼, 이 CLI가 자동으로 생성해 주는 '빌딩 블록(Building Blocks)', 즉 NestJS 아키텍처를 구성하는 가장 중요한 세 가지 요소가 무엇인지 알아보겠습니다.
캔버스의 3. NestJS의 빌딩 블록 항목을 봐주세요. 바로 **Modules(모듈), Providers(프로바이더), Controllers(컨트롤러)**입니다."
"첫 번째, **Modules (모듈)**입니다.
@Module() 데코레이터가 붙습니다.
이것은 3일차 React의 '컴포넌트'와 비슷한 개념이지만, UI가 아닌 **'기능(Feature)'**을 기준으로 묶는 '캡슐' 또는 '레고 블록'입니다.
예를 들어, UserModule은 '사용자' 관련 기능(회원가입, 로그인)을, ItemsModule(우리가 실습할)은 '아이템' 관련 기능(탐지 요청, 결과 조회)을 모두 담는 '기능 단위'입니다.
Module은 내부에 어떤 ControllerProvider가 한 세트인지 NestJS에게 알려주는 '설명서' 역할을 합니다."
"두 번째, Providers (프로바이더), 그리고 우리가 가장 많이 부르게 될 이름인 **Services (서비스)**입니다.
@Injectable() 데코레이터가 붙습니다. ('주입 가능하다'는 뜻)
여기가 바로 '실제 비즈니스 로직', 즉 '핵심 두뇌'가 들어가는 클래스입니다.
컨트롤러가 '요청'을 받으면, '그 요청을 실제로 어떻게 처리할지'가 여기에 코딩됩니다.
(예시) 'DB에서 데이터를 조회'하거나, '계산을 수행'하거나, '5일차에 만들 FastAPI(AI 서버)로 API를 호출'하는 등의 **'실제 일(Logic)'**은 모두 이 Service가 담당합니다.
Service는 잠시 후 배울 Controller로부터 '주입(Inject)'되어 사용됩니다."
"세 번째, **Controllers (컨트롤러)**입니다.
@Controller() 데코레이터가 붙습니다.
컨트롤러의 역할은 **'라우터(Router)', 즉 '교통 경찰'**입니다.
'HTTP 요청(Request)'을 **'수신'**하고, '응답(Response)'을 **'반환'**하는 것이 유일한 임무입니다.
(예시) POST /items라는 요청이 오면, 컨트롤러는 그 요청을 받아서, '아, 이건 Service가 할 일이네'라고 판단합니다.
그리고 Service에게 **'실제 로직(일)'을 '위임(Delegate)'**합니다. 컨트롤러가 직접 DB에 접근하거나 복잡한 계산을 하지 않습니다.
Service가 일을 끝내고 '결과'를 돌려주면, 컨트롤러는 그 결과를 받아 '응답(Response)'을 클라이언트(React)에게 전송합니다."
"자, 그럼 여기서 의문이 하나 생기실 겁니다. '강사님, DB랑 통신하는 Repository(저장소)는 어디 있나요?'
캔버스의 모듈 2: 계층형 아키텍처 [cite: 54-71]를 잠시 미리 보면, 분명히 Data Access Layer (Repository)라는 '창고 관리인'이 존재합니다.
그런데 왜 '기본 빌딩 블록'에는 빠져있을까요?"
"그 이유는, 강사님께서 짚어주신 대로, **NestJS에서 'Repository'는 Provider(Service)가 사용하는 '도구'**이기 때문입니다. 즉, 아키텍처상 Service포함되는(사용되는) 개념입니다.
Controller (교통 경찰) → Service (핵심 두뇌) → Repository (창고 관리인)
"그런데 우리가 모듈 4, 5에서 'TypeORM' 같은 ORM을 사용하게 되면, 이 구조는 더욱 최적화됩니다.
우리는 Repository라는 클래스 '구현체(Implementation)'를 직접 만들지 않습니다. 즉, findAll() { return 'SELECT * ...' } 같은 SQL 코드를 우리가 짤 필요가 없다는 뜻입니다.
TypeORM이 우리가 모듈 5에서 만들 **'Entity(엔터티)'**를 기반으로, find(), save(), delete() 같은 모든 DB 통신 메서드가 이미 구현된 'Repository 구현체'를 '자동으로' 생성해 줍니다.
우리는 모듈 5 실습에서 constructor(@InjectRepository(Item) ...)이라는 코드 한 줄로, 이 '이미 완성된' Repository를 Service에 **'주입(Inject)'**받아서 사용하기만 하면 됩니다.
정리하자면, Repository는 우리가 직접 만드는 빌딩 블록이 아니라, TypeORM이 '제공'해주고, Service가 '주입'받아 사용하는, 최적화된 데이터 접근 객체입니다."
"지금까지 NestJS의 3가지 빌딩 블록(Module, Provider, Controller)과, Provider(Service)가 사용하게 될 Repository의 개념까지 알아봤습니다.
다음 '모듈 2'에서는 이 빌딩 블록들이 어떻게 '계층형 아키텍처'와 'DI(의존성 주입)'라는 패턴으로 동작하는지 더 자세히 알아보겠습니다."

모듈 2: 아키텍처 및 디자인 패턴 (이론)

1. 계층형 아키텍처 (Layered Architecture)

2일차에 설계한 '플로우 차트'를 NestJS가 어떻게 구현하는가?
[ 3-Tier 아키텍처 ]
1.
Controller Layer: (교통 경찰)
main.ts -> app.module.ts -> items.controller.ts
@Controller('items'), @Get(), @Post()
오직 '요청'과 '응답'만 처리. (Request/Response)
2.
Service Layer (Provider): (핵심 두뇌)
items.service.ts
@Injectable()
*'실제 비즈니스 로직'**이 일어나는 곳.
(예: "아이템을 DB에 저장하라", "결과를 계산하라")
3.
Data Access Layer (Repository): (창고 관리인)
(실습 2에서 구현) TypeORM, items.repository.ts
실제 DB와 통신(SQL Query)하는 로직.
"자, 4일차 두 번째 이론 모듈, **'아키텍처 및 디자인 패턴'**입니다.
모듈 1에서는 NestJS의 '빌딩 블록'(Module, Controller, Provider)이 각각 무엇인지 '개념'을 배웠습니다.
이제 모듈 2에서는 이 빌딩 블록들이 '어떻게 함께 작동하는지', 그 '구조(Architecture)'를 알아보겠습니다."
"첫 번째 항목은 **'계층형 아키텍처 (Layered Architecture)'**입니다.
이 구조는 여기 계신 모든 개발자분들에게 이미 익숙한, 가장 표준적인 **'3-Tier 아키텍처'**입니다.
가장 중요한 질문은 이것입니다. '2일차에 우리가 설계한 '플로우 차트'를 NestJS가 어떻게 구현하는가?'"
"2일차에 우리는 React(클라이언트)가 API를 호출하면, 서버가 AI 모델을 호출하고 DB에 저장하는 '흐름'을 그렸습니다. NestJS는 이 '흐름'을 처리하기 위해, 서버 내부의 역할을 **'세 개의 계층(Layer)'**으로 명확하게 분리합니다.
캔버스의 [ 3-Tier 아키텍처 ] 항목을 보시죠. Controller, Service, Data Access Layer입니다."
"1. Controller Layer (컨트롤러 계층): (교통 경찰)
이 계층의 파일은 items.controller.ts입니다.
@Controller('items'), @Get(), @Post() 같은 데코레이터를 사용합니다.
모듈 1에서 배운 것처럼, 이 계층의 역할은 단 하나, **'교통 경찰'**입니다.
클라이언트(React)로부터 POST /items 같은 'HTTP 요청(Request)'을 받아서, '어떤 서비스'가 이 일을 처리해야 하는지 '라우팅'하고,
서비스가 일을 마치면, 그 '결과'를 받아 클라이언트에게 '응답(Response)'해줍니다.
절대로 컨트롤러가 직접 계산하거나 DB에 접근하지 않습니다. 오직 '요청'과 '응답'만 처리합니다."
"2. Service Layer (Provider): (핵심 두뇌)
이 계층의 파일은 items.service.ts입니다.
@Injectable() 데코레이터가 붙죠.
여기가 바로 '핵심 두뇌', 즉 **'실제 비즈니스 로직'**이 일어나는 곳입니다.
컨트롤러로부터 '아이템을 생성해 줘'라는 요청을 위임받으면,
(예시) '아이템을 DB에 저장하라', 'AI 서버를 호출해서 결과를 계산하라' 같은 **'실제 일(Logic)'**을 수행합니다."
"3. Data Access Layer (Repository): (창고 관리인)
이 계층은 모듈 5 실습에서 구현할 TypeORM이 담당합니다. (items.repository.ts라는 파일을 직접 만들 수도 있지만, TypeORM은 이 과정을 자동화해줍니다.)
이 계층의 역할은 **'창고 관리인'**입니다.
Service 계층(두뇌)으로부터 '아이템 저장해 줘'라는 명령을 받으면,
이 계층이 **'실제 DB와 통신(SQL Query)'**하는 로직을 수행합니다.
'SQL 쿼리'를 직접 작성하거나, 모듈 4에서 배울 'ORM 메서드'(repository.save())를 실행하는 유일한 곳입니다."
"정리하자면, 3일차의 React가 '탐지' 버튼을 누르면, 그 요청은 NestJS의 Controller('교통 경찰')에게 먼저 가고, ControllerService('핵심 두뇌')에게 일을 위임하며, ServiceRepository('창고 관리인')를 시켜 DB에 데이터를 저장한 뒤, 그 결과를 다시 Controller를 통해 React에게 응답하는 **명확한 '단방향 흐름'**이 만들어집니다. 이것이 NestJS가 강제하는 '계층형 아키텍처'입니다."
"그럼 다음 장에서는, 이 ControllerService가 서로 어떻게 '느슨하게' 연결되는지, NestJS의 핵심 사상인 **'DI(의존성 주입)'**에 대해 알아보겠습니다."

2. DI (의존성 주입) 및 IoC (제어의 역전)

Problem: 클래스 간의 강한 결합(Coupling).
Controllernew ItemsService()처럼 직접 객체를 생성하면,
ControllerItemsService에 '강하게 의존'하게 됨.
테스트가 어렵고, 유지보수가 힘듦.
Solution (IoC): NestJS가 '제어'를 '역전'시킴.
개발자가 new로 생성하는 게 아니라, NestJS 프레임워크가 객체를 생성하고 관리함.
How (DI):
Service@Injectable()로 "주입 가능"하다고 선언.
Module에 "이 모듈은 ItemsService를 쓴다"고 providers에 등록.
Controllerconstructor(private readonly itemsService: ItemsService)처럼 '생성자'에서 '주입'을 요청함.
결과: NestJS가 알아서 ItemsService 객체를 만들어 Controller에 주입함.
"자, 모듈 2의 첫 번째 주제로, 우리는 NestJS가 '계층형 아키텍처'를 사용해서 Controller(교통 경찰), Service(두뇌), Repository(창고)로 역할을 명확히 분리한다는 것을 배웠습니다."
코드에서 분리한다는 의미는 결국 파일 자체를, 파일은 또한 클래스 파일 자체를 분리한다는 말과 같습니다. 말그대로 컨트롤러 파일과 서비스 파일이 분리 된다는 것인데요.
컨트롤러 파일은 매개체 역할의 코드만 있을것이고, 서비스 파일은 비지니스 로직 즉, 연산과 관련된 내용만 있을 것 입니다. 레포지토리는 또한 데이터관련 코드만 있겠죠. 하지만 실제로 프로그램은 이렇게 따로따로 움직이지 않고 마치 우리가 달리기 할때 팔과 다리와 발가락과 무릎과 모든 부위가 나누어져있지만 서로 유기적으로 움직이면서 달린다는 행동을 취하는 것 처럼,
웹 서비스도 어떤 동작에 따라 이 모든것들이 유기적으로 연관된 부분들이 움직이게 됩니다. 하지만 코드가 분리되어 있는 점에서 어떻게 연결되어 있을까를 생각해봐야 합니다.
우선 직관적으로 단순한 방법은 이 코드들이 모두 한부위(파일)에 모여있는 것입니다. 그럼 서로 직접연결되어 있기 때문에 당연히 모두 움직이겠죠. 하지만 이러면 우리의 몸이 부위별로 나눠져있기 때문에 효율적으로 필요한 것만 움직이도록 설계되있는 것 처럼 프로그램도 필요한것만 사용하고 서로 통신되도록 구조화 시켜야 효율적이고 유지보수가 편리해집니다. 우리가 팔이 다치면 팔만 치료하듯이, 소프트웨어에도 어느부분이 오류가 있는지, 고쳐야 할 때 빠르게 찾고 교체하거나 수리 할 수 있겠죠.
이런 효율적으로 구조화하여 필요한 부분만 연결 시키는 것을 모듈화라고 하며 소프트웨어에서는 “낮은 결합도” 라고 합니다. 반대로 높은 결합도는 관련있는 다른 부분이, 직접 코드에 큰 영향(최악은 직접 포함 된 경우)을 미칠 수 밖에 없는 상태를 높은 결합도를 가지고 있다고 합니다. 좋은 영향이던 나쁜 영향이던 이 부품과 관련없는 부분은 낮은 결합도를 지향하도록 설계해야 유리하겠죠. 하지만 1개의 부품 내부를 살펴보겠습니다. 해당 부품은 해당 부품의 이름에 걸맞게 그 기능들만 수행 할 수 있도록 똘똘뭉쳐야 합니다. 네, 그 모듈은 “높은 응집도”를 가지고 있어야 한다라고 합니다. 이처럼 모듈간의 결합도는 낮추고, 모듈의 응집도를 높이는 것이 좋은 소프트웨어를 개발하는데 꼭 필요한 부분이라 할 수 있습니다.
이러한 관점에서 많은 것들을 바라 볼 수 있습니다. 저희는 AI 소프트웨어는 Python을 쓰고, 프론트엔드는 React, 백엔드는 NestJS, 데이터베이스를 MySQL을 사용하듯, 각 부분의 역할에 맞도록 전문적으로 응집도가 높은 프레임워크를 사용하고 있으며, 이 각 아키텍처들을 각 관문들을 통해서만 연결하여 낮은 결합도를 유지하고자 하는 아키텍처 설계 관점에서도 이미 이렇게 진행되고 있습니다.
한단계 확대를 해서 현재 배우고 있는 백엔드인 NestJS 내부에서만 보더라도 이전에 Controller, Service, Repository 처럼 3개의 계층으로 구분해서 구현될 것이라 했습니다. 이 또한 백엔드 내부를 각 파일의 역할을 나누어 각 역할에 집중되도록하고 그 사이를 연결하는, 서로 통신되도록 구성하는 설계 철학이 모두 담겨있는 것입니다.
이렇게 모듈화를 통해서 재활용성과 유지보수성을 확보 할 수 있고, 더 나은 코드와 다음 개발에서는 보다 빠르게 재사용할수 있는 부품들을 만들고, 또한 다른 개발자가 만든 재활용 부품을 사용하는 것이 개발 생태계가 이어질 수 있는 힘입니다.
여기까지 조금 개발과 관련된 주변이야기를 했지만, 다시 NestJS로 돌아와서 각 계층별 분리는 좋지만 서로 통신되도록 연결도 되어야합니다.
이런 연결을 의존성 주입이라고 합니다. 특히 각 모듈파일로 분리된 계층들을 “필요한 파일이(실제론 클래스)” 대상 클래스를 사용 할 수 있도록 주입하여 사용하게 됩니다.
쉽게 컨트롤러는 서비스로 데이터를 전달한다 라는 말에서 컨트롤러 클래스는 서비스 클래스를 가지고 있어야 데이터를 전달 할 수 있고, 이런 것을 컨트롤러가 서비스를 호출한다라고 합니다. 이어서 서비스도 레포지토리를 호출해야 하겠죠.
코드에서는 그 클래스의 메서드를 호출하기 위해서는 직접 그 코드에 상단에 미리 정의되어 있어야 합니다. 전혀 모르는 코드는 사용할 수 없는것이 기본인것 처럼요.
따라서 컨트롤러는 서비스의 코드 사용이 필요하기 때문에, 서비스라는 객체 모듈 자체를 가지고 있게 만듭니다. (실제 코드를 가지고 있는 것이 아닌) 이걸 어렵게는 의존성주입이라고 하며, 이런 것들을 연결해주고 서로 주입 관리해주는 부분을 DI 컨테이너라고 합니다. 이 의존성 생성 과 주입을 개발자가 직접 관리하는 것이 아니라, DI 컨테이너가 관리하기 때문에 프레임워크가 개발자의 코드를 관여하는 형태가 되면서, 이것을 IoC(제어의 역전) 이라고 합니다.
결과적으로 DI를 통해 제어의 역전이 발생했고, 이것은 낮은 결합도, 높은 응집도를 위한 설계를 진행하게 되는겁니다.
이 이론적인 부분은 처음엔 와닿지 않거나 조금 햇갈리게 느껴지실수 있지만, 용어자체가 어렵게 느껴져서 그렇다고 생각합니다. 실제로 우린 이미 제어의 역전을 계속 경험하고 있습니다. 프로그래밍 언어의 규칙을 따라야하는것, 프레임워크가 지정하는 틀대로 규칙대로 프로그램을 구성하는것, 이 행동들 자체의 목적은 빠르고 쉽게 그 가이드에 맞춰서 개발하면 좋은 프로그램을 빠르게 개발 할 수 있기 때문에 의지하게 되고 믿고 진행하는 것입니다. 따라서 제어의 역전을 통해 규칙에 따라 개발되면 좋은 품질의 소프트웨어를 신뢰도 있게 구성 할 수 있을 것입니다.
이렇게 NestJS의 의존성 주입은 각 모듈 파일간의 주입을 통해서 진행된다는 것으로 정리하겠습니다.
그 다음으로 중요한것은 해당 프레임워크의 규칙인 데코레이터(애너테이션)을 살펴보겠습니다.

주요 데코레이터 @

1. 핵심 빌딩 블록 (아키텍처)
@Module(): NestJS 앱의 '기능 단위'를 정의함. controllersproviders 배열에 이 모듈이 사용할 클래스들을 등록.
@Injectable(): 이 클래스가 DI(의존성 주입) 시스템에 '주입 가능한 Provider'(주로 Service)임을 선언.
@Controller('prefix'): 이 클래스가 HTTP 요청을 처리하는 '라우터'임을 선언함. prefix로 기본 경로(예: 'items')를 지정.
2. 컨트롤러 (라우팅 및 데이터 추출)
@Get(), @Post(), @Patch(), @Delete(): 각 HTTP Method(GET, POST 등) 요청을 처리할 '핸들러(메서드)'를 지정.
@Param('id'): URL 경로에서 파라미터(예: /items/:idid 값)를 추출.
@Body(): 클라이언트(React)가 보낸 Request Body 전체를 DTO 객체로 받아옴.
3. 데이터베이스 (TypeORM 연동)
@Entity(): 해당 클래스를 DB '테이블'과 매핑함 (2일차 ERD가 코드가 되는 순간).
@PrimaryGeneratedColumn(): @Entity 내부에서 '기본 키(PK)' 속성을 정의 (자동 증가).
@Column(): 해당 속성을 DB '컬럼'과 매핑함.
@InjectRepository(Item): Serviceconstructor에서 Item Entity의 Repository 객체를 '주입'받을 때 사용.
4. 데이터 유효성 검사 (DTO & Pipe)
@IsString(), @IsNotEmpty(): class-validator 라이브러리. ValidationPipe와 함께 DTO 클래스 속성의 유효성을 '자동으로' 검사.

모듈 3: 실습 1 - 프로젝트 환경 구축 및 API 테스트

1. 실습 목표

NestJS CLI로 백엔드 프로젝트 환경 구축.
'Resource' 명령어로 CRUD API 뼈대 자동 생성.
Postman으로 API 동작 테스트 (React 연동 전).
모듈 1, 2에서 우리는 NestJS가 '왜' 필요한지(자유도 문제 해결), 그리고 '어떻게' 동작하는지(DI, 계층형 아키텍처) 이론을 배웠습니다.
이제 이 이론을 코드로 직접 구현해 볼 시간입니다."
"이번 실습 목표는 캔버스에 보시는 것처럼 세 가지입니다.
1.
Nest CLI로 백엔드 프로젝트 환경을 구축합니다.
2.
'Resource' 명령어라는 Nest CLI의 핵심 기능을 사용해, 2일차에 설계한 CRUD API 뼈대를 자동으로 생성합니다.
3.
3일차 React와 연결하기 전에, Postman이라는 툴을 사용해 '이 백엔드 서버가 정말 독립적으로 동작하는지' 먼저 테스트합니다."
추가로 Postman이라는 것을 미리 설치해둡시다. 해당 프로그램은 데이터 통신을 간단하게 테스트 할 수 있는 도구로 GET요청같은 경우엔 브라우저로 손쉽게 할 수 있지만, 그 외 POST, PUT, DELETE등 다른 HTTP 메서드 요청을 테스트 할 수 있는 도구입니다.

2. Nest CLI 설치 (로컬)

3일차와 동일하게 로컬 터미널(VS Code 터미널) 사용.
(최초 1회만 실행)
npm install -g @nestjs/cli

1. Nest CLI 설치 (로컬)

"3일차에 React(Vite)를 설치하면서, 우리는 이미 nodenpm은 설치가 완료된 상태입니다."
"하지만 NestJS 프로젝트를 생성하고 관리하려면, Vite와 유사한 '전용 커맨드 라인 도구(CLI)'가 필요합니다.
다 같이 3일차에 사용했던 로컬 PC의 터미널을 열어주세요. (VS Code 터미널 등)"
"먼저, nest 명령어가 설치되어 있는지 확인해 보겠습니다."
Bash
nest --version
(실행) "만약 9.x.x 같은 버전 번호가 나오면 이미 설치된 것입니다. 하지만 'command not found' (명령어를 찾을 수 없습니다)라고 나온다면, 지금 바로 **글로벌(-g)**로 Nest CLI를 설치해야 합니다."
"설치가 안 되신 분들은, 다음 명령어를 입력해 주세요. (이미 설치된 분은 안 하셔도 됩니다.)"
Bash
npm install -g @nestjs/cli
(실행) "이것은 3일차 npm create vite와는 다르게, -g 옵션으로 nest라는 명령어를 우리 PC '전체'에 설치하는 과정입니다."

3. 새 프로젝트 생성

3일차 React 프로젝트(my-app)가 있는 폴더 으로 이동.
cd ..
nest new detect-backend (프로젝트 명)
(package manager는 npm 선택)
"좋습니다. 이제 nest 명령어가 준비되었습니다. 본격적으로 백엔드 서버 프로젝트를 생성해 보겠습니다."
"중요: 3일차에 만든 React 프로젝트(detect-frontend) **'안'**이 아니라, 그 **'밖'**에, 즉 React와 NestJS가 **'같은 레벨'**에 있도록 폴더를 만들어야 합니다."
"현재 터미널이 detect-frontend 폴더 안이라면, cd .. 명령어로 상위 폴더로 이동해 주세요."
"이제, nest new 명령어로 'detect-backend'라는 이름의 새 프로젝트를 생성합니다."
Bash
nest new detect-backend
(실행) "명령어를 실행하면, NestJS가 '어떤 패키지 매니저를 사용할 것인지' 물어봅니다. npm / yarn / pnpm이 나오죠? 우리는 3일차 React와 통일하기 위해, 키보드 화살표로 **npm**을 선택하고 엔터를 칩니다."
"(설치 대기) ... NestJS가 package.json에 필요한 모든 기본 패키지들을 npm으로 설치하고 있습니다. 3일차 npm install과 동일한 과정입니다."

4. 프로젝트 구조 분석

cd deepfake-backend
VS Code로 폴더 열기.
src/main.ts: 앱이 시작되는 '진입점'.
src/app.module.ts: 루트 모듈.
src/app.controller.ts: 기본 컨트롤러.
src/app.service.ts: 기본 서비스.
npm run start:dev 실행 (Hot-reload 지원). 그냥 npm run start로 해도 상관없습니다.
"(VS Code로 화면 전환) src 폴더를 열어보세요. 3일차 Vite가 App.tsx를 만들어 준 것과는 다르게, NestJS는 우리가 모듈 1, 2에서 배운 **'아키텍처'**에 필요한 파일들을 이미 다 만들어 줬습니다."
src/main.ts: NestJS 애플리케이션을 '시작'시키는(Bootstrap) 진입점 파일입니다.
src/app.module.ts: @Module() 데코레이터가 붙은, 앱의 '루트 모듈'입니다.
src/app.controller.ts: @Controller()가 붙은, '루트 컨트롤러'(교통 경찰)입니다.
src/app.service.ts: @Injectable()이 붙은, '루트 서비스'(핵심 로직)입니다.
"이것이 바로 '주장이 강한(Opinionated)' 프레임워크입니다. 이미 구조가 다 잡혀있죠."
"자, 이 서버를 바로 실행해 보겠습니다. VS Code 터미널에서 다음 명령어를 입력하세요."
Bash
npm run start:dev
"이 명령어는 3일차 npm run dev와 똑같이, 'Hot-reload', 즉 코드가 바뀌면 서버가 자동으로 재시작되는 개발용 서버 명령어입니다."
"(로그 확인) ... 터미널에 Application is running on: http://localhost:3000 메시지가 뜨면 성공입니다. 이제 브라우저에서 http://localhost:3000 주소로 접속해 보세요. app.service.ts가 반환하는 **'Hello World!'**가 보일 겁니다."

5. 리소스(Resource) 자동 생성

NestJS의 가장 강력한 기능.
(새 터미널) nest g resource detection
(g = generate)
(질문 1) What name would you like to use for the resource? detection
(질문 2) What transport layer would you like to use? REST API
(질문 3) Would you like to generate CRUD entry points? y (Yes)
결과:
src/detection 폴더가 생기고,
detection.module.ts, detection.controller.ts, detection.service.ts
detection.entity.ts, dto/create-detection.dto.ts
CRUD API 뼈대가 10초 만에 자동으로 완성됨.
"좋습니다. 서버가 동작하는 것을 확인했습니다. 이제 2일차에 설계한 '탐지 내역' 관련 API를 만들 차례입니다. detection.controller.ts, detection.service.ts... 이 파일들을 우리가 직접 만들까요?"
"아닙니다. 'Nest CLI'가 이 작업을 대신해 줍니다."
-
(새 터미널) nest g resource detection --no-spec
이 명령어를 (실행) "자, CLI가 우리에게 3가지를 물어봅니다."
1.
"What name would you like to use for the resource?" -> detection 이라고 이미 입력되어 있습니다. 엔터를 칩니다.
2.
"What transport layer would you like to use?" -> "어떤 통신 방식을 쓸 거냐?"는 질문입니다. 우리는 REST API를 만들 것이므로, REST API (기본값)를 선택하고 엔터를 칩니다.
3.
"Would you like to generate CRUD entry points?" -> "CRUD(생성, 읽기, 수정, 삭제) API 뼈대를 '자동으로' 생성해 줄까?" -> 이것이 핵심입니다. y (Yes)를 누르고 엔터를 칩니다.
"자, 이제 VS Code의 src 폴더를 주목해 주세요. src/detection이라는 폴더가 통째로 생겼습니다!
그 안에는 detection.module.ts, detection.controller.ts, detection.service.ts는 물론, detection.entity.tsdto/create-detection.dto.ts까지, 2일차에 우리가 설계했던 CRUD API의 모든 뼈대(Boilerplate)가 10초 만에 자동으로 완성되었습니다.
심지어 src/app.module.ts 파일을 열어보시면, Imports 배열에 DetectionModule이 '자동으로' 등록까지 되어있습니다.
이것이 '주장이 강한(Opinionated)' 프레임워크와 'Nest CLI'가 주는 최고의 생산성입니다."
"믿기지 않죠? 그럼 '탐지 내역(detection)' API가 정말 동작하는지 테스트해 보겠습니다. 3일차 React와 아직 연결하기 전이기 때문에, **'Postman'**이라는 툴을 사용해 API가 독립적으로 동작하는지 확인해야 합니다."
"첫 번째 터미널의 npm run start:dev 서버가 계속 실행 중인지 확인하세요. (포트 3000번)"

6. Postman을 이용한 API 테스트 (★수정됨)

npm run start:dev 실행 (포트 3000번)
Postman 툴 실행.
(GET) http://localhost:3000/detection -> findAll() (배열 응답)
(POST) http://localhost:3000/detection (Body: raw/JSON) -> create()
(GET) http://localhost:3000/detection/1 -> findOne()
(3일차 React와 아직 연동 안 했지만, 백엔드 서버가 준비됨)
"좋습니다. 그럼 '탐지 내역(detection)' API가 정말 동작하는지 테스트해 보겠습니다. 3일차 React와 아직 연결하기 전이기 때문에, **'Postman'**이라는 툴을 사용해 API가 독립적으로 동작하는지 확인해야 합니다."
"첫 번째 터미널의 npm run start:dev 서버가 계속 실행 중인지 확인하세요. (포트 3000번)"
"자, 다 같이 Postman 툴을 엽니다."
"테스트 1: GET /detection (모두 조회)"
"Postman에서 새 탭을 엽니다."
"Method를 GET으로 둡니다."
"URL에 http://localhost:3000/detection 를 입력하고 'Send' 버튼을 누릅니다."
"결과(Response)를 보세요. Body에 This action returns all detection라는 문자열이 오면 성공입니다. detection.controller.tsfindAll() 메서드가 자동으로 호출된 것입니다."
"테스트 2: POST /detection (create)"
"Method를 POST로, Body에 아까처럼 JSON을 넣고 'Send'"
"결과: This action adds a new detection 문자열이 오면 성공입니다. create()가 호출됐습니다."
"이것으로 Controller 뼈대가 살아있음을 증명했습니다."
"테스트 3: GET /detection/:id (특정 1개 조회)"
"Method를 다시 GET으로 변경합니다."
"URL 끝에 /1을 붙여 http://localhost:3000/detection/1 로 만듭니다."
"'Send'를 누릅니다. This action returns a #1 detection 응답이 오면 성공입니다. findOne() 메서드가 호출되었습니다."
"좋습니다. 이것으로 4일차 첫 번째 실습이 끝났습니다. 우리는 Nest CLI로 백엔드 프로젝트를 생성했고, 'Resource' 명령어로 10초 만에 API 뼈대를 만들었으며, Postman으로 이 API 엔드포인트가 '살아있음'을 증명했습니다."
"하지만 지금 이 서버는 '가짜'입니다. Postman으로 데이터를 POST해도 반환(return)으로 지정된 문자만 응답하고 있죠. 실제 데이터베이스나 어떤 로직이 있지 않습니다.
"이제 다음 **모듈 4 (이론)**와 **모듈 5 (실습)**에서는, 이 '가짜' 서비스를 '진짜' MySQL 데이터베이스TypeORM으로 연결하는 작업을 진행하겠습니다."

모듈 4: Database와 ORM (이론)

RDBMS 및 SQL 기초

우선 실제 데이터베이스를 백엔드 서버와 연동하기 위해서 데이터베이스에 대해서 기본적으로 이해해야 합니다.
그 근간이 되는 관계형 데이터베이스와 SQL의 기초를 정리하는 것은 매우 중요합니다.

관계형 데이터베이스(RDBMS) 및 SQL 기초

1. 관계형 데이터베이스 (RDBMS)란?

정의: 데이터를 **'테이블(Table)'**이라는 정형화된 2차원 표 형태로 저장하고 관리하는 데이터베이스 시스템을 의미함. (우리가 4일차에 사용할 MySQL이 대표적임).
주요 구성 요소:
테이블 (Table): 데이터가 실제로 저장되는 기본 단위 (엑셀 시트와 유사). (예: 2일차에 설계한 Detection 엔터티 -> detection 테이블)
행 (Row / Record): 테이블에 저장된 하나의 완전한 데이터 항목. (예: 'ID 1번 탐지 내역'의 모든 정보)
열 (Column / Attribute): 데이터의 '속성' 또는 '타입'. (예: id, filename, filetype, createdAt)
키 (Key):
기본 키 (Primary Key, PK): 각 행(Row)을 '고유하게' 식별하는 값. (예: id 컬럼)
외래 키 (Foreign Key, FK): 한 테이블이 다른 테이블을 '참조(연결)'하기 위해 사용하는 키. 2일차에 설계한 ERD의 **'관계(Relationship)'**를 구현하는 핵심 요소임.

2. SQL (Structured Query Language)이란?

정의: 이러한 RDBMS(관계형 데이터베이스)와 통신(데이터를 넣거나, 빼거나, 수정하거나, 정의)하기 위해 사용하는 '표준 질의 언어'.
ORM과의 관계: 모듈 4에서 배운 TypeORM 같은 ORM은, 개발자가 작성한 TypeScript 코드(예: repository.find())를, 내부적으로 이 **'SQL'**로 '번역'하여 DB에 대신 실행해 줌.

3. SQL의 3가지 주요 종류 (구분)

SQL 명령어는 그 '역할'에 따라 크게 3가지로 구분함.
1. DDL (Data Definition Language - 데이터 정의어)
역할: 데이터베이스의 '구조(Structure)'를 정의하는 언어 (테이블/DB의 '설계도'를 다룸).
대표 명령어:
CREATE: 테이블, 데이터베이스를 생성.
ALTER: 테이블 구조를 변경 (예: 컬럼 추가, 타입 변경).
DROP: 테이블, 데이터베이스를 삭제.
(TypeORM의 synchronize: true 옵션은, detection.entity.ts 파일을 읽고 이 DDL(CREATE, ALTER)을 '자동으로' 실행해 주는 기능임)
2. DML (Data Manipulation Language - 데이터 조작어)
역할: 테이블 내의 '데이터(Row)'를 **조작(CRUD)**하는 언어 (가장 많이 사용됨).
대표 명령어 (CRUD):
SELECT (Read): 데이터를 조회.
INSERT (Create): 새 데이터를 삽입.
UPDATE (Update): 기존 데이터를 수정.
DELETE (Delete): 기존 데이터를 삭제.
(TypeORM의 repository.find(), repository.save(), repository.delete() 메서드가 이 DML을 대신 실행해 줌)
3. DCL (Data Control Language - 데이터 제어어)
역할: 데이터에 대한 '접근 권한'과 '보안'을 제어하는 언어 (주로 DB 관리자가 사용).
대표 명령어:
GRANT: 특정 사용자에게 SELECT, INSERT 등 특정 작업의 권한을 부여.
REVOKE: 사용자의 권한을 회수.

1. Why ORM? (Object-Relational Mapping)

Problem: SQL과 객체지향(TS/JS)의 '패러다임 불일치'.
(TS) class User { ... } (객체)
(SQL) SELECT * FROM users WHERE ... (문자열 쿼리)
서비스 로직이 'SQL 문자열'로 가득 차게 됨 (유지보수 어려움).
Solution (ORM): SQL 없이, '객체'와 '메서드'로 DB를 조작.
(ORM) userRepository.find({ where: { id: 1 } })
코드가 'SQL'이 아닌 'TypeScript'가 됨 (가독성, 안정성 향상).
"자, 4일차 모듈 3 실습을 모두 마쳤습니다. Postman으로 http://localhost:3000/detection을 테스트해 보니, API가 동작하는 것은 확인했습니다.
하지만 우리는 detection.service.tscreate() 메서드가 실제로 어떤 연산은 없는 문자만 반환하는 '가짜' 서비스죠.
"이제 모듈 4모듈 5에서는 이 '가짜' 서비스를 '진짜' MySQL 데이터베이스와 연동하여, 데이터가 영구적으로 저장되도록 만들 것입니다."
"DB를 연동한다고 하면, 많은 분들이 Service 로직 안에서 'SQL 쿼리'를 직접 작성하는 것을 떠올립니다. 하지만 NestJS는 그렇게 하지 않습니다. 바로 **'ORM'**이라는 기술을 사용하죠."
"첫 번째 항목, **'Why ORM? (Object-Relational Mapping)'**을 보겠습니다."
"**'Problem'**은 명확합니다. **'SQL과 객체지향(TS/JS)의 '패러다임 불일치'**입니다."
"이게 무슨 뜻일까요?
우리가 4일차에 다루는 NestJS(TypeScript)는 **'객체(Object)'**로 생각합니다. (예시: class User { ... })
하지만 우리가 사용할 MySQL은 **'관계형 데이터(테이블)'**로 생각합니다.
"그래서 Service 로직을 짤 때, 객체지향의 세계에서 갑자기 '문자열'의 세계로 넘어가야 합니다." (예시: SELECT * FROM users WHERE id = 1 ... )
"이렇게 Service 파일(detection.service.ts) 안에 SQL '문자열'이 가득 차게 되면,
1.
SELECT 오타가 나도 VS Code가 못 잡아줍니다. (컴파일 오류가 아닌 런타임 오류 발생)
2.
JOIN이 복잡해지면 SQL 문자열은 끔찍하게 길어집니다.
3.
DB를 MySQL에서 PostgreSQL로 바꾸면? 이 모든 SQL '문자열'을 다 찾아서 수정해야 합니다.
"이것이 바로 '유지보수가 어려운' 코드입니다."
"그래서 **'Solution (ORM)'**이 나왔습니다. ORM은 'Object-Relational Mapping', 즉 '객체-관계 매핑'의 약자입니다.
이름 그대로, SQL '문자열' 없이, 오직 **'객체(Object)'와 '메서드(Method)'**만으로 DB를 조작하게 해주는 '번역기'입니다."
"예시를 보시죠. (SQL) SELECT * FROM users WHERE id = 1 이 쿼리 문자열이, (ORM) userRepository.find({ where: { id: 1 } }) ...이렇게 바뀝니다."
"이게 우리에게 주는 장점은 명확합니다." "코드가 'SQL'이 아닌, 100% 'TypeScript'가 됩니다.find, where 같은 '메서드'를 쓰기 때문에, whre라고 오타를 치면 VS Code가 즉시 에러를 잡아줍니다.
SQL 쿼리를 몰라도, 자바스크립트/타입스크립트의 '객체'와 '메서드'만으로 DB와 통신할 수 있게 되는 것이죠. 이것이 **'가독성'**과 **'안정성'**을 비약적으로 향상시키는 ORM의 존재 이유입니다."

2. TypeORM

NestJS가 공식 지원하는 대표적인 TypeScript ORM.
2일차에 설계한 'ERD'를 'Entity 클래스'로 1:1 매핑.
ORM도 Prisma, Sequelize 등 종류가 많습니다. 왜 하필 TypeORM일까요?"
"NestJS가 공식 지원하는 대표적인 TypeScript ORM이기 때문입니다. @nestjs/typeorm이라는 전용 모듈을 제공해서, 모듈 2에서 배운 DI(의존성 주입) 패턴과 완벽하게 호환됩니다."
"그리고 가장 중요한 점은, 2일차에 우리가 설계했던 'ERD'를 기억하시나요? TypeORM은 2일차에 설계한 'ERD'를 'Entity 클래스'로 1:1 매핑합니다."
"2일차에 그렸던 'Detection'이라는 ERD가, 모듈 5 실습에서 @Entity() 데코레이터가 붙은 Detection 클래스로 그대로 변환될 것입니다. 즉, 2일차의 설계가 4일차의 '코드'가 되는 것이죠."
"그럼, 다음 '모듈 5' 실습에서는 이 TypeORM을 사용해서 detect-backend 프로젝트와 '진짜' MySQL DB를 연동하는 작업을 시작하겠습니다."

모듈 5: 실습 2 - DB 연동 및 Entity 구현

1. 실습 목표

NestJS 프로젝트와 MySQL 데이터베이스 연동.
TypeORM 'Entity'로 구현.
'Service' 로직을 실제 DB 연동 로직으로 수정.

2. TypeORM 관련 패키지 설치

(터미널)
npm install @nestjs/typeorm typeorm mysql2

3. AppModule에 TypeORM 설정

app.module.ts 수정.
TypeOrmModule.forRoot({...}) 설정 추가.
(host, port, username, password, database)
(entities: [Item], synchronize: true) -> 'synchronize'는 개발용 (Entity 기준으로 DB 자동 변경)

4. Entity 생성 (ERD -> Code)

items.entity.ts 파일 수정.
2일차 ERD(simple_erd.html) [cite: 1-61] 설계를 코드로 변환.
@Entity()
@PrimaryGeneratedColumn() (id)
@Column() (filename, filetype)
@CreateDateColumn() (createdAt)

5. Repository 패턴 적용

items.module.ts 수정:
imports: [TypeOrmModule.forFeature([Item])] (이 모듈이 Item Entity를 쓴다고 등록)
items.service.ts 수정 (DI):
@Injectable()
constructor(@InjectRepository(Item) private itemsRepository: Repository<Item>) {}
(NestJS가 itemsRepository를 '주입'해 줌)
create(item) 메서드 수정:
(Before) this.items.push(item) (단순 배열)
(After) const newItem = this.itemsRepository.create(item);
(After) return this.itemsRepository.save(newItem); (실제 DB 저장)
findAll() 메서드 수정:
(Before) return this.items;
(After) return this.itemsRepository.find(); (실제 DB 조회)

6. Postman 재테스트

(POST) http://localhost:3000/items -> DBeaver/DataGrip에서 DB 확인.
(GET) http://localhost:3000/items -> DB 데이터가 JSON으로 반환됨.

모듈 6: REST API 리팩토링 (이론/실습)

1. DTO (Data Transfer Object)

Problem: create(item) 메서드가 클라이언트의 body를 그대로 받으면, 'body'에 뭐가 들었는지 알 수 없음 (타입 불안정).
Solution (DTO): '데이터 전송 객체'
create-item.dto.ts (CLI가 이미 생성함)
클라이언트가 보내야 할 '데이터의 모양(타입/클래스)'을 명시.
items.controller.ts 수정:
(Before) @Body() body: any
(After) @Body() createItemDto: CreateItemDto

2. Pipe (ValidationPipe)

Problem: DTO를 썼지만, 클라이언트가 filename을 빼먹거나, filetype을 숫자로 보내도 서버가 받음.
Solution (Pipe): 컨트롤러에 도달하기 전 '데이터 변환/검증'
npm install class-validator class-transformer
main.tsapp.useGlobalPipes(new ValidationPipe()) 추가.
create-item.dto.ts 수정:
@IsString()
@IsNotEmpty() 같은 '데코레이터' 추가.
결과: 클라이언트가 filename을 빼먹고 요청하면, NestJS가 400 Bad Request 에러를 '자동으로' 반환함.

요약 및 5일차 예고

4일차 성과:
NestJS로 'RESTful API 서버' 구축 완료.
2일차 ERD(simple_erd.html) [cite: 1-61]를 TypeORM Entity로 구현.
Postman으로 '실제 DB'에 CRUD가 되는 것을 확인.
DTO/Pipe로 '안정적인' API 엔드포인트 완성.
5일차 예고 (AI 모델 분석):
4일차에 만든 NestJS는 '메인 서버'임.
5일차에는 'AI 모델'을 서빙할 별도의 **'Python (FastAPI) 서버'**를 구축할 것임.
3일차(React) -> 4일차(NestJS) -> 5일차(FastAPI)로 이어지는 풀스택 아키텍처의 마지막 조각을 맞출 준비.
Search