개발일지

node.js, express, prisma 사용하기 본문

Backend

node.js, express, prisma 사용하기

wa_n 2023. 8. 10. 11:47
728x90
반응형

원티드 온보딩 인턴십 사전과제를 만들면서 express에서 prisma를 사용해보려고 한다.

orm 언어 중에 typeorm을 사용해 봤다 다른 orm을 처음 써봐서 헷갈리는 부분과 express도 만들어 보는 것도

처음이라 정리해놓으려고 한다. 내가 이해할 수 있게 정리해 봐야겠다

설치 명령어

express와 prisma

//express

yarn init-y  //프로젝트 처음 시작하다면 package.json 을 설치한다  

yarn add express   //node_mosules 안에 Express.js를 설치하고, package.json 파일에 Express.js를 종속성으로 추가합니다.
  1. Prisma CLI 설치
    • 명령어: yarn add @prisma/cli --save-dev
    • 설명: Prisma CLI를 개발 의존성으로 설치합니다. 이 CLI는 데이터베이스 마이그레이션, 스키마 관리 등의 작업에 필요한 도구를 제공합니다.
  2. Prisma 초기화
    • 명령어: npx prisma init
    • 설명: 프로젝트에서 Prisma를 사용하기 위한 초기 설정을 합니다. 이 명령어를 실행하면 prisma 디렉터리와 그 안에 schema.prisma 파일이 생성됩니다.
  • Prisma 초기화 이유 npx prisma init 명령어를 실행하면, Prisma는 프로젝트에 prisma 디렉터리와 prisma/. env 파일을 생성합니다.
    • prisma 디렉터리는 Prisma 스키마 파일(schema.prisma)을 포함합니다. 이 파일은 Prisma의 데이터 모델을 정의하고, 데이터베이스 연결을 설정하는 곳입니다. 여기에서는 데이터베이스에 있는 각 테이블을 나타내는 Prisma 모델을 정의하고, 데이터베이스 프로바이더와 연결 설정을 지정합니다.
    • prisma/. env 파일은 환경 변수를 저장하는 곳입니다. 이 파일에는 데이터베이스 연결 문자열 같은 민감한 정보를 저장하며, 이 정보는 schema.prisma 파일에서 참조됩니다.
    따라서, Prisma를 초기화하는 과정은 프로젝트에 Prisma를 설정하고, Prisma를 통해 데이터베이스와 효과적으로 상호 작용하는 데 필요한 구성을 정의하는 데 필요합니다.
  • Prisma를 초기화하는 주요 이유는 프로젝트에 Prisma의 구성 요소를 설정하고, Prisma가 데이터베이스와 상호 작용하는 방식을 정의하기 위함입니다.
  1. 데이터베이스 연결
    • 설명: schema.prisma 파일의 datasource 부분에서 데이터베이스 연결 정보를 설정합니다.
  2. Prisma 모델 정의
    • 설명: schema.prisma 파일에 데이터베이스 테이블과 매핑될 모델을 정의합니다.
  3. 데이터베이스 마이그레이션
    • 명령어:
      • npx prisma migrate dev --name init (개발 모드)
      • npx prisma migrate deploy (배포 모드)
    • 설명: Prisma 모델의 변경 사항을 데이터베이스에 적용합니다.
  4. Prisma Client 생성
    • 명령어: npx prisma generate
    • 설명: Prisma 모델을 기반으로 Prisma Client를 생성합니다. 이 Client는 데이터베이스에 타입 세이프한 쿼리를 수행하는 데 사용됩니다.
  5. Prisma Client 사용
    • 설명: Express 애플리케이션 코드 내에서 Prisma Client를 import 하여 데이터베이스 쿼리를 실행합니다.
  • Prisma Client를 설치 이유 Prisma Client는 자동 생성된 타입 안전한 데이터베이스 클라이언트입니다. 이를 설치하는 이유는 다음과 같습니다:
    1. 데이터베이스 작업의 단순화: Prisma Client는 SQL 또는 다른 쿼리 언어에 익숙하지 않은 개발자들도 데이터베이스 작업을 쉽게 수행할 수 있게 합니다. 간단하고 직관적인 API를 제공하며, 복잡한 쿼리를 작성할 필요 없이 데이터를 생성, 읽기, 업데이트, 삭제(CRUD)할 수 있습니다.
    2. 타입 안전성: Prisma Client는 Prisma 스키마에 기반하여 자동으로 TypeScript 타입을 생성합니다. 이를 통해 개발 환경에서 타입 안전성을 제공하며, 쿼리의 결과를 예측하고 잠재적인 오류를 방지할 수 있습니다.
    3. 자동 완성 및 검사: Prisma Client는 코드 편집기의 자동 완성 및 검사 기능을 통해 개발 생산성을 향상합니다. 이를 통해 쿼리를 작성하는 동안 필드 이름, 함수 이름 등을 쉽게 찾을 수 있습니다.
    4. 효율적인 연결 관리: Prisma Client는 데이터베이스 연결을 효율적으로 관리합니다. 연결 풀을 사용하여 연결을 재사용하고, 데이터베이스와의 통신을 최적화합니다.
    따라서, Prisma Client를 설치하면 데이터베이스 작업을 단순화하고, 타입 안전성을 제공하며, 개발 생산성을 향상할 수 있습니다.
  • Prisma Client는 Prisma에서 제공하는 자동 생성되는 타입 안전한 데이터베이스 클라이언트입니다.
