| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- 카카오뷰N잡
- Axios 라이브러리
- 실시간 통신
- git 협업하기
- 이석증
- 카카오뷰 성장
- 도서 원씽
- 성공에 대한 거짓말
- 위드굿즈 굿즈샵
- 엑셀 기초 함수
- CSS
- 카카오뷰 수익
- 엑셀 프린트하기
- 카카오뷰 부업
- 위드굿즈
- Git 팀 작업
- ppt 도형 색
- 자기관리
- 카뷰 수익 인증
- HTML
- ppt 다이어그램
- 카카오뷰 탭이동
- express.js 환경 셋팅
- 책 원씽
- 성공비법
- 원씽
- 30일 글쓰기
- 웹기초
- 카카오뷰 초보
- 카카오뷰 온라인 수익화
- Today
- Total
김데이의 개발공부
[ TIL ] Day 25 ~ 26 - 실습 : 관계 모델 CRUD 작성 + Route 분리 / 유효성 검사 적용 본문
[ TIL ] Day 25 ~ 26 - 실습 : 관계 모델 CRUD 작성 + Route 분리 / 유효성 검사 적용
theday365 2025. 10. 31. 19:32🗓️ 수업 일자 : 2025.10.30~31
✨ 오늘의 수업 평가 : [ PROJECT ] 드디어 형태가 보인다 👀🏗️
Comment 모델은 "목록 전용 페이지"와 각 "Product / Article에 모아 보기(및 부가 기능)"가 있다고 가정하고 작업했다.
"Comment 목록 전용 페이지"에서 사용할 RUD요청은 금방 작성 했는데,
C = Create 는 로직 상 Product / Article에 각각 생기는 게 맞을 거 같아서
각각의 모델 페이지에서 작업했는데 조금 헤멧다 😅
(각 모델의 입장에선 comments가 수정되는거니까.. PATCH로 작업 한건데 맞는지 모르겠다 ㅎ)
제일 어려웠던건 Product 또는 Article 상세 목록에서 특정 comment를 지정하여 "수정, 삭제" 하는 부분이었는데
이렇게 되면 route가 "/products/:productid/comments/:commentId" (혹은 더 짧게)되면서 ID도 두 번 사용하고,
실제 필요한 데이터의 깊이도 깊어지면서 뭔가 하면 할 수록 꼬여서
하루 넘게 작업하다 포기하고 "Comment 목록 전용 페이지" 에서 썻던 Patch/Delete 요청을 그대로 썼다
그러면서 전체 일정이 많이 늦어져서 마음이 급해졌..
👩💻 [프로젝트] 오늘 작업 내용 💻
- 각 모델 별 CRUD 작업
1. 관계형 모델 CRUD 작업 : Comment + Product / Comment + Article
2. 개별 모델 GET 요청 추가 작업 : Product / Article GET
- 모델 Route 분리 작업
1. 관계형 모델 Route 분리
2. 개별 모델 Route 재정리
- 유효성 검사 적용
0. 전체 작업 순서
- 작업 내용 분석 : 데이터 모델 정리, 작업 파일 구조화 등 (완벽하지 않아도 됨, 전체 틀을 잡는 다는 생각으로)
- 작업 공간 셋팅 : 필요한 npm 다운로드 및 설치 확인(기본 구문으로 테스트), 필요 시 Git 연결 (with new branch)
- 데이터 모델링 및 시딩 작업
- 데이터 모델링 : schema.prisma 파일에 작업 후 마이그레이션(초기 init) 작업
- 시딩 작업 : 비어있는 DB에 테스트용 초기 데이터를 넣는 작업
- 각 모델 별 CRUD 작업 : (아직 익숙하지 않으므로) app.js 혹은 main.js에 한번에 작업
이후 모델 별 route 분리 작업 : route 폴더에 모델별로 1차 분기
→ xx_route .js에는 서버 요청만 남기고 xx_controller.js 에 실행 함수 분리. - 유효성 검사 적용 : 공용으로 제작 후, 각 모델별로 분리하여 xx_controller.js에 적용
- 비동기 에러 래퍼 함수 적용 : 서버 다운을 막기 위하여 각 xx_route.js에 적용
- 미들웨어 적용 작업 : 공용 / 각 모델별 미들웨어를 각각 분리해서 app.js / xx_route.js 에 각각 적용
- 작업을 진행하며 xx.http를 이용해 지속적인 검토 진행
- 데이터 모델링 수정이 필요한 경우, 작업 후 꼭 마이그레이션 진행
1-1. 관계형 모델 CRUD 작업
1. 관계형 모델에서 사용 할 요청작업(with URL) 정리
1) Comment 모델에서 작업
| 요청 | URL | 기능 | 부가설명 |
| GET | /comments | 전체 댓글 조회 | where + category 쿼리 스트링 조합으로 product / article 모아보기 제공 |
| /comments/:id | 세부 댓글 조회 | where + id 조합으로 세부 댓글 보기 | |
| PATCH | /comments/:id | 댓글 조회 페이지에서 수정 | comment의 content만 수정 가능 |
| DELETE | /comments/:id | 댓글 조회 페이지에서 삭제 | 연결 된 product / article 이 아닌 현재 댓글만 삭제 |
2) Product 모델에서 작업
- Product 상세페이지에서 요청 작업 진행
| 요청 | URL | prisma method | 기능 |
| GET | /products/:id/comments | .findUnique() | product 상세페이지의 댓글 확인 |
| POST | comment.create() product.update() |
product 상세페이지의 댓글 신규 작성 (이미 있는 상품에 댓글을 넣는 작업이므로 생성 후 업데이트 작업) |
|
| PATCH | /products/:id/comments/:id | comment.update() | product 상세페이지의 댓글 수정 |
| DELETE | comment.delete() | product 상세페이지의 댓글 삭제 |
3) Article 모델에서 작업
- Article 상세페이지에서 요청 작업 진행
| 요청 | URL | prisma method | 기능 |
| GET | /articles/:id/comments | .findUnique() | article 상세페이지의 댓글 확인 |
| POST | comment.create() article.update() |
article 상세페이지의 댓글 신규 작성 (이미 있는 글에 댓글을 넣는 작업이므로 생성 후 업데이트 작업) |
|
| PATCH | /articles/:id/comments/:id | comment.update() | article 상세페이지의 댓글 수정 |
| DELETE | comment.delete() | article 상세페이지의 댓글 삭제 |
2. 모델 별 CRUD 작업 - 작업 별 간략 특징
- Comment 모델 : "/comments" - GET , "/comments/:id" - GET, PATCH, DELETE 요청 작성
- GET : cursor 방식의 페이지 네이션 기능
→ 끊김없이 데이터가 나열되는 페이징 기능으로, 최신 글이 맨 위로 오도록 셋팅
- GET : cursor 방식의 페이지 네이션 기능
- Product & Article 모델 : 기존 라우터에서 새로운 "/comments", "/comments/:id" 라우터를 추가 후 각각 요청 내용 작성
- POST : Comment DB 에 데이터를 생성 한 뒤, 각각의 모델에 연결(update & connect)하는 방식 사용
1-2. 개별 모델 GET 요청 추가 작업
추가 할 내용
- 특정 필드의 검색 기능 추가
- Product Route : name / description 필드에서 검색
- Article Route : title / content 필드에서 검색
- 조회 시 보여 줄 항목 정리
- Product Response : id, name, price, createdAt 표기
- Article Response : id, title, content, createdAt 표기
- Comment Response : id, content, createdAt 표기
[기존 GET 요청 작업] - 대표 product model
productRoute
.route('/')
.get(async (req, res) => {
const { offset = 0, limit = 10, order = 'newest' } = req.query;
let orderBy;
switch (order) {
case 'oldest':
orderBy = { createdAt: 'asc' };
break;
case 'newest':
orderBy = { createdAt: 'desc' };
break;
default:
orderBy = { createdAt: 'asc' };
}
const productList = await prisma.product.findMany({
skip: parseInt(offset),
take: parseInt(limit),
orderBy,
});
res.status(200).send(productList);
})
[ 수정 한 GET 요청 작업 ] - 대표 product model
productRoute
.route('/')
.get(async (req, res) => {
// 1. 받아오는 데이터에 name / description 키워드를 추가
const {offset = 0, limit = 10, order = 'newest', name = '', description = ''} = req.query;
let orderBy;
switch (order) {
case 'oldest':
orderBy = { createdAt: 'asc' };
break;
case 'newest':
orderBy = { createdAt: 'desc' };
break;
default:
orderBy = { createdAt: 'asc' };
}
const productList = await prisma.product.findMany({
// 2. 해당 데이터를 찾기 위하여 where 프로퍼티를 추가하고 각각 키워드 전달
where: {
name: { contains: name },
description: { contains: description },
},
skip: parseInt(offset),
take: parseInt(limit),
orderBy,
// 3. 최종으로 response 할 정보만 설정
select: {
id: true,
name: true,
price: true,
createdAt: true,
},
});
res.status(200).send(productList);
})
+ 추가 관계형 모델 스키마 작성시 함께 사용하는 옵션 onDelete
model Comment {
...
product Product? @relation(fields: [productId], references: [id], onDelete: Cascade)
...
}
- 기본 내용 : 관계형 모델 속성인 @relation 안에 사용하는 옵션으로,
연결된 값이 삭제되는 경우 현재 모델의 데이터를 어떻게 할지 정의
1) Cascade : 연결 된 값이 삭제되면, 현재 모델의 데이터를 같이 삭제
예시 - 블로거 계정이 삭제되면 작성한 포스팅도 모두 사라짐
2 ) Restrict : 연결 된 값을 삭제하고 싶으면, 현재 모델의 데이터 먼저 삭제하고 연결 된 값을 삭제 가능
예시 - 게시글에 댓글이 달린 상태에서는 게시글을 삭제할 수 없음 (댓글을 먼저 삭제해야 함)
3) SetNull : 연결 된 값이 삭제되면, 해당 값을 가리키던 필드가 Null로 변경, @relation 필드와 FK 필드 모두 옵셔널(?) 설정 필수
예시 - 쇼핑몰의 유저가 탈퇴해도, 주문 목록은 유지(주문자 명은 없음)
4) SetDefault : 연결 된 값이 삭제되면, 해당 값을 가리키던 필드에 디폴트 값 넣어줌, FK에 @default("...") 셋팅 필요
예시 - 댓글을 남긴 작성자의 계정이 삭제되도 댓글은 남고 이름에 (탈퇴 한 유저) 라고 나오는 경우
2. 유효성 검사 적용
1. 유효성 데이터 검사를 진행 할 필드 구분

