김데이의 개발공부

[ TIL ] Day 21 - 관계형 데이터베이스 / Postgres 사용하기 2 본문

코드잇 Node.js(BE) 부트 캠프/TIL (Today I Learn) 📑

[ TIL ] Day 21 - 관계형 데이터베이스 / Postgres 사용하기 2

theday365 2025. 10. 24. 18:46
반응형

🗓️ 수업 일자 : 2025.10.24

✨ 오늘의 수업 평가 :  [ GOOD ]   오늘도 한 걸음 성장! 🌱✨🌙

 

최종 Git Repo : https://github.com/KimDay366/codeit-nodejs-6th-comazon

 

GitHub - KimDay366/codeit-nodejs-6th-comazon: codeit-nodejs-6th, Comazon project Repo

codeit-nodejs-6th, Comazon project Repo. Contribute to KimDay366/codeit-nodejs-6th-comazon development by creating an account on GitHub.

github.com

 

코드를 보면 주석 범벅이라 내용은 진짜 정신 없지만,  

그래도 강사님 코드 안보고 작성 해 보고, 에러 날 때마다 혼자 해결했다는게 참 뿌듯한 날이다 😉👍

 

 

📝  오늘 배운 내용  
- 여러 OMR 기능 / 문법 익히기 2

- 모델 간 관계 정립 @relation

- 지금까지 배운 ORM / Prisma 개념 한 줄 정리

 


1. 여러 ORM 기능/문법 익히기 2

Prisma에서 Transaction 

  • 서버에 작업을 요청하는 도중 서버에 문제가 생겨 일부 쿼리만 실행되는 경우에 대한 문제 방지 
    예시 ) [ 주문 진행 = 재고 확인 → 재고 수정 → 주문 생성 ] 과정에서 서버 다운이 되면서 재고 수정만 되고 주문이 생성되지 않으면 재고에 대한 문제가 생김. 
  • 여러 쿼리(실제 데이터를 가져오는 메소드)를 $transaction 안에 묶어서 모든 작업이 성공하지 않으면 롤백하여 모든 작업이 실패 하도록 설정
  • 사용 예제
[ Transaction 없이 사용 한 경우 ]

const queries = productIds.map((id) => { ... }); // 재고를 수정하는 코드

const orderCreat = await prisma.order.create({ ... }); // 오더를 등록하는 코드

queries가 실행 된 뒤, 모종의 이유로 서버 연결이 중단되면 orderCreat가 실행되지 않음



[ Transaction으로 처리 한 경우 ]

const queries = productIds.map((id) => { ... }); // 재고를 수정하는 코드

const [ orderCreat ] = await prisma.$transaction([
	prisma.order.create({ 
    ... // 오더를 등록하는 코드
    }),
    ... queries // 수정된 재고 값
]); 

[ orderCreat ] 안에 오더 등록 / 재고 모두 담겨있음
해당 구문을 진행하는 도중 서버가 중단되면, 모든 구문이 정지되어 일관성을 유지

 

 

 

비동기 라우트 핸들러용 에러 래퍼 함수 ( Async Route Handler Wrapper)
혹은 비동기 에러 처리용 고차함수 (Higher-Order Function for Async Error Handling)

또는 에러 래퍼 함수, 비동기 에러 래퍼 함수 등등.. with asyncHandler(){}

  • 에러가 발생하는 경우 서버가 다운되지 않고 유지하면서 에러를 내보낼 수 있게 셋팅
  • 함수를 인자로 받고, 새로운 async 함수를 다시 돌려줌
  • 모든 라우터 핸들러에 설정할 수 있는 고차함수로 구성
  • catch문 안에 if문으로 에러를 하나하나 찾아내 확인 할 수 있음
function asyncHandler(handler) {
  return async function (req, res) {
    try {
      // 실행 하려는 HTTP 작업이 실행되는 공간
      await handler(req, res);
    } catch (e) {
      console.error(e);
      // if 구문으로 에러 값을 적어서 여러 에러를 처리 가능!
      if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025') {
        res.sendStatus(404);
      } else if (e.name === 'StructError') {
        res.status(400).send({ message: e.message });
      } else {
        res.status(500).send({ message: e.mossage });
      }
    }
  };
}

