Node.js

노드 Node.js 프로젝트 만들기 A-Z

selonjulie 2022. 6. 16. 13:39

서버

-웹이나 앱을 사용할 때 우리들의 데이터(아이디, 비번, 이메일)와 서비스의 데이터가 생성됨. 이 데이터를 어디에서 저장하고, 거기에서 클라이언트로 받아와야하는 곳. 그곳이 서버.

-클라이언트의 요청에 대해 응답 함(Yes or No).

세팅

  • node설치
    • npm init
  • package.json확인
  • express설치
    • npm i express
  • index.js파일(엔트리 파일) 추가 (index.js와 app.js를 server.js로 합쳐도 가능/package.json에도 main과 script부분 수정) 
    • vi index.js 아니면 직접 VScode에서 파일추가
    • 엔트리 파일: 이전에 썼던 application code를 실행시키는 역할
  • app.js 파일 추가
    • const express = require('express');
      • cmd누르고 express클릭하면 어떤 코드로 이루어져있는지 설명하는 파일에 접근 가능
    • const app= express();
      • express를 호출하면 app은 express객체가 된다
      • express객체를 사용하는 방법
        • app.use
        • app.get
        • app.route 등등
  • index.js에서 app.js를 호출하여 사용할 수 있게하기
    • app.js: module.exports = app;
    • index.js: const app = require('./app');
      • ./ : 현재 폴더
  • index.js내부에 server객체 생성, http모듈 사용 (app, http불러오고, 서버 listen하게 지정)
    • const http = require('http')
      • http는 node에 있는 라이브러리라서 ./를 사용하지 않아도됨
      • node에서는 require문법, react는 import문법
    • const server = http.createServer(app);
      • express application을 만들어서 이를 http로 보내겠다
      • 이제 서버는 listen을 할 수 있는 상태
    • server.listen();
      • server.listen(10010, () => {
          console.log("server start: http://localhost:10010/");
        });
      • 어떤 포트로 listen할지 결정
      • 이에 따라 insomnia, postman에서 뒤에 붙는 숫자가 달라짐
        • 포트에 대한 규칙은 없음
        • 1000번때 이상으로(~30000)에만 적어주면 됨 (1000번대 아래는 이미 예약된 포트) 
      • 10010: express 시그니쳐 포트
  • package.json
    • script 추가 
      • "start": "node index.js"
  • app.js 작성: 서비스가 추가됨
    • app.get, app.post, app.use 써서 구현 가능
    • signup과 login URL생성 
      • app.post('/signup', (req, res)=>{})
      • app.post("/login", (req, res)=>{})
    • express서버에서는 json으로만 통신할거라서, json읽을 수 있는 middleware 추가
      • app.use(express.json());
        • 이걸 써야지 app.post에 들어오는 body를 읽어올 수 있음

  • 데이터베이스 연결
    • 회원가입 서비스는 데이터를 DB에 저장해야하니까 mySQL설치, ORM인 prisma설치
      • npm i prisma
        • prisma설치
      • npx prisma init
        • prisma세팅: prisma폴더, .env파일, .gitignore 파일 생성
          • prisma/schema.prisma
            • databasesource db, provider -> mysql로 수정 
          • .env파일
            • DATABASE_URL=mysql://root:비밀번호@localhost:3306/데이터베이스명
              • 3306: mySQL(데이터베이스)의 포트
    • schema폴더 생성
      • ORM을 사용하면 파일로 바로 sql을 작성할 수 있음
      • sql작성해서 확장자 .sql로 작성
        • 01.create-users-table.sql
    • schema.prisma에서 스키마 작성
      • prisma에서 mysql에 등록된 데이터베이스를 가져올 수 있음
      • npx prisma db pull
        • schema.prisma내부에 DB모델 생성
      • npx prisma generate
        • 프리즈마 파일은 자동완성 추적이 안되서 reload를 해줘야함. VScode에서 cmd + P,  >reload window다시실행
        • app.js 에 아래 코드 삽입
          • import { PrismaClient } from "@prisma/client";
            • const { PrismaClient } = require("@prisma/client");
            • import에서 require로 수정
          • const prisma = new PrismaClient();
      • prisma 세팅 끝
      • 잘 세팅 됬는지 확인
        • node index.js
        • node는 코드 바뀔때마다 재실행 해줘야되서 사용하기 편한 nodemon 설치
          • npm i nodemon
            • npm i -g: 이걸로 설치하면 컴퓨터에 설치하는거지 해당 프로젝트에 설치하는게 아님
          • package.json 안에 scripts에서 node -> nodemon으로 변경
            • *이미 dependencies안에 nodemon이 있어야함
      • 서버 확인
        • npm run start
          • (nodemon)이제 코드를 바꾸면 자동으로 서버 새로 실행
      • .prettierrc 파일 생성하여 다른 사람과의 포멧 통일
        • printWith: 80 노트북 환경, 120 데스크탑 환경 (보통 80)
          • {
                "singleQuote": true,
                "printWidth": 80
            }

 

