1. Postman 정리
- postman _ collection 추가
- postman _ collection_request 추가
1-1. 모든 트윗 가져오기
Ⅴ Ⅴ Ⅴ
1-2. 해당 아이디에 대한 트윗 가져오기
Ⅴ Ⅴ Ⅴ
1-3. 해당 이름에 대한 트윗 가져오기
Ⅴ Ⅴ Ⅴ
1-4. 트윗 생성하기
Ⅴ Ⅴ Ⅴ
1-5. 트윗 수정하기
Ⅴ Ⅴ Ⅴ
1-6. 트윗 삭제하기
Ⅴ Ⅴ Ⅴ
2-1. 회원가입하기
Ⅴ Ⅴ Ⅴ
2-2. 로그인하기
Ⅴ Ⅴ Ⅴ
2-3. 나 확인하기
Ⅴ Ⅴ Ⅴ
- 폴더정리하기
2. 문제
회원가입시 아이디 중복체크 하기
단, 중복이라면 409를 리턴
- data > auth.js 추가
< data > auth.js >
{
id: '2',
username: "banana",
password: "$2b$10$/4lYMMsFC6rBSxlI1/qP5uwROuFZMtALVttv/2TbQ0bJXIFGeqPXe",
name: "반하나",
email: "banana@banana.com",
}
</ data > auth.js >
- data > auth.js 추가
< data > auth.js >
//아이디 중복검사
export async function findByUsername(username){
return users.find((user) => user.username === user.username);
}
//id 중복검사
export async function findByid(id){
return users.find((user) => user.id === id);
}
</ data > auth.js >
- controllar > auth.js 수정
< controllar > auth.js >
// 회원가입 함수 //수정
export async function signup(req, res, next){
const { username, password, name, email, url } = req.body;
const found = await authRepository.findByUsername(username); //found 추가
if(found){ //if 추가
return res.status(409).json({message:`${username}이 이미 있습니다`})
}
const hashed = await bcrypt.hash(password, bcryptSaltRounds); //추가
const userId = await authRepository.createUser({username, hashed, password,name, email, url});
//중괄호로 user 객체로 받기
const token = createJwtTodken(userId); //토큰 추가
res.status(201).json({token, username}); //if문 삭제
}
< /controllar> auth.js >
- data > auth.js 추가
< data > auth.js >
export async function createUser(user){ //매개변수 변경
const created = {id:'10', ... user} //created 정의
// const user ={
// id: "10",
// username,
// password,
// name,
// email,
// }
users.push(created) //push
return created.id;
}
</ data > auth.js >
h |
- controllar > auth.js 수정
< controllar> auth.js >
// 로그인하는 함수
export async function login(req, res, next){
const { username, password } = req.body;
// const user = await authRepository.login(username); //삭제
const user = await authRepository.findeByUsername(username);
if(!user){
return res.status(401).json({message: `아이디를 찾을 수 없음`});
}
const isValidpassword = await bcrypt.compareSync(password, user.password);
if(!isValidpassword){
return res.status(401).json({message: `비밀번호가 틀렸음`});
}
const token = createJwtTodken(user,id);
res.status(200).json({token, username});
}
// if(bcrypt.compareSync(password, user.password)){
// //res.status(201).json(`${username} 로그인 완료`); //지우기
// res.status(201).header('Token', makeToken(username)).json(`${username} 로그인 완료`); //토큰값만들기 //추가
// }
// else{
// res.status(404).json({message: `${username}님 아이디 또는 비밀번호 확인하세요`})
// }
// }else{
// res.status(404).json({ message: `${username} 님 아이디 또는 비밀번호를 확인해주세요` });
// }
</ controllar> auth.js >
|
- 토큰 사이트 : HTTP authentication - HTTP | MDN
- postman에서 Authorization:Bearer 등록
- controllar > auth.js 삭제 추가
< controllar > auth.js >
//토큰 확인 삭제
//export async function verify(req, res, next){
// const token = req.header['Token'];
// if(token){
// res.status(200).json(token);
// }
//}
//me 추가
export async function me(req, res, next){
const user = await authRepository.findById(req.userId);
if(!user){
return res.status(404).json({message: `일치하는 사용자가 없음`});
}
res.status(200).json({token: req.token, username: user.username});
}
< /controllar> auth.js >
- middleware > auth.js 생성
< middleware > auth.js >
import jwt from 'jsonwebtoken';
import * as authRepository from '../data/auth.js';
const AUTH_ERROR = {message: "인증에러"};
export const isAuth = async(req, res, next) => {
const authHeader = req.get('Authorization');
console.log(authHeader);
}
</ middleware > auth.js >
- router > auth.js 수정
< router > auth.js >
import express from 'express';
import { body } from 'express-validator';
import * as authController from '../controller/auth.js';
import { validate } from '../middleware/validator.js';
import { isAuth } from '../middleware/auth.js'; //추가
const router = express.Router();
const validateSignup = [
body('username').trim().isLength({ min: 3 }).withMessage('최소 3자 이상 입력'),
body('password').trim().isLength({ min: 4 }).withMessage('최소 4자 이상 입력'),
body('email').trim().isEmail().withMessage('이메일 형식 확인'),
validate
]
router.post('/signup', validateSignup, authController.signup);
router.post('/login', authController.login);
router.get('/me', isAuth, authController.me); //수정
export default router;
</ router > auth.js >
[console 창] 토큰이 찍힘 |
- middleware > auth.js 수정
< middleware > auth.js >
import jwt from 'jsonwebtoken';
import * as authRepository from '../data/auth.js';
const AUTH_ERROR = {message: "인증에러"};
export const isAuth = async (req, res, next) => {
const authHeader = req.get('Authorization');
console.log(authHeader);
if(!(authHeader && authHeader.startsWith('Bearer '))){
console.log('에러1');
return res.status(401).json(AUTH_ERROR);
}
const token = authHeader.split(' ')[1];
jwt.verify(
token, 'abcd1234%^&*', async(error, decoded) => {
if(error){
console.log('에러2');
return res.status(401).json(AUTH_ERROR);
}
const user = await authRepository.findById(decoded.id);
if(!user){
console.log('에러3');
return res.status(401).json(AUTH_ERROR);
}
req.userId = user.id;
next();
}
);
}
</ middleware > auth.js >
- router > auth.js 수정
< router > auth.js >
import express from 'express';
import { body } from 'express-validator';
import * as authController from '../controller/auth.js';
import { validate } from '../middleware/validator.js';
import { isAuth } from '../middleware/auth.js'; //추가
const router = express.Router();
//추가
const validateLogin = [
body('username').trim().notEmpty().withMessage('username을 입력하세요'),
body('password').trim().isLength({ min: 4 }).withMessage('password는 최소 4자 이상 입력하세요'), validate
];
//수정
const validateSignup = [
...validateLogin,
body('name').trim().notEmpty().withMessage('최소 3자 이상 입력'),
body('email').isEmail().withMessage('이메일 형식을 확인하세요'),
body('url').isURL().withMessage('URL 형식을 입력하세요'),
validate
];
router.post('/signup', validateSignup, authController.signup);
router.post('/login', validateSignup, authController.login); //수정
router.get('/me', isAuth, authController.me);
export default router;
</router > auth.js >
- router > tweets
< router > tweets >
import express from "express";
import * as tweetController from '../controller/tweet.js';
import { body } from 'express-validator';
import { validate } from "../middleware/validator.js";
import { isAuth } from "../middleware/auth.js" /추가
const router = express.Router();
/*
Post, Put에 text에 대해 빈문자열을 없애고, 최소 3자 이상 입력해야 데이터를 저장하도록 API에 적용
*/
const validateTweet = [
body('text').trim().isLength({min: 3}).withMessage('최소 3자 이상 입력'), validate
]
// 해당 아이디에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets?username=:username
router.get('/', isAuth, tweetController.getTweets); /isAuth추가
// 글번호에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets/:id
router.get('/:id', isAuth, tweetController.getTweet); /isAuth추가
// 트윗하기
// POST
// http://localhost:8080/tweets
// name, username, text
// json 형태로 입력 후 추가된 데이터까지 모두 json으로 출력
router.post('/', isAuth, validateTweet, tweetController.createTweet); /isAuth추가
// 트윗 수정하기
// PUT
// http://localhost:8080/tweets/:id
// id, username, text
// json 형태로 입력 후 변경된 데이터까지 모두 json으로 출력
router.put('/:id', isAuth, validateTweet, tweetController.updateTweet); /isAuth추가
// 트윗 삭제하기
// DELETE
// http://localhost:8080/tweets/:id
router.delete('/:id', isAuth, tweetController.deleteTweet); /isAuth추가
export default router;
</ router > tweets >
|
❗ 전체코드
- controllar 폴더
더보기
- auth.js
import * as authRepository from '../data/auth.js';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { config } from '../config.js'; //추가
//삭제
//const secretKey = "abcd1234%^&*" ;
//const jwtExpiresInDays = '2d';
//const bcryptSaltRounds = 10;
// jwt 토근생성 //수정
function createJwtToken(id){
return jwt.sign({id}, config.jwt.secretKey, {expiresIn: config.jwt.expiresInSec});
}
// 회원가입 함수
export async function signup(req, res, next){
const {username, password, name, email, url} = req.body;
const found = await authRepository.findByUsername(username);
if(found){
return res.status(409).json({message:`${username}이 이미 있습니다`});
}
const hashed = await bcrypt.hash(password, config.bcrypt.saltRounds); //수정
const userId = await authRepository.createUser({username, hashed, name, email, url});
const token = createJwtToken(userId);
res.status(201).json({token, username});
}
// 로그인하는 함수
export async function login(req, res, next){
const {username, password} = req.body;
const user = await authRepository.findByUsername(username);
if(!user){
return res.status(401).json({message : '아이디를 찾을 수 없음'})
}
const isValidpassword = await bcrypt.compareSync(password, user.password);
if(!isValidpassword){
return res.status(401).json({message : `비밀번호가 틀렸음`});
}
const token = createJwtToken(user.id);
return res.status(200).json({token, username});
}
//me
export async function me(req, res, next){
const user = await authRepository.findById(req.userId);
if(!user){
return res.status(404).json({message: `일치하는 사용자가 없음`});
}
res.status(200).json({token: req.token, username: user.username});
}
- tweets.js
import * as tweetRepository from '../data/tweet.js';
// 여러 트윗을 가져오는 함수
export async function getTweets(req, res){
const username = req.query.username;
const data = await (username ? tweetRepository.getAllByUsername(username)
: tweetRepository.getAll());
res.status(200).json(data);
}
// 하나의 트윗을 가져오는 함수
export async function getTweet(req, res, next) {
const id = req.params.id;
const tweet = await tweetRepository.getById(id);
if(tweet){
res.status(200).json(tweet);
}else{
res.status(404).json({message: `${id}의 트윗이 없습니다`})
}
}
// 트윗을 생성하는 함수
export async function createTweet(req, res, next) {
const {text, name, username} = req.body;
const tweet = await tweetRepository.create(text, name, username);
res.status(201).json(tweet);
}
// 트윗을 변경하는 함수
export async function updateTweet(req, res, next) {
const id = req.params.id;
const text = req.body.text;
const tweet = await tweetRepository.update(id, text);
if(tweet){
res.status(201).json(tweet);
}else{
res.status(404).json({message: `${id}의 트윗이 없습니다`})
}
}
// 트윗을 삭제하는 함수
export async function deleteTweet(req, res, next) {
const id = req.params.id;
await tweetRepository.remove(id);
res.sendStatus(204);
}
- data 폴더
더보기
- auth.js
let users = [
{
id: '1',
username: "apple",
password: "$2b$10$/4lYMMsFC6rBSxlI1/qP5uwROuFZMtALVttv/2TbQ0bJXIFGeqPXe",
name: "김사과",
email: "apple@apple.com",
},
{
id: '2',
username: "banana",
password: "$2b$10$/4lYMMsFC6rBSxlI1/qP5uwROuFZMtALVttv/2TbQ0bJXIFGeqPXe",
name: "반하나",
email: "banana@banana.com",
}
];
//아이디 중복검사
export async function findByUsername(username){
return users.find((user) => user.username === user.username);
}
//id 중복검사
export async function findById(id){
return use.find((user) => user.id === id);
}
export async function createUser(user){ //매개변수 변경
const created = {ud:'10', ... user} //created 정의
// const user ={
// id: "10",
// username,
// password,
// name,
// email,
// }
users.push(created) //push
return created.id;
}
export async function login(username){
return users.find((users) => users.username === username);
}
- tweets.js
let tweets = [
{
id: '1',
text: '안녕하세요!',
createdAt: Date.now().toString(),
name: '김사과',
username: 'apple',
},
{
id: '2',
text: '반갑습니다!',
createdAt: Date.now().toString(),
name: '반하나',
username: 'banana',
}
];
// 모든 트윗을 리턴
export async function getAll() {
return tweets;
}
// 해당 아이디에 대한 트윗을 리턴
export async function getAllByUsername(username){
return tweets.filter((tweet) => tweet.username === username)
}
// 글번호에 대한 트윗을 리턴
export async function getById(id){
return tweets.find((tweet) => tweet.id === id);
}
// 트윗을 작성
export async function create(text, name, username){
const tweet = {
id: '10',
text,
createdAt: Date.now().toString(),
name,
username
};
tweets = [tweet, ...tweets];
return tweets;
}
// 트윗을 변경
export async function update(id, text){
const tweet = tweets.find((tweet) => tweet.id === id);
if(tweet){
tweet.text = text;
}
return tweet;
}
// 트윗을 삭제
export async function remove(id){
tweets = tweets.filter((tweet) => tweet.id !== id);
}
- middleware 폴더
더보기
- auth.js
import jwt from 'jsonwebtoken';
import * as authRepository from '../data/auth.js';
const AUTH_ERROR = {message: "인증에러"};
export const isAuth = async (req, res, next) => {
const authHeader = req.get('Authorization');
console.log(authHeader);
if(!(authHeader && authHeader.startsWith('Bearer '))){
console.log('에러1');
return res.status(401).json(AUTH_ERROR);
}
const token = authHeader.split(' ')[1];
jwt.verify(
token, 'abcd1234%^&*', async(error, decoded) => {
if(error){
console.log('에러2');
return res.status(401).json(AUTH_ERROR);
}
const user = await authRepository.findById(decoded.id);
if(!user){
console.log('에러3');
return res.status(401).json(AUTH_ERROR);
}
req.userId = user.id;
next();
}
);
}
- validator.js
import { validationResult } from 'express-validator';
export const validate = (req, res, next) => {
const errors = validationResult(req);
if(errors.isEmpty()){
return next();
}
return res.status(400).json({message:errors.array()[0].msg})
}
- router 폴더
더보기
- auth.js
import express from 'express';
import { body } from 'express-validator';
import * as authController from '../controller/auth.js';
import { validate } from '../middleware/validator.js';
import { isAuth } from '../middleware/auth.js'; //추가
const router = express.Router();
//추가
const validateLogin = [
body('username').trim().notEmpty().withMessage('username을 입력하세요'),
body('password').trim().isLength({ min: 4 }).withMessage('password는 최소 4자 이상 입력하세요'), validate
];
//수정
const validateSignup = [
...validateLogin,
body('name').trim().notEmpty().withMessage('name을 입력하세요'),
body('email').isEmail().withMessage('이메일 형식을 확인하세요'),
body('url').isURL().withMessage('URL 형식을 입력하세요'),
validate
];
router.post('/signup', validateSignup, authController.signup);
router.post('/login', validateSignup, authController.login); //수정
router.get('/me', isAuth, authController.me);
export default router;
- tweets.js
import express from "express";
import * as tweetController from '../controller/tweet.js';
import { body } from 'express-validator';
import { validate } from "../middleware/validator.js";
import { isAuth } from "../middleware/auth.js"
const router = express.Router();
/*
Post, Put에 text에 대해 빈문자열을 없애고, 최소 3자 이상 입력해야 데이터를 저장하도록 API에 적용
*/
const validateTweet = [
body('text').trim().isLength({min: 3}).withMessage('최소 3자 이상 입력'), validate
]
// 해당 아이디에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets?username=:username
router.get('/', isAuth, tweetController.getTweets);
// 글번호에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets/:id
router.get('/:id', isAuth, tweetController.getTweet);
// 트윗하기
// POST
// http://localhost:8080/tweets
// name, username, text
// json 형태로 입력 후 추가된 데이터까지 모두 json으로 출력
router.post('/', validateTweet, isAuth, tweetController.createTweet);
// 트윗 수정하기
// PUT
// http://localhost:8080/tweets/:id
// id, username, text
// json 형태로 입력 후 변경된 데이터까지 모두 json으로 출력
router.put('/:id', validateTweet, isAuth, tweetController.updateTweet);
// 트윗 삭제하기
// DELETE
// http://localhost:8080/tweets/:id
router.delete('/:id', isAuth, tweetController.deleteTweet);
export default router;
- app.js
더보기
import express from "express";
import morgan from "morgan";
import tweetsRouter from './router/tweets.js';
import authRouter from './router/auth.js';
import { config } from './config.js'; //추가
const app = express();
app.use(express.json());
app.use(morgan("dev"));
app.use('/tweets', tweetsRouter);
app.use('/auth', authRouter);
app.use((req, res, next) => {
res.sendStatus(404);
});
//app.listen(8080);//삭제
app.listen(config.host.port);//추가
- bycrpt.js
더보기
import * as bcrypt from "bcrypt";
const password = 'abcd1234';
const hashed = bcrypt.hashSync(password, 10)
//hashSync(매개변수, 솔트값) // 솔트값: 숫자가 높을 수록 속도가 느림 // 10이하 추천
console.log(`password: ${password}, hashed: ${hashed}`);
const result = bcrypt.compareSync('abcd1234',hashed);
console.log(result);
- config.js
더보기
import dotenv from 'dotenv';
dotenv.config();
function required(key, defaultValue=undefined){
const value = process.env[key] || defaultValue
// process.env() : 시스템에 접근 관련한 모듈
// env 다룰 수 있는 기능이 들어있음
// 먼저 전달받은 키가 있는지 확인,
// or 앞의 값이 True로 판별되면 앞의 값이 대입,
// or 앞의 값이 False로 판별되면 뒤의 값이 대입
if(value == null){
throw new Error(`키 ${key}는 undefined!!`);
}
return value;
}
//외부에서 가져다 쓸 수 있게 작성
export const config = {
jwt: {
secretKey: required('JWT_SECRET'),
expiresInSec: parseInt(required('JWT_EXPIRES_SEC', 172800))
},
bcrypt: {
saltRounds: parseInt(required('BCRYPT_SALT_ROUNDS', 10))
},
host: {
port: parseInt(required('HOST_PORT', 8080))
}
}
- jwt.js
더보기
import jwt from 'jsonwebtoken';
const secret='abcdefg1234%^&*';
const token = jwt.sign(
{
id:'apple',
isAdmin:false
},
secret,
{expiresIn: "1h"} // 얼마만큼 토근이 유지될 것인가
);
//expiresIn: "1h"
//expiresIn: 2 유지되다 에러
setTimeout(()=>{
jwt.verify(token,secret,(error,decoded)=>{
console.log(error,decoded);
});
}, 3000); // 3초마다 찍어주기
console.log(token);
// 일단 토큰찍고 4초 뒤에 set timeout에서 토큰, 시크릿 확인해보니 에러
// (4초 지나서 만료시간 지남 jwt expired)
// 2초로 바꾸면 decoded 잘 뜸
- validation.js
더보기
import express from 'express';
import { body, param, validationResult } from "express-validator";
const app = express();
app.use(express.json());
const validate = (req, res, next) => {
const errors = validationResult(req);
if(errors.isEmpty()){
return next();
}
console.log(errors.array());
return res.status(400).json({message: errors.array()[0].msg});
}
app.get('/:email', [param('email').isEmail().withMessage('이메일을 입력하세요!'), validate], (req, res, next) => {
res.send(':연애편지:');
});
//[]: validation 기능을 안에다 쓸 수 있다
// 메서드 체이닝으로 벨리데이션 쓰기
// trim: 공백 없애기
// isLength: 최대최소 설정
// post: 자료를 bbody로 받음
// get: param로 받음
app.post('/users', [
body('name').trim().isLength({min:2}).withMessage('이름은 두글자 이상으로 입력!'),
body('age').isInt().withMessage('나이는 숫자로 입력!'),
body('height').isInt({min:100, max:200}).withMessage('키는 100이상 200이하로 입력하세요!'),
body('job').notEmpty(),
validate
], (req, res, next) => {
console.log(req.body);
res.sendStatus(201);
})
app.listen(8080);
'Web > Node.js' 카테고리의 다른 글
16. ORM (0) | 2024.05.03 |
---|---|
15. 환경변수, 웹소켓 (0) | 2024.05.03 |
13. Authentication, bcrypt, JWT (0) | 2024.04.29 |
12. Validate(데이터 검증) (0) | 2024.04.29 |
11. 리팩토링 (0) | 2024.04.26 |