javascriptCopy code
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

데이터 쿼리

  • 설명: Prisma Client를 사용하여 데이터를 CRUD 작업을 수행합니다. 예를 들면, 사용자를 생성하거나, 데이터를 조회하는 등의 작업을 수행할 수 있습니다.
//schema.prisma 파일

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

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

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  password  String
  createdAt DateTime @default(now())
  boards    Board[]
}

model Board {
  id        String    @id @default(uuid())
  title     String
  content   String
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  isDeleted Boolean   @default(false)
  deletedAt DateTime?
  userId    String
  user      User      @relation(fields: [userId], references: [id])

  @@index([userId], map: "Board_userId_fkey")
}

env 파일 사용

yarn add dotenv

//app.js 서버 실행 파일에 작성하기

import dotenv from "dotenv";

dotenv.config();

//process.env.사용 할 키이름

env 파일 수정하기

DATABASE_URL="mysql://사용자이름:비밀번호@localhost:3306/데이터베이스이름"

ex) 데이터베이스 구축하기 페이지에서 생성한 데이터베이스와 유저이름, 비밀번호를 채워주시면 됩니다.
DATABASE_URL="mysql://node_blogs_user:node_is_great@localhost:3306/node_blogs"

express 폴더구조

├── node_modules
├── src
│   ├── controllers
│   ├── middleware
│   ├── models
│   ├── routes
│   └── app.ts
├── package.json
└── tsconfig.json
  • node_modules: 이 폴더는 Node.js 의존성을 포함하고 있습니다. yarn 또는 **npm**을 사용하여 패키지를 설치하면, 해당 패키지는 이 폴더에 저장됩니다.
  • src: 이 폴더는 프로젝트의 주요 소스 코드를 포함하고 있습니다. 이 폴더 내에는 여러 하위 폴더가 포함될 수 있습니다.
    • controllers: 컨트롤러는 사용자의 요청을 받아서 처리하고, 그 결과를 사용자에게 돌려주는 역할을 합니다. 예를 들어, 사용자가 웹 사이트에서 글을 작성하려고 "글 작성" 버튼을 누르면, 그 요청은 컨트롤러에게 전달되고, 컨트롤러는 사용자가 작성한 내용을 데이터베이스에 저장하는 일을 담당합니다. 그리고 그 결과 (성공 여부나 에러 메시지 등)를 사용자에게 돌려줍니다.
    • middleware: 미들웨어는 사용자의 요청과 응답 사이에 위치하여 특정 작업을 수행하는 함수입니다. 예를 들어, 사용자가 로그인을 하려고 할 때, 미들웨어는 사용자가 입력한 이메일과 비밀번호가 유효한지 확인하는 역할을 합니다. 또한, 로그를 기록하거나 요청을 다른 라우터로 전달하는 등 다양한 역할을 수행합니다.
    • models: 이 폴더는 데이터 모델을 포함하고 있습니다. 이는 데이터베이스의 테이블을 나타내며, 데이터의 구조와 관계를 정의합니다.
    • routes: 라우트는 사용자의 요청이 어떤 컨트롤러로 전달될지 결정하는 역할을 합니다. 예를 들어, 사용자가 웹 사이트의 '/login' URL로 접속하면, 라우트는 이 요청을 로그인을 처리하는 컨트롤러로 전달합니다. '/signup' URL로 접속하면, 회원 가입을 처리하는 컨트롤러로 전달합니다.
    따라서, 컨트롤러는 사용자의 요청을 처리하는 로직을 가지고 있고, 미들웨어는 요청과 응답 사이에서 특정 작업을 수행하며, 라우트는 요청을 적절한 컨트롤러로 전달하는 역할을 합니다.
    • app.ts: 이 파일은 Express 애플리케이션의 진입점입니다. 여기에서는 애플리케이션의 초기 설정을 수행하고, 미들웨어를 등록하며, 라우팅을 설정합니다.
  • package.json: 이 파일은 프로젝트의 메타데이터와 의존성을 정의하고 있습니다. yarn 또는 **npm**을 사용하여 패키지를 설치하면, 해당 패키지는 이 파일에 기록됩니다.
  • tsconfig.json: 이 파일은 TypeScript 프로젝트의 설정을 포함하고 있습니다. 여기에서는 TypeScript 컴파일러의 동작 방식을 정의합니다.

이것은 Express 프로젝트의 일반적인 폴더 구조를 나타내며, 실제 프로젝트에서는 더 많은 폴더와 파일이 포함될 수 있습니다. 예를 들어, 테스트 코드를 위한 tests 폴더, 환경 변수를 저장하는. env 파일, 로깅을 위한 logs 폴더 등이 있을 수 있습니다.

위의 내용들로 원티드 사전 과제를 만들어 보았다