...

app.post('/user', asyncHandler( asycn (req,res) => {
     ... asyncHandler에 감싸진 상태로 요청 코드 작성 ...
  }), 
);

 

+ Prisma Error 관련 링크 : https://www.prisma.io/docs/orm/reference/error-reference

 

Errors | Prisma Documentation

Prisma Client, Migrate, Introspection error message reference

www.prisma.io

 

 

배포하기 

  1. PostgreSQL Database 생성 
    • render.com 홈페이지에서 New > Progres 클릭하여 신규 DB 생성
  2. Web Service 셋팅 ( build commend 참고 사항 )
    • npm install --production : 배포용 패키지 설치
    • npx prisma migrate deploy : 배포 할 때 가장 최신 버전의 마이그레이션으로 DB에 정보를 생성
      (npx prisma migrate dev : 개발 작업 시 data model이 바뀔때마다 실시간으로 DB를 생성)
    • npx prisma generate : prisma client 설치
      ⇒ Build Command에 한번에 입력
      : npm install --production && npx prisma migrate deploy && npx prisma generate

 

 

 

 

2. 모델 간 관계 정립 @relation (어제 배운거 포함)

0) 관계 정립 기본 문법

[ data model 영역]
model User {
  order   Order[]
}

model Order {
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId       String   @unique
}

위 코드 내용은 문법에 맞지 않습니다. 다양한 내용을 보여주기 위해 샘플로 작성하였습니다 :) 

  1. 참조를 사용 할 모델(=model Order)에서 @relation 하기
    user                     User                        @relation(fields: [userId], references: [id], onDelete: Cascade)
    사용 할 값 이름  |  참조 할 model이름  | @relation( fields, references, onDelete(optinal) )
    - fields: [ 현재 모델에서 사용할 필드 이름 ]
    - references: [ 참조 할  필드 이름 ]
    - onDelete: 참조 데이터가 삭제될 때 설정

  2. 참조를 사용 할 모델(=model Order)에서 불러온 값 정의하기
    userId                   String                     @unique
    @relation에서 작성한 fields 값     |     해당 값의 속성     |  1:1 매칭이면 @unique 사용

  3. 참조를 보낸 모델(=model User)에서 연결되는 필드값 정리하기 
    orders                    Order[]                     (속성 x) 
    참조 보낼 필드값 이름    |  해당 모델명을 작성하며 어떻게 받을지도 결정
                                            (단일 - 로우 받음, [ ] - 해당 모델들의 로우를 배열로 받음)

- model Order에서 @relation을 통해 생성된 두 Data 필드의 차이 
  1) user : 데이터가 오고가는 통로, 별도의 값이 존재하지 않음

  2) userId : 통로를 통해 만들어진 실제로 사용되는 값 
  + orders : 데이터가 오고 가는 통로, 속성값을 보면 한 개가 아닌 여러 개의 로우 데이터를 받을 수 있음

 

 

1) ||--|| : 1:1 관계, One and only one

[ data model 영역]
model User {
  id               String            @id @default(uuid())
  ...
  userPreference   UserPreference?
}

model UserPreference {
  ...
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId       String   @unique
  ...
}

[ 작성 조건 ]
- model User의 ID정보를 model Userpreference에서 사용 
- model Userpreference는 model User이 생길 때 함께 생기는 "마이페이지" 데이터    두 모델이 1:1 단일 매칭
  
[ model User의 입장 ]
model User에서 참조하는 "UserPreference"는 "UserPreference"에 속해 있는 값 중 하나가 아니라,
"UserPreference의 로우 1줄" 전체임

[ model UserPreference 의 입장 ]
참조하여 가지고 온 UserId의 경우 1 : 1 관계로 참조하기 때문에 @unique를 써야 함
(
만약에 다른 경우로 쓴다면 @unique를 쓰면 안됨!)


 

