본문 바로가기
내일배움 과제/CH3 아이템 박스 시뮬레이터

3.7부터 Sign up/Log in

by GREEN나무 2024. 11. 27.
728x90

 

DB 만들기

더보기
더보기

1. 폴더 만들고 라이브러리 설치하기

bash 

# 프로젝트를 초기화합니다.
yarn init -y
#생성된 package.json 파일에서 ES6 문법을 사용하기 위해 "type":"module" 추가

# 라이브러리를 설치합니다.
yarn add express prisma @prisma/client cookie-parser jsonwebtoken

# nodemon 라이브러리를 DevDependency로 설치합니다.
yarn add -D nodemon

# 설치한 Prisma를 초기화 하여, Prisma를 사용할 수 있는 구조를 생성합니다.
npx prisma init

2. 'src' 폴더를 만들고 'app.js' 파일 생성

 

3. /prisma/ schema.prisma에 db provider을 "mysql"로 바꾸고 테이블을 만듧니다.

 

코드

// prisma/schema.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model Users {
  userId    Int      @id @default(autoincrement()) @map("userId")
  email     String   @unique @map("email")
  password  String   @map("password")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  @@map("Users")
}

//  @db.Text : string 타입이지만 더 많은 데이터를 가질 수 있음
// @map : 해당 데이터를 db에서도 동일하게 사용하기 위함
model Posts {
  postId    Int      @id @default(autoincrement()) @map("postId")
  title     String   @map("title")
  content   String   @map("content") @db.Text
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  @@map("Posts")
}

// Int? : 타입 뒤에 ? 붙이면 입력받지 않아도 된다는 뜻.
model UserInfos {
  userInfoId   Int      @id @default(autoincrement()) @map("userInfoId")
  name         String   @map("name")
  age          Int?     @map("age")
  gender       String   @map("gender")
  profileImage String?  @map("profileImage")
  createdAt    DateTime @default(now()) @map("createdAt")
  updatedAt    DateTime @updatedAt @map("updatedAt")

  @@map("UserInfos")
}

model Comments {
  commentId Int      @id @default(autoincrement()) @map("commentId")
  content   String   @map("content")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  @@map("Comments")
}

// annotation의 map을 이용해서 DB에소도 해당 컬럼을 사용할 수 있게 합니다.

// typscript나 java나 python 등에서 @를 코드 위에 작성하는 경우가 있습니다.
// 이런식으로 특정한 것 위에다 @를 쓰는 것을 annotation이나 decorator라고 부릅니다.

 

테이블 간의 관계

 

1 : 1

 

관계를 설정하려는 모델(UserInfos)에서 어떤 모델과 관계를 맺을지(Users) 설정해야합니다.

       user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
관계를 맺게되는 모델(Users)에서 어떤 모델이 관계를 맺는지(UserInfos) 설정해야합니다. (1명에게만 종속)

     userId Int @unique @map("userId")
관계를 맺게되는 모델(Users)에서, 타입을 지정할 때, Optional Parameter(?)를 지정해줘야합니다.

    userInfos UserInfos?

사용자는 사용자 정보가 존재하지 않을 수 있기 때문입니다.(유저 생성 후 info 생성 전) 

// schema.prisma 

model Users {
  userId    Int      @id @default(autoincrement()) @map("userId")
  email     String   @unique @map("email")
  password  String   @map("password")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.

  @@map("Users")
}


model UserInfos {
  userInfoId   Int      @id @default(autoincrement()) @map("userInfoId")
  userId       Int      @unique @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
  name         String   @map("name")
  age          Int?     @map("age")
  gender       String   @map("gender")
  profileImage String?  @map("profileImage")
  createdAt    DateTime @default(now()) @map("createdAt")
  updatedAt    DateTime @updatedAt @map("updatedAt")

  // Users 테이블과 관계를 설정합니다.
  user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@map("UserInfos")
}

fields
사용자 정보(UserInfos) 모델에서 사용할 외래키(Forien Key) 컬럼을 지정합니다.
여기선, userId 컬럼으로 외래키를 지정하였습니다.
references
key: 참조하는 다른 모델의 Column를 지정합니다.
여기선, 사용자(Users) 모델의 userId 컬럼을 참조합니다.
onDelete | onUpdate
참조하는 모델이 삭제 or 수정될 경우 어떤 행위를 할 지 설정합니다.
Cascade 옵션을 선택하여 사용자가 삭제될 경우 그에 연결된 사용자 정보도 함께 삭제되도록 설정하였습니다.

 

1: N

// schema.prisma

model Users {
  userId    Int      @id @default(autoincrement()) @map("userId")
  email     String   @unique @map("email")
  password  String   @map("password")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
  posts     Posts[] // 사용자(Users) 테이블과 게시글(Posts) 테이블이 1:N 관계를 맺습니다.

  @@map("Users")
}