app.js 

 회원가입: app.post("/signup", (req, res) => {});

  • 백엔드의 콜백함수에서는 라우터 부분에서 req객체, res객체를 받아옴
  • 자바스크립트는 비동기 처리를 위해 콜백함수를 사용
  • 1. request로 뭘 받을지 정의
    • 1). 어떤 데이터를 받을 것인지 정의: request body안에 email, password정보를 받아올 거다라는 것을 미리 정의 

  • 2). 예외처리/검사: if문으로 입력한 email, password 정보에 대한 예외처리/검사
    • if(!email.includes('@')){

          }
      if(password.length < 7){
              
          }
  • 3). 받은 데이터를 데이터베이스에 저장(회원가입은 처음 받는 데이터니까 DB에 저장): email, password를 정보, mySQL함께 사용 (MySQL)
    •     prisma.$queryRaw`
          INSERT INTO 
              users (email, password)
          VALUES (${email}, ${password})`
    • prisma.$queryRaw : 프로미스 타입, queryRaw의 결과도 배열로 들어옴
      • 프로미스 타입: 바로 값이 들어오지 않음. 비동기 처리 시점을 표현해야함.
        • 실제 데이터를 추출하기 위해서는
        • .then(data)으로 써서 데이터를 받아오는 형태임 (fetch도 동일)
          • 들여쓰기가 늘어나는 형태(콜백헬)를 피하기 위해 await, async사용
        • 아니면,  await, async(상위 함수에 적음)를 붙여줄 수 있음
          • async (req, res) =>
    • 값이 들어갈때 email, password 동적으로 값이 할당되게 함
  • 2. 값이 잘 들어왔는지 response응답
    • 성공
      • res.status(201).json.({message: "success"})
    • 실패 
      • 에러처리 추후에 추가
    • *promise의 에러처리
      • .then
      • .catch
      • .finally

로그인: app.post('/login', (req, res) => {});

  • 1. request로 뭘 받을지 정의
    • 1). 어떤 데이터를 받을 것인지 정의: request body안에 email, password정보를 받아올 거다라는 것을 미리 정의 
    • 2). 예외처리/검사: if문으로 email, password 정보에 대한 예외처리/검사
      • if(!email.includes('@')){
        res.status(400).json({message: 'error'});
        return;
        }
        • email에서 @가 안들어가면 에러 메세지 보내줘
        • if문에 들어가서 res하면 오류가나면 더이상 로직을 처리할 필요 없으니까 return바로 하면됨
    • 3). 로그인은 회원가입이 된 사람이기 때문에 기존 DB(user테이블)에서 데이터를 불러오기
      • id, email, password데이터 불러오기
      •   const [user] = prisma.$queryRaw`
          SELECT id, email, password
          FROM users;
          `
      • 데이터를 배열로 받게됨 -> 하나의 데이터만 필요하기 때문에 구조분해 할당 사용
    • 4).  로그인을 위해 client가 입력한 데이터 - 변수 user(불러온 데이터) 비교: 비밀번호가 동일한지 확인
      • if... else절
      •   if(user.password === password){
        res.status(200)
          } else {
        res.status(400);
          }