폴더구조

 ┣ 📂prisma
 ┃ ┗ 📜schema.prisma
 ┣ 📂src
 ┃ ┣ 📂controllers
 ┃ ┃ ┣ 📜boardController.js
 ┃ ┃ ┗ 📜userController.js
 ┃ ┣ 📂middleware
 ┃ ┃ ┣ 📜authenticateToken.js
 ┃ ┃ ┗ 📜validateUser.js
 ┃ ┣ 📂routes
 ┃ ┃ ┣ 📜boardRoutes.js
 ┃ ┃ ┗ 📜userRoutes.js
 ┃ ┣ 📂services
 ┃ ┃ ┣ 📜boardService.js
 ┃ ┃ ┗ 📜userService.js
 ┃ ┗ 📂utils
 ┃ ┃ ┣ 📜generateToken.js
 ┃ ┃ ┗ 📜hashPassword.js
 ┣ 📜.env
 ┣ 📜.gitignore
 ┣ 📜README.md
 ┣ 📜app.js
 ┣ 📜package.json
 ┗ 📜yarn.lock

nest.js 프레임워크를 주로 사용해서 services를 만들었던 거 같다 여기에 model은 schema.prisma를 사용해서 만들지 않았다. 그래서 데이터베이스에 직접적으로 조작하는 부분은 serviecs에서 이루어지게 만들어 봤다.

만들어보니 express에서 구조와 역할에 대해서 알게 되었다.

//app.js
import dotenv from "dotenv";
import express from "express";
import userRouter from "./src/routes/userRoutes.js";
import boardRoutes from "./src/routes/boardRoutes.js";

dotenv.config();

const app = express();
const PORT = process.env.PORT ?? 3000;

app.use(express.json());

app.use("/users", userRouter);  // 각 라우터를 import를 해온다 
app.use("/boards", boardRoutes);   //
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => {
  res.send("Hello, world");
});

app.listen(PORT, () => {
  console.log(`Server is running at <http://localhost>:${PORT}`);
});
//boardRoutes.js

const router = express.Router();

router.post("/create", authenticateToken, createNewBoard); //게시글 생성
router.get("/", getBoards); //게시글 목록 조회 페이지네이션
router.get("/:id", getBoardById); //특정 게시글 조회
router.put("/:id", authenticateToken, updateBord); // 게시글 수정
router.delete("/:id", authenticateToken, deleteBoard); // 게시글 삭제

라우터를 통해서 각 메소드에 맞게 URL을 입력하고 해당하는 함수 미들워어와 컨트롤러를 불러와서 실행시킵니다. 

router.post("/create", authenticateToken, createNewBoard);
POST 요청을 /create 엔드포인트로 받으면, authenticateToken 미들웨어로 인증을 수행한 후, 게시글을 생성하는 createNewBoard 컨트롤러를 실행합니다.

router.get("/", getBoards);
GET 요청을 / 엔드포인트로 받으면, 게시글 목록을 조회하는 getBoards 컨트롤러를 실행합니다. 이때 페이지네이션 기능도 함께 제공됩니다.

router.get("/:id", getBoardById);
동적 파라미터 :id를 사용해, 특정 게시글의 ID를 URL에서 가져와 해당 게시글을 조회하는 getBoardById 컨트롤러를 실행합니다.

router.put("/:id", authenticateToken, updateBord);
PUT 요청을 통해 게시글을 수정합니다. 수정할 게시글의 ID는 URL에서 가져오며, 인증된 사용자만 게시글을 수정할 수 있습니다.

router.delete("/:id", authenticateToken, deleteBoard);
DELETE 요청을 통해 게시글을 삭제합니다. 인증된 사용자만 게시글을 삭제할 수 있습니다.
각 라우터는 URL, 미들웨어, 그리고 컨트롤러를 연결하여 API 요청을 적절히 처리하고 응답을 반환합니다.

/

//boardController.js

export const createNewBoard = async (req, res) => {
  const { title, content } = req.body;
  const userId = req.user.id;
  if (!userId) {
    return res
      .status(401)
      .json({ error: "로그인 후 게시글 작성이 가능합니다. " });
  }
  const newBoard = await createBoard(title, content, userId);

  res.status(201).json(newBoard);
};

---------------------------------------------------------------------------

//boardService.js

export const createBoard = async (title, content, userId) => {
  return await prisma.board.create({
    data: {
      title,
      content,
      userId,
    },
  });
};

JWT를 이용한 인증 인가

비밀번호 bcrypt
이 부분은 추후 정리해서 올려야겠다 테스트코드 작성하고 추가과제 다하고 다 정리해야지

swagger은 나중에 해봐야지

yarn add swagger-jsdoc swagger-ui-express

[ Node.js] - prisma

express jest test code

728x90
반응형

'Backend' 카테고리의 다른 글

Nest.js Testing 공식문서  (0) 2023.07.14
소셜 로그인 프로세서  (0) 2022.12.29
Microservice  (0) 2022.12.25
DNS / Load Balancer  (0) 2022.12.24
TDD  (0) 2022.12.23