2. 유효성 검사용 파일에 필요한 셋팅 진행하기 ( 샘플 : Product Model )
[ productStruct.js ]
import * as structs from 'superstruct';
3. 파일 안에 유효성 검사를 진행하는 내용 작성하기
Patch용 유효성 검사의 경우 Creat검사용에서 옵셔널하게 검사 가능하므로 partial() 메소드를 사용해 작성
[ productStruct.js ]
import * as structs from 'superstruct';
// POST용 유효성 검사
export const CreateProduct = s.object({
name: s.size(s.string(), 1, 30),
description: s.size(s.string(), 1, 500),
price: s.min(s.number(), 0),
tags: s.array(s.string()),
});
// PATCH용 유효성 검사
export const PatchProduct = structs.partial(createProduct);
4. 유효성 검사를 진행 할 파일 안에 Superstruct의 assert함수와 유효성 검사 이름을 import로 불러오고,
assert 함수를 사용하여 필요한 부분에서 검사 진행
[ productRoute.js]
...
import { assert } from 'superstruct';
import { CreateProduct, PatchProduct } from '../structs/productStructs';
...
// POST요청에 적용 한 내용
app.post('/products', async (req, res) => {
assert(req.body, CreateProduct);
...
});
// PATCH요청에 적용 한 내용
app.patch('/products/:id', async (req, res) => {
assert(req.body, PatchProduct);
...
});
5. 나머지 다른 모델에도 모두 적용
+ 추가 작업 시 참고했던 SuperStructJS 홈페이지 : https://docs.superstructjs.org/
Introduction | Superstruct
CopyIntroduction Superstruct makes it easy to define interfaces and then validate JavaScript data against them. Its type annotation API was inspired by TypeScript, Flow, Go, and GraphQL, giving it a familiar and easy to understand API. But Superstruct is d
docs.superstructjs.org
📃 내일은 뭘 배울까 🤔
- route 파일 2차 분기 ⇒ controll 파일로 작업 영역 이동
- 비동기 에러 래퍼 함수 적용
- 미들웨어 적용 작업
+ 가능하다면 "이미지(multer) 작업" 진행
'코드잇 Node.js(BE) 부트 캠프 > TIL (Today I Learn) 📑' 카테고리의 다른 글
| [ TIL ] Day 28 - 팀 초급 프로젝트(실습) : 선정 한 프로젝트 스키마 정의 & 작업 영역 분배 & 기타 사항 정리 (1) | 2025.11.04 |
|---|---|
| [ TIL ] Day 27 - 실습 : 비동기 에러 래퍼 함수 / 미들웨어 적용 / 최종 배포 (0) | 2025.11.03 |
| [ TIL ] Day 24 - 실습 : 데이터 시딩 작업 후 개별 모델 CRUD 작성 + Route 분리 (1) | 2025.10.29 |
| [ TIL ] Day 23 - 실습 개발 환경 셋팅하기 (Git, Express, 전체 구조) (0) | 2025.10.28 |
| [ TIL ] Day 22 - Express 미들웨어 / 라우트 / Multer(파일 업로드) (0) | 2025.10.27 |