기능 구현 잘 되었는지 postman에서 확인

  • signup
    • post -> JSON -> body
    • 객체로 email, password 넣어서 보낸후 작동되는지 확인 *프로퍼티 사이에 꼭 , 작성
    • mySQL에서 실제 데이터가 DB에 들어왔는지 확인

  • login
    • 추후확인

 

라우터 생성, 폴더별로 나누어 주기

  • routers폴더 생성
    • postings.js, user.js 생성
  • postings.js
    • express, prisma가져오기
      • const { PrismaClient } = require('@prisma/client');
        const prisma = new PrismaClient();

        const express = require('express');

        const router = express.Router();

        module.exports = router;
    • app.js에서는 express에서 가져왔는데 routers에서는 각각의 router를 확장하는 것이기 때문에 Router를 써줌
    • https://expressjs.com/en/guide/routing.html

  • users.js
    • 동일 적용
  • 관심사 나누기: app.js에 router를 import하기
    • const userRouter = require('./routers/user');
      const postingRouter = require('./routers/postings');
    • user와 관련된 것은 user.js로 옮겨주기
      • app.post("/signup", (req, res) => {}),  app.post("/login", (req, res) => {})
      •  router로 변경
        • router.post("/signup", (req, res) => {}),  router.post("/login", (req, res) => {})
    • app.js에서 router쓸 수 있게 불러오기
      • app.use(userRouter);
        app.use(postingRouter);

 

posting.js: CRUD

  • CRUD 기능(불러오기,상세정보, 게시,수정,삭제)에 따라 만들기
    • router.get('/postings', (req,res)=>{});
      router.get('/postings/:id', (req,res)=>{});
      router.post('/postings', (req,res)=>{});
      router.put('/postings/:id', (req,res)=>{});
      router.delete('/postings/:id', (req,res)=>{});
    • async (req, res) 적용 

1.[READ] 불러오기: router.get('/postings', (req,res)=>{});

  • 1.DB에서 postings 데이터를 받아오기
    •   const postings = prisma.$queryRaw`
          SELECT 
              id, 
              user_id, 
              contents, 
              created_at
          FROM postings;
      `;
    • queryRaw가 프로미스이기 때문에 데이터를 받을 수 있게 await, async적용해주기
  • 2.response : 받은 postings라는 배열을 이용해서 res로 나가야함
    •   res.json({ data: postings });
      • 백엔드 먼저 개발한다고 가졍하고, data로 통일

 

2.[READ]: id를 이용해 특정 포스트 들고오기: router.get('/postings/:id', (req,res)=>{});

  • 동일하게 들고와서 req.params활용, where문 추가
    • http://expressjs.com/en/5x/api.html#req.params 
      • req.params은 객체를 가진 프로퍼티임
    •     const {id} = req.params;
          
          const postings = await prisma.$queryRaw`
          SELECT 
              id, 
              user_id, 
              contents, 
              created_at
          FROM postings
          WHERE id = ${id};
      `;
        res.json({ data: postings });
    • :id (params 파라미터) 를 이용하여 데이터를 들고 옴
      • 구조분해 할당을 하여 제일 처음에 있는 데이터를 들고오기
      • const [posting] = await prisma.$queryRaw
      •   res.json({ data: posting });

