서버
-웹이나 앱을 사용할 때 우리들의 데이터(아이디, 비번, 이메일)와 서비스의 데이터가 생성됨. 이 데이터를 어디에서 저장하고, 거기에서 클라이언트로 받아와야하는 곳. 그곳이 서버.
-클라이언트의 요청에 대해 응답 함(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 등등
- const express = require('express');
- 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 시그니쳐 포트
- server.listen(10010, () => {
- const http = require('http')
- package.json
- script 추가
- "start": "node index.js"
- script 추가
- 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를 읽어올 수 있음
- app.use(express.json());
- 데이터베이스 연결
- 회원가입 서비스는 데이터를 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(데이터베이스)의 포트
- DATABASE_URL=mysql://root:비밀번호@localhost:3306/데이터베이스명
- prisma/schema.prisma
- prisma세팅: prisma폴더, .env파일, .gitignore 파일 생성
- npm i prisma
- 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();
- import { PrismaClient } from "@prisma/client";
- prisma 세팅 끝
- 잘 세팅 됬는지 확인
- node index.js
- node는 코드 바뀔때마다 재실행 해줘야되서 사용하기 편한 nodemon 설치
- npm i nodemon
- npm i -g: 이걸로 설치하면 컴퓨터에 설치하는거지 해당 프로젝트에 설치하는게 아님
- package.json 안에 scripts에서 node -> nodemon으로 변경
- *이미 dependencies안에 nodemon이 있어야함
- npm i nodemon
- 서버 확인
- npm run start
- (nodemon)이제 코드를 바꾸면 자동으로 서버 새로 실행
- npm run start
- .prettierrc 파일 생성하여 다른 사람과의 포멧 통일
- printWith: 80 노트북 환경, 120 데스크탑 환경 (보통 80)
- {
"singleQuote": true,
"printWidth": 80
}
- {
- printWith: 80 노트북 환경, 120 데스크탑 환경 (보통 80)
- 회원가입 서비스는 데이터를 DB에 저장해야하니까 mySQL설치, ORM인 prisma설치
app.js
회원가입: app.post("/signup", (req, res) => {});
- 백엔드의 콜백함수에서는 라우터 부분에서 req객체, res객체를 받아옴
- 자바스크립트는 비동기 처리를 위해 콜백함수를 사용
- 1. request로 뭘 받을지 정의
- 1). 어떤 데이터를 받을 것인지 정의: request body안에 email, password정보를 받아올 거다라는 것을 미리 정의
- 구조분해할당, 객체 -> email, password로 변수를 선언한 것
- const {email, password} = req.body;
- req.body는 객체(키-값)을 받음
- http://expressjs.com/en/5x/api.html#req.body
- 1). 어떤 데이터를 받을 것인지 정의: request body안에 email, password정보를 받아올 거다라는 것을 미리 정의
- 2). 예외처리/검사: if문으로 입력한 email, password 정보에 대한 예외처리/검사
- if(!email.includes('@')){
}
if(password.length < 7){
}
- if(!email.includes('@')){
- 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 동적으로 값이 할당되게 함
- prisma.$queryRaw`
- 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바로 하면됨
- if(!email.includes('@')){
- 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;
- const { PrismaClient } = require('@prisma/client');
- app.js에서는 express에서 가져왔는데 routers에서는 각각의 router를 확장하는 것이기 때문에 Router를 써줌
- https://expressjs.com/en/guide/routing.html
- express, prisma가져오기
- 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);
- app.use(userRouter);
- const userRouter = require('./routers/user');
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) 적용
- router.get('/postings', (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적용해주기
- const postings = prisma.$queryRaw`
- 2.response : 받은 postings라는 배열을 이용해서 res로 나가야함
- res.json({ data: postings });
- 백엔드 먼저 개발한다고 가졍하고, data로 통일
- res.json({ data: postings });
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 });
- queryRaw의 결과는 배열로 들고옴
- const queryRows = await prisma.$queryRaw`...`;
- console.log(queryRows); // [...]
- id를 이용하여 쿼리를 하면 배열에는 항상 하나의 결과값만 담김. 첫번째 결과만 분해 할당하여 사용
- https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access
- queryRaw의 결과는 배열로 들고옴
- http://expressjs.com/en/5x/api.html#req.params
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})
`;
- await prisma.$queryRaw`
- 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은 한번만
- try {
- 데이터가 잘 들어가면 response에 도달
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}`;
- const { id } = req.params;
- 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}
`;
- const { id } = req.params;
- 2.응답
- res.status(201).json({ message: 'success' });
- *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
);
- CREATE TABLE posting_images
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;
- const [user] = prisma.$queryRaw`
- 2.중복이 있으면(if문) 알려주기
- if (user) {
res.status(409).json({ message: 'existing_user' });
return;
}
- if (user) {
- 중복된 유저일 경우 409 status보내기->데이터 접근해서 확인 -> prisma
레이어드 패턴화: 파일이 커지면서 분업화 하기
- 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;
}
- function getUserByEmail(email) {
- 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를 분할하고, 그대로 쿼리 실행(데이터를 넣는 작업)
- async function createUser(createUserDto) {
- user안에 정보를 객체 구조분해 -> 받아올 정보: 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상태가 되면 다시 재개
- async/await는 프로미스를 기반으로 동작
- 1.email중복확인, email 받아오고(매개변수), user리턴, 비동기화 async-await 추가하기
- /signup에서 쿼리문 가져오기
- services: 비지니스 로직 담당
- user.js
- 서비스 단에서 빼낸 modules 호출
- 어떤 서비스인지 서비스명대로 정의 (함수 만들기), 이것도 controller에서 사용되기 때문에 module로 빼기
- function signup(email, password) {}
function login(email, password) {}
module.exports = {signup, login};
- function signup(email, password) {}
- user.js
- 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);
}
}
- (req, res) => {
- 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);
}
};
- routers/user.js (/login, /signup의 callback부분) -> controller로 이동
- controller도 router에서 호출해서 쓸거기 때문에 export하기
- module.exports = { signupController, loginController };
- user.js생성
- Routes (routers에서 폴더 이름만 바꿈)
- user.js
- controller를 import함 (require쓰고 경로 지정)
- const { signupController, loginController } = require('../controllers/user');
- res,req자리에 지정
- router.post('/signup', signupController);
router.post('/login', loginController);
- router.post('/signup', signupController);
- controller를 import함 (require쓰고 경로 지정)
- 관심사 별로 코드를 한눈에 파악할 수 있음
- user.js
- 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에 넣기
- if (password.length < 8) {
- 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에서 담당하기 때문에 지워줌
- if (password.length < 8) {
- user중복확인
- models/user.js에 있는 getUserByEmail로 대체
- const user = await getUserByEmail(email);
if (user) {
const error = new Error('existing user');
error.statusCode = 400;
throw error;
}
- const user = await getUserByEmail(email);
- 중복된 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})`;
}
- async function createUser(createUserDto) {
- 처음에 매개변수로는 email, password가 분리되서 들어오는데 createUser에서 하나로 묶게 되어있음. createUserDto라는 object를 이용해서 넘기면 안에 넣어주면 됨
- await createUser({ email, password });
- models/user.js에 있는 getUserByEmail로 대체
- 비밀번호 확인
- 중복된 코드를 관리하고 싶을때 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);
- function createError(errMessage, errCode) {
- user.js, signup Controller에서 signup 서비스 떼서 await signup(email, password)로 대체
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 부분 지우기
- if (!email.includes('@')) {
- 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});
- const token = await login(email, password);
- password받기 위해 JWT와 bcrypt설치
- npm i bcrypt jsonwebtoken
- signup할 때 비밀번호를 암호화 하기
- services/user.js
- bcrypt, jwt를 import하기
- const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
- const bcrypt = require('bcrypt');
- salt를 하나 만들기
- const salt = bcrypt.genSaltSync();
- bcrypt, jwt를 import하기
- services/user.js
- const loginController = async(req, res) => {
- 서비스하는 함수를 불러오기, 토큰 넘기기
- 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으로 정의
- .env
- 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});
}
};
- const loginController = async(req, res) => {
- return이 쓰기 싫으면 res.json자체를 try안에 넣을 수 있음
- const signupController = (req, res) => {
- controllers, services, models에서 던지는 에러들이 각각 있는데, 각각 try-catch로 관리하지말고(반복하지 말고)
- else {
- if (bcrypt.compareSync(password, user.password)) {
- if문 매개변수 첫번째: client가 입력한 매개변수의 password받고, 두번째: 데이터베이스에서 가져온 암호화된 password. user안에 있음
- getUserByEmail은 models 에서 async로 만들었기 때문에 위에서도 await를 걸어준다
- [user] = prisma.$queryRaw`
- 해야할 일: 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);
}
});
- app.delete('/postings/:id', async (req, res, next) => {
'Node.js' 카테고리의 다른 글
미들웨어 (0) | 2022.06.19 |
---|---|
백 엔드, 프론트 엔드 정리 (0) | 2022.06.17 |
[백엔드]인증, 인가 (0) | 2022.06.15 |
URL: 같은 파일이지만 서로 다른 페이지를 보여주는 방법 (0) | 2022.06.14 |
엔드 포인트Endpoint, 엔트리 포인트Entrypoint (0) | 2022.06.14 |