Node.js 리팩토링, validator
리팩토링
- 소프트웨어 개발 과정에서 코드를 재구성하여 가독성을 높이고 유지보수를 쉽게 만드는 과정이다.
- 코드의 구조를 개선하고 중복을 제거하여 더 나은 설계 패턴을 도입함으로 코드의 품질을 향상시킨다.
- 코드의 기능을 변경하지 않으면서 코드를 개선하는 방법이다.
서버를 실행시킬 파일
import express from "express";
import morgan from "morgan";
import tweetsRouter from "./router/tweets.js";
const app = express();
app.use(express.json());
app.use(morgan("dev"));
app.use("/tweets", tweetsRouter);
app.use((req, res, next) => {
res.sendStatus(404);
});
app.listen(8080);
저번과 같음..
정보를 처리하는 파일
import express from "express";
import * as tweetController from "../controller/tweets.js";
const router = express.Router();
// 해당 아이디에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets?username=:username
router.get("/", tweetController.getTweets);
// 글 번호에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets/:id
router.get("/:id", tweetController.getTweet);
// 트윗하기
// POST
// http://localhost:8080/tweets
// name, username, text
// json 형태로 입력 후 추가된 데이터까지 모두 json으로 출력
router.post("/", tweetController.createTweet);
// 트윗 수정하기
// PUT
// http://localhost:8080/tweets/:id
// id, username, text
// json 형태로 입력 후 변경된 데이터까지 모두 json으로 출력
router.put("/:id", tweetController.updateTweet);
// 트윗 삭제하기
// DELETE
// http://localhost:8080/tweets/:id
router.delete("/:id", tweetController.deleteTweet);
export default router;
controller 폴더를 생성하여 tweets.js 파일을 만들어 준 후 받은 정보를 보내준다.
controller 폴더에 tweets.js파일
import * as tweetRepository from "../data/tweets.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.getAllByid(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.sendSatus(204);
}
위의 코드는 저번에 했던 코드와 비슷하다. 하지만 파일을 세 개로 나눠서 호출하는 방식으로 진행된다는 것만 다르다. 하지만 이런 방식이 이해하기 쉽고 관리도 쉽다고 한다.
모든 트윗을 가져오는 함수는 username을 받아서 data.js파일에 있는 모든 트윗을 리턴하는 getAll와 이름에 해당하는 트윗을 리턴하는 getAllByUsername 함수를 호출하여 username이 존재하면 getAllByUsername에서 이름에 해당하는 트윗이 있다면 그 배열을 data에 할당하여 응답하고, username의 값을 주지 않는다면 모든 트윗을 반환하여 data에 할당 후 응답을 하게 만든 것이다.
하나의 트윗을 가져오는 함수는 id(글번호)를 받아서 data.js파일에 getAllByid함수를 호출하여 같은 id값이 있으면 그 값을 반환받아 tweet 객체에 할당한다. 그리고 조건문을 사용하여 tweet의 값이 있으면 그 값을 응답하여 주고, 없을 경우 message를 보내게 만든 것이다.
트윗을 생성하는 함수는 text, name, username을 받아 data.js파일에 create함수를 호출하여 전개구문의 (...)연산자를 사용하여 추가 후 반환받은 값을 tweet 객체에 할당 후 응답하게 만든 것이다.
트윗을 변경하는 함수는 id를 파라미터로 받고, text를 받아 data.js에 update함수를 호출하여 해당 아이디에 text를 변경하고 반환받은 값을 tweet 객체에 할당 후 응답하게 만든 것이다.
트윗을 삭제하는 함수는 id를 파라미터로 받은 후 data.js에 remove함수를 호출하여 해당 아이디 다른 트윗만 tweets에 제 할당하여 삭제하는 방식이다.
각 함수의 조건문은 트윗이 없는 경우에 message를 전달하도록 한 것이다.
기존 데이터를 갖고 있는 파일
let tweets = [
{
id: "1",
text: "안녕하세요",
createAt: 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: "하이",
createAt: Date.now().toString(),
name: "반하나",
username: "banana",
url: "https://img.freepik.com/premium-vector/banana-cute-kawaii-style-fruit-character-vector-illustration_787461-1772.jpg",
},
];
// 모든 트윗을 리턴
export async function getAll() {
return tweets;
}
// 해당 아이디에 대한 트윗을 리턴
export async function gatAllByUsername(username) {
return tweets.filter((tweet) => tweet.username === username);
}
// 글번호에 대한 트윗을 리턴
export async function gatById(id) {
return tweets.find((tweet) => tweet.id === id);
}
// 트윗을 작성
export async function create(text, name, username) {
const tweet = {
id: "10",
text,
createAt: Date.now().toString(),
name, // name: 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);
}
data.js에 함수들은 받은 값들을 가지고 tweets에서 find, filter, 전개구문 등을 사용하여 tweets 내부의 값을 변경하거나 찾아내어 리턴해주는 방식이다.
그 방식은 저번에 리팩토링하기 전에 만들었던 것과 같다.
파일을 여러 개 사용하여 조금 복잡하다고 느낄 뿐이지 익숙해지면 이 방법이 보기 더 편할 것 같다는 생각이다.
express-validator
- Express.js 를 사용하여 웹 애플리케이션을 개발할 때 입력 데이터의 유효성 검사하기 위한 패키지이다.
isLength(): 문자열 길이 검증
app.get('/:email', [param('email').isLength({min:3}).withMessage('이메일을 3자 이상 입력하세요.!'), validate], (req, res, next) => {
res.send('💌');
});
isEmail(): 이메일 주소의 유효성 검증
app.get('/:email', [param('email').isEmail().withMessage('이메일을 입력하세요.!'), validate], (req, res, next) => {
res.send('💌');
});
isInt(): 숫자의 최소 또는 최댓값 검증
app.get('/:num', [param('num').isInt({min:10, max:100}).withMessage('숫자의 범위는 10이상 100이하로 입력하세요.'), validate], (req, res, next) => {
res.send('🧡');
});
matches(): 정규표현식을 사용하여 문자열의 패턴을 검증
app.get('/:name', [param('name').matches({/^[가-힣]+$/}).withMessage('이름은 한글로 입력하세요.'), validate], (req, res, next) => {
res.send('❔');
});
validator 적용해 보기
- post, put에서 text에 대한 빈문자열을 없애고 최소 3자 이상 입력해야 데이터를 저장하도록 만들기.
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 });
};
validator 파일을 만들어줬다.
import express from "express";
import * as tweetController from "../controller/tweets.js";
import { body } from "express-validator";
import { validate } from "../middleware/validator.js";
const router = express.Router();
const validateTweet = [
body("text").trim().isLength({ min: 3 }).withMessage("최소 3자 이상 입력"),
validate,
];
// 해당 아이디에 대한 트윗 가져오기
// GET
// http://localhost:8080/tweets?username=:username
router.get("/", tweetController.getTweets);
// 글 번호에 대한 트윗 가져오기
// 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;
validateTweet 객체에 조건을 만들어주고 controller로 넘어가기 전에 검사를 한다.
post


text를 3자 미만으로 입력했을 경우 message가 나오는 것을 볼 수 있다.
put


post와 같이 text를 3자 미만 입력했을 때 message가 나오는 것을 볼 수 있다.