2) ||--o{ : 1:n 관계, Zero or many

[ data model 영역]
model User {
  id       String          @id @default(uuid())
  ...
  posts    Post[]
}

model Post {
  ...
  user         User       @relation(fields: [userId], references: [id], onDelete: SetNull)
  userId       String?   
  ...
}

[ 작성 조건 ]
- model User의 ID정보를 model Post에서 사용 
- 하나의 User는 여러 Post를 작성할 수 있음 ⇒ model User와 model Post이 1: 다수 매칭인 경우
  
[ model User의 입장 ]
model User에서 참조하는 Post는 하나가 아니므로 배열로 선언

[ model Post의 입장 ]
참조하여 가지고 온 userId의 경우 Post 관점에서는 1 : 1 관계로 보이지만, 
User 입장에서 볼땐 Post가 여러개이므로, UserID의 속성에 @unique를 쓰면 안됨

 

 

 

3) }o--o{ : n:m 관계, Many to many

[ data model 영역]
model User {
  id           String          @id @default(uuid())
  ...
  joingroups   Group[]
}

model Group {
  ...
  members       User[]   
  ...
}

[ 작성 조건 ]
- model User 는 하나의 사람이고, model Group은 하나의 소규모 그룹임 
- 하나의 User는 여러 Group에 가입할 수 있고, Group 역시 여러 사람을 데리고 있을 수 있음
  ⇒ model User와 model Group이 다수 : 다수 매칭인 경우
  
[ model User의 입장 ]
model User에서 참조하는 Group는 하나가 아니므로 배열로 선언

[ model Group의 입장 ]
model Group에서 참조하는 User는 하나가 아니므로 배열로 선언

 

[ 특이사항 ]

작업이 끝난 뒤 마이그레이션을 진행하면 양쪽 테이블을 이어주는 임의의 중간 테이블이 자동으로 생김

N:M 관계의 경우, 마이그레이션을 진행하면 자동으로 중간 테이블이 생성
N:M 관계의 경우, 마이그레이션을 진행하면 자동으로 중간 테이블이 생성

 

 

 

3. 지금까지 배운 ORM / Prisma 개념 한 줄 정리 

  1. 작업 관련
    • seeding(시딩) : 작업에 필요한 초기 데이터를 서버에 넣는 과정
    • @relation : 각 데이터 모델간의 관계를 정립할 때 사용하는 명령어
    • 스키마 : 데이터베이스의 model / 값 이름 및 속성 / 관계 / 관련 카테고리들을 정의하는 것
    • 마이그레이션 : 실제 데이터 베이스에 작성한 Data Model을 저장 하는 것
  2. 에러 처리
    • asyncHandler( ) : 에러가 생겨도 서버를 다운 시키지 않는 것
    • assert( ) & Superstruct : 입력할 데이터 자체에 대한 검증
    • $transaction([ ]) : 서버가 중간에 중단되면, 작업한 내용을 모두 롤백 시켜서 데이터가 꼬이는 것을 방지
  3. 배포 진행 
    • 환경 설정 : 배포 진행 할 때, 공개되면 안되는 정보 혹은 배포에 필요한 정보에 대해 설정하는 것
    • Build Command : 배포 작업에 사용 될 명령어 모음, 배포용 패키지 / 마이그레이션 작업 / 클라이언트 선언 등 
      Start Command : 배포 후 실행되는 첫 명령어, html로 치면 index.html을 지정 해 주는 것
  4. 기타 개념
    • Cardinality : 데이터 모델간의 관계에 대한 이론
    • UUID : 완벽하게 고유 문자는 아니지만, 중복 가능성이 거의 없는 ID
    • enum : 데이터에 사용하는 값이 특정 카테고리일 경우, enum을 활용하여 카테고리 생성 & 정의

 

📃 내일은 뭘 배울까 🤔

- 미들 웨어와 파일 관리

- 밀린 LMS 강의 듣기(제발 좀 밀리지 마🙏)

반응형