model Posts {
  postId    Int      @id @default(autoincrement()) @map("postId")
  userId    Int      @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
  title     String   @map("title")
  content   String   @map("content") @db.Text
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  // Users 테이블과 관계를 설정합니다.
  user     Users     @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@map("Posts")
}

 

관계를 맺게되는 모델(Users)에서, 타입을 지정할 때, 배열 연산자([])를 작성해줘야합니다.
사용자는, 여러개의 게시글을 가질 수 있습니다.

탈퇴할 경우

// Users 테이블과 관계를 설정합니다.
user     Users     @relation(fields: [userId], references: [userId], onDelete: Cascade)

현재 게시글 모델의 경우 작성한 사용자가 회원 탈퇴(onDelete)하게 될 경우 작성한 모든 게시글이 삭제되도록 구현되어 있습니다. 이런 설정은 @relation 어노테이션을 사용하여 지정합니다.​
여기서 User는 게시글(Posts)이 참조하는 다른 모델을 지정하고, fields는 게시글(Posts) 모델에서 사용할 외래키 컬럼을 지정합니다. references는 참조하는 다른 모델의 컬럼을 지정하고, onDelete는 잠조하는 모델이 삭제될 경우 어떤 행위를 할 지 설정합니다.
onDelete의 경우, Cascade 옵션으로 사용자가 삭제될 경우 연관된 게시글 또한 삭제되도록 설정하였습니다.

 

// prisma/schema.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model Users {
  userId    Int      @id @default(autoincrement()) @map("userId")
  email     String   @unique @map("email")
  password  String   @map("password")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
  posts     Posts[] // 사용자(Users) 테이블과 게시글(Posts) 테이블이 1:N 관계를 맺습니다.
  comments  Comments[] // 사용자(Users) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.

  @@map("Users")
}

// Int? : 타입 뒤에 ? 붙이면 입력받지 않아도 된다는 뜻.
// Cascade : 사용자 삭제 시 info도 함께 삭제됨
model UserInfos {
  userInfoId   Int      @id @default(autoincrement()) @map("userInfoId")
  userId       Int      @unique @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
  name         String   @map("name")
  age          Int?     @map("age")
  gender       String   @map("gender")
  profileImage String?  @map("profileImage")
  createdAt    DateTime @default(now()) @map("createdAt")
  updatedAt    DateTime @updatedAt @map("updatedAt")

  // Users 테이블과 관계를 설정합니다.
  user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@map("UserInfos")
}

//  @db.Text : string 타입이지만 더 많은 데이터를 가질 수 있음
// @map : 해당 데이터를 db에서도 동일하게 사용하기 위함

model Posts {
  postId    Int      @id @default(autoincrement()) @map("postId")
  userId    Int      @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
  title     String   @map("title")
  content   String   @map("content") @db.Text
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  // Users 테이블과 관계를 설정합니다.
  user     Users      @relation(fields: [userId], references: [userId], onDelete: Cascade)
  comments Comments[] // 게시글(Posts) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.

  @@map("Posts")
}

model Comments {
  commentId Int      @id @default(autoincrement()) @map("commentId")
  postId    Int      @map("postId") // 게시글(Posts) 테이블을 참조하는 외래키
  userId    Int      @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
  content   String   @map("content")
  createdAt DateTime @default(now()) @map("createdAt")
  updatedAt DateTime @updatedAt @map("updatedAt")

  // Posts 테이블과 관계를 설정합니다.
  post Posts @relation(fields: [postId], references: [postId], onDelete: Cascade)
  // Users 테이블과 관계를 설정합니다.
  user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@map("Comments")
}

// annotation의 map을 이용해서 DB에소도 해당 컬럼을 사용할 수 있게 합니다.

// typscript나 java나 python 등에서 @를 코드 위에 작성하는 경우가 있습니다.
// 이런식으로 특정한 것 위에다 @를 쓰는 것을 annotation이나 decorator라고 부릅니다.

 

4. Prisma DB, Table 생성하기

 

.env파일을 수정하세요

# .env
# DATABASE_URL="mysql://root:aaaa4321@엔드포인트:3306/community_hub"
DATABASE_URL="mysql://root:aaaa4321@express-database.cvyiaeceu603.ap-northeast-2.rds.amazonaws.com:3306/community_hub"

 

터미널에서

# 해당 프로젝트에 schema.prisma에 정의된 테이블을 MySQL에 생성합니다.
npx prisma db push

생성됨

 

여러 제약조건이 있을 때 Uniqe가 우선해서 보입니다.

 

Comments

Posts

 

UserInfos

 

명세서 : https://teamsparta.notion.site/f0d79caca13145abb20b68f96c17d146?v=eb65c164a9b743bc872f3290d1860e37&pvs=4


 