3.[CREATE] 포스팅 작성하기: router.post('/postings', (req,res)=>{});

  • post메소드는 create할 때 사용됨
  • 1.req.body에 어떤 데이터가 들어오게 할건지(포스팅할때 어떤 데이터를 받아야하는지)를 정하기
    • user_id, contents넣어주기 (처음 포스팅쓸때 아이디, 내용 추가, id와 created_at은 defalut값으로 들어가므로 별도 추가 하지 않아도됨 )
    •   const { userId, contents } = req.body;
      • *req.body는 객체를 받음

  • 2.들어오는 데이터를 DB에 추가하기 (INSERT INTO)
    •   await prisma.$queryRaw`
        INSERT INTO
          postings (user_id, contents)
        VALUES
          (${userId}, ${contents})
        `;
  • 3.데이터가 들어간 이후의 response
    • 데이터가 잘 들어가면 response에 도달
      •   res.status(201).json({ message: 'created' });
    • 데이터가 안 들어가면
      • await를 걸었기 때문에 throw가 되서, 에러 처리 middleware가 있으면 그 쪽으로 가고, 여기는 middleware가 없기 때문에 async 함수가 queryRaw에서 던진 에러를 받게됨. async 익명함수(콜백함수)도 에러를 던지게되면 '전역'에러이기 때문에 프로그램이 종료됨  
      • 그러는걸 방지하기 위에 try, catch 혹은 async wrap을 사용
        •   try {
              await prisma.$queryRaw`
                INSERT INTO
                  postings (user_id, contents)
                VALUES
                  (${userId}, ${contents})
                `;
            } catch {
              res.status(500).json({ message: 'error' });
              return;
            }
        • *응답 두 번 보내면 에러이기 때문에 return은 한번만

 

3.[PUT] 업데이트: router.put('/postings/:id', (req,res)=>{});

  • 1.put메소드는 body로 받아올 수 있음. 어떤 데이터로 업데이트 할건지 WHERE를 사용 (id가 params 파라미터인것)
  • 쿼리문에 비동기 적용해주기
    •   const { id } = req.params;
        const { userId, contents } = req.body;

        await prisma.$queryRaw`
      UPDATE
          postings
      SET
          user_id = ${userId},
          contents = ${contents}
      WEHRE
          id = ${id}`;
  • 2.응답
    • res.status(201).json({ message: 'success' });

 

4.[DELETE] 삭제하기: router.delete('/postings/:id', (req,res)=>{});

  • 1.데이터
    •   const { id } = req.params;

        await prisma.$queryRaw`
          DELETE FROM
              postings
          WHERE id = ${id}
          `;
  • 2.응답
    •   res.status(201).json({ message: 'success' });

http://expressjs.com/en/5x/api.html#res.status
http://expressjs.com/en/api.html#res.json

 

 

  • *foreign key 에러날때 ON DELETE CASCADE적용: 연결되어 있는거까지 지워지는 것
    • CREATE TABLE posting_images
      (
      id INT AUTO_INCREMENT,
      posting_id INT NOT NULL,
      image_url VARCHAR(3000),
      created_at DATETIME DEFAULT NOW(),
      PRIMARY KEY (id),
      FOREIGN KEY (posting_id) REFERENCES postings (id) ON DELETE CASCADE
      );

 

