본문 바로가기

JavaScript/Node JS

Java Script(12)-Refactoring(리팩토링), validate(데이터 검증)

*이 글을 읽기전에 작성자 개인의견이 있으니, 다른 블로그와 교차로 읽는것을 권장합니다.*

1. 리팩토링

- 소프트웨어 개발 과정에서 코드를 재구성하여 가독성을 높이고, 유지보수를 쉽게 만드는 과정입니다.
- 코드의 구조를 개선하고 중복을 제거하여 더 나은 설계 패턴을 도입함으로, 코드의 품질을 향상시킵니다.
- 코드의 기능을 변경하지 않으면서, 코드를 개선하는 방법입니다.

미들웨어 tweets.파일을 가져와서 router폴더의 tweets.js파일을 data폴더로 만들어 가져옵니다. data > tweets.js파일안에 미들웨어 방식을 실행시키는 함수를 추가해줍니다.

// data > tweets.js 파일
let tweets = [
    {
        id: '1',
        text: '안녕하세요!',
        createdAt: Date.now().toString(),
        name: '김사과',
        username: 'apple',
        url: 'https://www.logoyogo.com/web/wp-content/uploads/edd/2021/02/logoyogo-1-45.jpg'
    },
    {
        id: '2',
        text: '반갑습니다!',
        createdAt: Date.now().toString(),
        name: '반하나',
        username: 'banana',
        url: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTJSRyel4MCk8BAbI6gT_j4DBTEIcY0WW4WWfoklymsWA&s'
    }
];

// 모든 트윗을 리턴
export async function getAll() {
    return tweets;
}

// 해당 아이디에 대한 트윗을 리턴
export async function getAllByUsername(username){
    return tweets.find((tweet) => tweet.username === username)
}

// 글번호에 대한 트윗을 리턴
export async function getById(id){
    return tweets.find((tweet)=> tweet.id === id);
}

// 트윗을 작성, js에서 키와 value값이 같으면 1번만 써도 됨
export async function create(text, name, username){
    const tweet = {
        id: '10',
        text,
        createAt: 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);
}

router > tweets.js에 있는 파일을 controller 폴더로 만들어 분리합니다. 

// controller > 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);
}

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";

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('/', tweetController.getTweet);


// 글번호에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets/:id
router.get('/:id', tweetController.getTweet);


// 트윗하기
// POST
// http://localhost:8080/tweets
// name, username, text
// json 형태로 입력 후 추가된 데이터까지 모두 json으로 출력
router.post('/', validateTweet, tweetController.createTweet);


// 트윗 수정하기
// PUT
// http://localhost:8080/tweets/:id
// id, username, text
// json 형태로 입력 후 변경된 데이터까지 모두 json으로 출력
router.put('/:id', validateTweet, tweetController.updateTweet);


// 트윗 삭제하기
// DELETE
// http://localhost:8080/tweets/:id:
router.delete('/:id', 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';

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);

파일은 data->controller->router->app.js 순으로 실행됩니다. 

 

2. Validate

express-validator

Express.js를 사용하여 웹 애플리케이션을 개발할 때, 입력 데이터의 유효성을 검사하기 위한 패키지입니다. 라이브러리 안의 모듈을 메서드를 통해 제한을 걸어줍니다.

express-validator 설치하기
npm i express-validator

미들웨어에 GET요청을  걸어둘 validation.js파일을 만듭니다.

// 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();
    }
	return res.status(400).json({message:errors.array()[0].msg});
};

app.get('/:email', [param('email').isEmail().withMessage('이메일을 입력하세요!'), validate], (req, res, next) => {
	res.send('💌');
});

app.listen(8080);

const validation을 통해, 오류가 발생할 경우 해당 오류 메시지(400)를 클라이언트에 반환하고, 에러가 없을 경우 next()로 미들웨어로 넘어가게 만듭니다. 엔드포인트를 '/:email'로 설정하고, 미들웨어 요청방식을 get으로 설정합니다. 이후 미들웨어 유효성검사를 위해 validate를 추가합니다.  

// middleware > 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});
}


isLength(): 문자열 길이를 검증합니다. 최솟값, 최댓값을 넣어 설정 가능합니다.

 param('email').isLength( {min:7})
 
app.get('/:email', [param('email').isLength({min:3})
.withMessage('이메일은 3자이상 입력하세요!'), validate], (req,res,next) => {
	res.send('💌');
});

isEmail(): 이메일 주소의 유효성 검증합니다.

 param('email').isEmail()
 
app.get('/:email', [param('email').isEmail()
.withMessage('이메일을 입력하세요!'), validate], (req,res,next) => {
	res.send('💌');
});

isInt(): 숫자의 최소 또는 최댓값을 검증합니다.

body('age').isInt()

app.get('/:email', [param('email').isInt({min:10, max:100})
.withMessage('숫자의 범위는 10이상 100이하로 입력하세요!'), validate], (req,res,next) => {
res.send('❤');
});

matches(): 정규표현식을 사용하여 문자열의 패턴을 검증합니다.

param('name').maches()

app.get('/:name', [param('name').matches(/^[가-힣]+$/)
.withMessage('이름은 한글로 입력하세요!'), validate], (req,res,next) => {
	res.send('💌');
});

trim(): 공백을 제거합니다.

 param('email').trim()

withMessage(): 문자를 보냅니다.

param('email').withMessage('이메일은 7글자 이상!')]

notEmpty(): 빈칸아닌 형태인지 확인합니다.

param('email').notEmpty()]

 

/users 엔드포인트에 미들웨어 post방식으로 요청하기 위해 validation.js파일을 만듭니다.

// validation.js 파일
app.post('/users',[
	body('name').trim().isLength({min:2}).withMessage('이름은 두 글자 이상으로 입력!'),
	body('age').isInt().withMessage('나이는 숫자로 입력!'),
	body('email').isEmail().withMessage('이메일을 입력!'),
	body('job.name').notEmpty(), validate],
    (req,res,next)=>{
    	console.log(req.body);
		res.sendStatus(201);
});

app.listen(8080);

 

app.js 파일을 다시 변경합니다.

// app.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('💌');
});

app.post('/users', [
    body('name').trim().isLength({min:2}).withMessage('이름은 두글자 이상으로 입력!'),
    body('age').isInt({min:0, max:100}).withMessage('나이는 숫자로 입력!'),
    body('height').isInt().withMessage('키는 100이상 200이하로 입력하세요!'),
    body('job').notEmpty(),
    validate
], (req,res, next) =>{
    console.log(req.body);
    res.sendStatus(201);
})

app.listen(8080);



문제
Post, Put에 text에 대해 빈문자열을 없애고, 최소 3자 이상 입력해야 데이터를 저장하도록 API에 적용

// middle > 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});
}