1) 인증(Authentication)
인증(Authentication)은 일반적인 사이트의 로그인 기능에 해당합니다.
참고 - OAuth : 오어스   (구글, 카톡 등의)플렛폼을 통한 로그인

2) 인가(Authorization)
인가(Authorization)는 이미 인증된 사용자가 특정 리소스에 접근하거나 특정 작업을 수행할 수 있는 권한이 있는지를 검증하는 작업
인가(Authorization)기능은 사용자 인증 미들웨어를 통해서 구현할 예정입니다.


로그인, 회원가입

Prisma 게시판 프로젝트 API 명세서 :

https://teamsparta.notion.site/e226b48376d5430da163ec5a9a08ec98?v=3b302a655f85456ea3ec38d288cef440

 

1. 프로젝트 초기화

모든 Router에서 Prisma를 사용할 수 있도록 '프로젝트폴더\src\utils\prisma\index.js ' 파일 만들기

// src/app.js

import express from 'express';
import cookieParser from 'cookie-parser';

const app = express();
const PORT = 3018;

app.use(express.json());
app.use(cookieParser());

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});
// src/utils/prisma/index.js

import { PrismaClient } from '@prisma/client';

export const prisma = new PrismaClient({
  // Prisma를 이용해 데이터베이스를 접근할 때, SQL을 출력해줍니다.
  log: ['query', 'info', 'warn', 'error'],

  // 에러 메시지를 평문이 아닌, 개발자가 읽기 쉬운 형태로 출력해줍니다.
  errorFormat: 'pretty',
}); // PrismaClient 인스턴스를 생성합니다.

 

2. 회원가입 API

[게시판 프로젝트] 회원가입 API 비즈니스 로직

1. email, password, name, age, gender, profileImage를 body로 전달받습니다.
2. 동일한 email을 가진 사용자가 있는지 확인합니다.
3. Users 테이블에 email, password를 이용해 사용자를 생성합니다.
4, UserInfos 테이블에 name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.

 

회원가입 API는 사용자와 사용자 정보가 1:1 관계를 가진 것을 바탕으로 비즈니스 로직이 구현됩니다.

사용자 정보는 사용자가 존재하지 않을 경우 생성될 수 없으므로, 전달받은 인자값을 이용해 사용자 → 사용자 정보 순서대로 회원가입을 진행해야합니다.
그러면 routes/users.router.js 파일을 생성하고, UsersRouter를 app.js전역 미들웨어에 등록 후 회원가입 API를 구현 하겠습니다

 

// app.js

import express from "express";
import cookieParser from "cookie-parser";
import UsersRouter from "./routes/users.router.js"; //

const app = express();
const PORT = 3018;

app.use(express.json());
app.use(cookieParser());
app.use("/api", [UsersRouter]); //

app.listen(PORT, () => {
  console.log(PORT, "포트로 서버가 열렸어요!");
});

 

// src/routes/users.router.js

import express from "express";
import { prisma } from "../utils/prisma/index.js";

const router = express.Router();

// [게시판 프로젝트] 회원가입 API 비즈니스 로직
// 1. email, password, name, age, gender, profileImage를 body로 전달받습니다.
// 2. 동일한 email을 가진 사용자가 있는지 확인합니다.
// 3. Users 테이블에 email, password를 이용해 사용자를 생성합니다.
// 4. UserInfos 테이블에 name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.

/** 사용자 회원가입 API **/
router.post("/sign-up", async (req, res, next) => {
  // 1. email, password, name, age, gender, profileImage를 body로 전달받습니다.
  const { email, password, name, age, gender, profileImage } = req.body;
  // 2. 동일한 email을 가진 사용자가 있는지 확인합니다.
  const isExistUser = await prisma.users.findFirst({
    where: {
      email,
    },
  });

  if (isExistUser) {
    // 이미 있는 이메일 사용
    return res.status(409).json({ message: "이미 존재하는 이메일입니다." });
  }

  // 3. Users 테이블에 email, password를 이용해 사용자를 생성합니다.
  const user = await prisma.users.create({
    data: { email, password },
  });

  // 4. UserInfos 테이블에 name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.
  const userInfo = await prisma.userInfos.create({
    data: {
      userId: user.userId, // 여기 'user'는 3.에서 const user한 변수. 생성한 유저의 userId를 바탕으로 사용자 정보를 생성합니다.
      name,
      age,
      gender: gender.toUpperCase(), // 성별을 대문자로 변환합니다.
      profileImage,
    },
  });

  return res.status(201).json({ message: "회원가입이 완료되었습니다." });
});

export default router;

 

터미널에서 

npx prisma db push

 

 

실행하기

npx nodemon src/app.js


 

 



 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'내일배움 과제 > CH3 아이템 박스 시뮬레이터' 카테고리의 다른 글

참고 자료  (0) 2024.11.28
DB  (0) 2024.11.26