API예외처리 (회원가입)

  • user.js
    • router.post('/signup', async (req, res) => {}
    • 회원가입 성공시
      •   res.status(201).json({ message: 'signup_success' });
    • 회원가입 예외처리
      • 중복된 유저일 경우 409 status보내기->데이터 접근해서 확인 -> prisma
        • (기존@들어가있는 코드 지우고)
        • 1.데이터 받아오고
          •   const [user] = prisma.$queryRaw`
              SELECT 
                id, 
                email, 
                password
              FROM users;
        • 2.중복이 있으면(if문) 알려주기
          •   if (user) {
                res.status(409).json({ message: 'existing_user' });
                return;
              }

 


레이어드 패턴화: 파일이 커지면서 분업화 하기

  • 3개로 나누어질 폴더 생성 (controllers, services, models)
    • model/dao/repository 여러 이름으로 불림
  • controllers

Signup분리 

  • models: 쿼리(SQL)관련 된건 모두 넣기
    • postings.js, user.js 파일 만들기
    • 쿼리 들어있는거 다 가져온 후 각각 함수 만들어주기
    • user.js
      • /signup에서 쿼리문 가져오기
        • 1.email중복확인, email 받아오고(매개변수), user리턴, 비동기화 async-await 추가하기 
          • function getUserByEmail(email) {
              const [user] = prisma.$queryRaw`
                SELECT 
                  id, 
                  email, 
                  password
                FROM users
                WHERE email = ${email}    
                `;

              return user;
            }
        •  2.회원가입 createUser, 매개변수 createUserDto(data transfer object) 
          • user안에 정보를 객체 구조분해 -> 받아올 정보: email, password
            • async function createUser(createUserDto) {
                const { email, password } = createUserDto;

                await prisma.$queryRaw`
                  INSERT INTO 
                      users (email, password)
                  VALUES (${email}, ${password})`;
              }
            • createUserDto를 받아와서, email, password를 분할하고, 그대로 쿼리 실행(데이터를 넣는 작업)
        • 3.사용을 하기 위해 module로 밖으로 빼주기
          • module.exports = { getUserByEmail, createUser };
        • *module
          • module: 특정한 기능을 하는 함수나 변수들의 집합 (재사용 가능). module.exports에 변수들을 담은 객체를 대입 가능. module.exports에 객체, 함수, 변수 대입 가능
        • *async await위치
          • async/await는 프로미스를 기반으로 동작
            • 프로미스의 후속처리 메서드(then/catch/finally) 없이 마치 동기처리처럼 프로미스가 처리 결과를 반환하도록 구현
          • async 함수: async function으로 정의
          • await 키워드: 프로미스가 settled상태(비동기 처리가 수행된 상태)가 될 때까지 대기하다가 settled되면 프로미스가 resolve한 처리 결과가 res변수에 할당. await는 반드시 프로미스 앞에서 사용해야함.
            • 다음 실행을 일시 중지시켰다가 프로미스가 settled상태가 되면 다시 재개
  • services: 비지니스 로직 담당
    • user.js
      • 서비스 단에서 빼낸 modules 호출
      • 어떤 서비스인지 서비스명대로 정의 (함수 만들기), 이것도 controller에서 사용되기 때문에 module로 빼기
        • function signup(email, password) {}

          function login(email, password) {}

          module.exports = {signup, login};
        •  
  • controller (*router와 controller와 큰 차이는 없음): request, response담당
    • user.js생성 
      • routers/user.js  (/login, /signup의 callback부분) -> controller로 이동
        • (req, res) => {
              const { email, password } = req.body;
            
              if (!email.includes('@')) {
                res.status(400).json({ message: 'error' });
                return;
              }
              if (password.length < 7) {
                res.status(400).json({ message: 'error' });
                return;
              }
            
              const [user] = prisma.$queryRaw`
                SELECT id, email, password
                FROM users;
                `;
            
              if (user.password === password) {
                res.status(200);
              } else {
                res.status(400);
              }
            }
      • const signupController = (req, res) => {
          const { email, password } = req.body;

          if (password.length < 8) {
            res.status(400).json({ message: 'Password_too_short' });
            return;
          }

          if (user) {
            res.status(409).json({ message: 'existing_user' });
            return;
          }

          res.status(201).json({ message: 'signup_success' });
        };

        const loginController = (req, res) => {
          const { email, password } = req.body;

          if (!email.includes('@')) {
            res.status(400).json({ message: 'error' });
            return;
          }
          if (password.length < 7) {
            res.status(400).json({ message: 'error' });
            return;
          }

          const [user] = prisma.$queryRaw`
              SELECT id, email, password
              FROM users;
              `;

          if (user.password === password) {
            res.status(200);
          } else {
            res.status(400);
          }
        };
    • controller도 router에서 호출해서 쓸거기 때문에 export하기
      • module.exports = { signupController, loginController };
  • Routes (routers에서 폴더 이름만 바꿈)
    • user.js
      • controller를 import함 (require쓰고 경로 지정)
        • const { signupController, loginController } = require('../controllers/user');
      • res,req자리에 지정
        • router.post('/signup', signupController);

          router.post('/login', loginController);
    • 관심사 별로 코드를 한눈에 파악할 수 있음
  • Controllers -> Services
    • user.js, signup Controller에서 signup 서비스 떼서 await signup(email, password)로 대체
      • if (password.length < 8) {
            res.status(400).json({ message: 'Password_too_short' });
            return;
          }

          if (user) {
            res.status(409).json({ message: 'existing_user' });
            return;
          }
      • =>얘는 services/user.js에 넣기
    • services/user.js: res.status대신  throw추가해주기
      • 비밀번호 확인
        •   if (password.length < 8) {
              const error = new Error('Password_too_short');
              error.statusCode = 400;
              throw error;
            }
          • req, res는 controller에서 담당하기 때문에 지워줌
      • user중복확인
        • models/user.js에 있는 getUserByEmail로 대체
          •   const user = await getUserByEmail(email);

              if (user) {
                const error = new Error('existing user');
                error.statusCode = 400;
                throw error;
              }
        • 중복된 user가 없을때(정상적인 회원가입일때) 들어온 정보를 DB에 추가
          •   await createUser({ email, password });
            • 처음에 매개변수로는 email, password가 분리되서 들어오는데 createUser에서 하나로 묶게 되어있음. createUserDto라는 object를 이용해서 넘기면 안에 넣어주면 됨
              • or
              • const createUserDto = { email, password };
                await createUser(createUserDto);
            • models/user.js : createUser에서는 createUserDto를 받아서 Dto안에 있는 email, password를 구조분해 할당을 하고 INSERT를 진행함
              • async function createUser(createUserDto) {
                  const { email, password } = createUserDto;

                  await prisma.$queryRaw`
                    INSERT INTO 
                        users (email, password)
                    VALUES (${email}, ${password})`;
                }
    • 중복된 코드를 관리하고 싶을때 model 폴더 - createError.js
      • function createError(errMessage, errCode) {
          const error = new Error(errMessage);
          error.statusCode = errCode;

          return error;
        }

        module.exports = { createError };
      • services/user.js
        • 에러쪽 대체하기, import해주기
        • const { createError } = require('../module/createError');
        •     const error = new Error('Password_too_short');
              error.statusCode = 400;
        • =>
        •    const error = createError('Password_too_short', 400);

Login분리 (token발행)

  • controllers/user.js 에 loginController -> services/user.js로 넘기기
    • if (!email.includes('@')) {
          res.status(400).json({ message: 'error' });
          return;
        }
        if (password.length < 7) {
          res.status(400).json({ message: 'error' });
          return;
        }

        const [user] = prisma.$queryRaw`
            SELECT id, email, password
            FROM users;
            `;

        if (user.password === password) {
          res.status(200);
        } else {
          res.status(400);
        }
    • res.status 부분 지우기
  • controllers/user.js: loginController
    • 서비스하는 함수를 불러오기, 토큰 넘기기
      • const loginController = async(req, res) => {
          const { email, password } = req.body;
          
          await login(email, password);

          res.json({token: ''});
        };
      • await를 받아오는건 token, 로그인을 하면 token을 client한테 전달 해야함
        •   const token = await login(email, password);

            res.json({token});
      • password받기 위해 JWT와 bcrypt설치
        • npm i bcrypt jsonwebtoken
        • signup할 때 비밀번호를 암호화 하기
          • services/user.js
            • bcrypt, jwt를 import하기
              • const bcrypt = require('bcrypt');
                const jwt = require('jsonwebtoken');
            • salt를 하나 만들기
              • const salt = bcrypt.genSaltSync();

  •  createUserDto의 password를 hash화해서 값을 전달

  •   const createUserDto = {
        email,
        password: bcrypt.hashSync(password, salt),
      };

      await createUser(createUserDto);
    • createUser를 넘길 때, 기존에 암호화를 하지 않았다라면 ->평문, bcrypt적용하면 암호화 해서 넘김
  • services/user.js : user를 받아오기
    • [user] = prisma.$queryRaw`
                SELECT id, email, password
                FROM users;
                `;
    • =>
    •   const user = await getUserByEmail(email);
      • getUserByEmail은 models 에서 async로 만들었기 때문에 위에서도 await를 걸어준다
        • models
        • async function getUserByEmail(email) {
            const [user] = await prisma.$queryRaw`
              SELECT 
                id, 
                email, 
                password
              FROM users
              WHERE email = ${email}    
              `;

            return user;
          }
      • 그럼 user에 데이터가 들어오고, 그 데이터에서 그 password와 로그인시 암호화한 password비교 
      • services/user.js
        • if문 매개변수 첫번째: client가 입력한 매개변수의 password받고, 두번째: 데이터베이스에서 가져온 암호화된 password. user안에 있음
          •   if (bcrypt.compareSync(password, user.password)) {
              //로그인 성공
              }else{
              //로그인 실패
              }
          • 로그인 성공 -> 토큰 만들어주기
            • jwt.sign(매개변수는 식별되는 값으로, )
            • 토큰을 만들때는 secret key 필요. secret key값은 .env에서 작성해서 관리(보안상으로 더 안전)
              • .env
                • SECRET_KEY = secret_key
              • process를 사용하면 .env에 접근가능
              • 토큰 유효기간 expiresIn으로 정의
            •   if (bcrypt.compareSync(password, user.password)) {
                  const token = jwt.sign({ id: user.id }, process.env.SECRET_KEY, {
                    expiresIn: '1d',
                  });
                  return token;
                }
          • 로그인 실패 -> null값 보내기, 아니면 throw
            • else {
                  const error = createError('Login fail', 400);
                  throw error;
                }
            • 에러처리
              • controllers, services, models에서 던지는 에러들이 각각 있는데, 각각 try-catch로 관리하지말고(반복하지 말고)
                • const signupController = (req, res) => {
                    const { email, password } = req.body;
                  try{
                    await signup(email, password)

                  }catch(err){
                    res.status(err.statusCode).json({message:err.message})
                    return;
                  }

                    res.status(201).json({ message: 'signup_success' });
                  };
                • const loginController = async(req, res) => {
                    const { email, password } = req.body;
                    try{
                      const token = await login(email, password);
                    }catch(err){
                      res.status(err.statusCode).json({message:err.message})
                      return;
                    }

                    res.json({token});
                  };
                  • return이 쓰기 싫으면 res.json자체를 try안에 넣을 수 있음
                    • const loginController = async(req, res) => {
                        const { email, password } = req.body;
                        
                      try{
                          const token = await login(email, password);
                          res.json({token});
                        }catch(err){
                          res.status(err.statusCode).json({message:err.message});
                        }
                      };
  • 해야할 일: posting.js를 각각 controllers, services, models로 나누기

 

에러 핸들링 미들웨어

app.use(() => {})

  • 전체에서 실행. 미들웨어 에러핸들링은 전체에서 실행되야함. 아래에 두기 (const server = http.createServer(app);)전에 
    • app.use(/, () => {})
    • 해당 경로에서만 실행
  • app.use((err, req, res, next) => {})
    • 인자의 이름은 상관없고 인자의 순서가 중요
    • app.use((err, req, res, next) => {
        if (String(err.message).startsWith('user')) {
          res.status(err.statusCode).json('message: err.message');
          return;
        }

        if (String(err.message).startsWith('posting')) {
          res.status(err.statusCode).json('message: err.message');
          return;
        }

        if (err) {
          res
            .status(err.statusCode || 500)
            .json({ message: 'server error:관리자에게 문의하세요' });
        }
      });
    • try, catch는 다 삭제해도 됨
    • console.error(err);추가해서 어떤 에러가 찍히는지 확인
  • 비동기 async있는곳은 next를 써주면됨 (아니면 asyncWrap 사용), async없으면 throw
    • app.delete('/postings/:id', async (req, res, next) => {
        const { id } = req.params;

        try {
          await prisma.$queryRaw`
      DELETE FROM postings WHERE = ${id};`;
        } catch (err) {
          const error = new Error('posting: 삭제에 실패했습니다');
          error.statusCode = 400;
          next(error);
        }
      });