728x90
Express To-Do List 프로젝트 코드 리뷰
🎯 전체 평가
Overall Score: 8.5/10
Express를 사용한 RESTful API 구현이 잘 되어 있으며, 전반적인 코드 품질이 양호합니다. 다만 치명적인 오타 1개와 몇 가지 개선 가능한 부분이 있습니다.
🐛 Critical Issues (즉시 수정 필요)
⚠️ 1. 치명적인 오타 발견 - Line 58
const [todos] = await pool.quert("SELECT * FROM todos WHERE id = ?", [id]);
// ^^^^^ 오타!
문제점:
pool.query가 아닌pool.quert로 작성되어 있음- 이 오타로 인해 PUT 요청 시 서버가 크래시됨
수정:
const [todos] = await pool.query("SELECT * FROM todos WHERE id = ?", [id]);
✅ 잘된 부분
1. Express 미들웨어 활용
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(express.static(__dirname));
- JSON 파싱과 정적 파일 제공이 깔끔하게 구현됨
2. SQL Injection 방지
await pool.query("SELECT * FROM todos WHERE id = ?", [id]);
- Prepared Statement를 사용하여 SQL Injection 공격 방지
3. 적절한 HTTP 상태 코드 사용
200 OK- 조회, 수정 성공201 Created- 생성 성공204 No Content- 삭제 성공400 Bad Request- 입력 오류404 Not Found- 리소스 없음500 Internal Server Error- 서버 오류
4. 입력 검증
if(!content || content.trim() === "") {
return res.status(400).json({error:"할 일 내용을 입력해주세요."});
}
- 빈 값 체크 및 trim() 처리
5. 비즈니스 로직 구현
if(todo.completed) {
return res.status(400).json({error: "완료된 항목은 수정할 수 없습니다."});
}
- 완료된 항목 수정 방지 로직
6. 에러 핸들링
- 모든 라우트에서 try-catch 사용
- 404 핸들러 구현
🔧 개선 가능한 부분
1. 세미콜론 일관성 부족 - Line 16
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "index.html"));
}) // 세미콜론 없음
개선안:
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "index.html"));
}); // 세미콜론 추가
2. 코드 스타일 일관성
// 일부는 공백이 없음
try{
const [rows] = await pool.query...
// 일부는 공백이 있음
try {
const {content} = req.body;
개선안: ESLint/Prettier 사용 권장
3. 반복되는 코드 (DRY 원칙)
모든 라우트에서 동일한 패턴 반복:
const [todos] = await pool.query("SELECT * FROM todos WHERE id = ?", [id]);
if(todos.length === 0) {
return res.status(404).json({error: "할 일을 찾을 수 없습니다."});
}
개선안:
// 미들웨어로 분리
async function findTodoById(req, res, next) {
const {id} = req.params;
const [todos] = await pool.query("SELECT * FROM todos WHERE id = ?", [id]);
if(todos.length === 0) {
return res.status(404).json({error: "할 일을 찾을 수 없습니다."});
}
req.todo = todos[0];
next();
}
// 사용
app.put("/todos/:id", findTodoById, async (req, res) => {
const todo = req.todo;
// ...
});
4. 데이터베이스 쿼리 최적화
수정/완료 토글 후 다시 SELECT 하는 부분:
await pool.query("UPDATE todos SET content = ? WHERE id = ?", [content.trim(), id]);
const [updatedTodos] = await pool.query("SELECT * FROM todos WHERE id = ?", [id]);
개선안:
- 이미 가져온 데이터를 활용하거나
- MySQL의
RETURNING구문 사용 고려 (단, MySQL 5.7+에서는 미지원)
5. 매직 넘버/문자열
에러 메시지가 하드코딩되어 있음:
res.status(500).json({error: "서버 오류가 발생했습니다."});
개선안:
// constants.js
const ERROR_MESSAGES = {
SERVER_ERROR: "서버 오류가 발생했습니다.",
NOT_FOUND: "할 일을 찾을 수 없습니다.",
EMPTY_CONTENT: "할 일 내용을 입력해주세요.",
CANNOT_EDIT_COMPLETED: "완료된 항목은 수정할 수 없습니다."
};
module.exports = { ERROR_MESSAGES };
6. 라우터 분리
모든 라우트가 app.js에 있어 파일이 길어짐
개선안:
// routes/todos.js
const express = require('express');
const router = express.Router();
router.get('/', async (req, res) => { /* ... */ });
router.post('/', async (req, res) => { /* ... */ });
// ...
module.exports = router;
// app.js
const todosRouter = require('./routes/todos');
app.use('/todos', todosRouter);
7. 입력 검증 강화
현재는 빈 값만 체크:
if(!content || content.trim() === "") { ... }
개선안:
// 길이 제한 추가
const MAX_CONTENT_LENGTH = 500;
if(!content || content.trim() === "") {
return res.status(400).json({error:"할 일 내용을 입력해주세요."});
}
if(content.length > MAX_CONTENT_LENGTH) {
return res.status(400).json({error:`할 일은 최대 ${MAX_CONTENT_LENGTH}자까지 입력 가능합니다.`});
}
📊 보안 체크리스트
| 항목 | 상태 | 비고 |
|---|---|---|
| SQL Injection 방지 | ✅ | Prepared Statement 사용 |
| XSS 방지 | ⚠️ | 프론트엔드에서 처리 필요 |
| CSRF 방지 | ❌ | 필요시 csurf 패키지 추가 |
| 입력 검증 | ✅ | 기본적인 검증 완료 |
| 에러 핸들링 | ✅ | 모든 라우트에서 try-catch |
| 환경변수 관리 | ✅ | .env 파일 사용 |
🎨 코드 스타일 권장사항
// .eslintrc.json 설정 예시
{
"extends": "eslint:recommended",
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"],
"indent": ["error", 2],
"space-before-blocks": "error"
}
}
📝 우선순위별 수정 사항
🔴 High Priority (즉시 수정)
- Line 58:
pool.quert→pool.query오타 수정
🟡 Medium Priority (권장)
- 세미콜론 일관성 유지
- 코드 스타일 통일 (ESLint/Prettier)
- 에러 메시지 상수화
🟢 Low Priority (선택적)
- 라우터 분리
- 미들웨어로 공통 로직 추출
- 입력 검증 강화
💡 추가 제안
1. 환경 분리
// config/database.js
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT || 3306,
waitForConnections: true,
connectionLimit: 10
});
module.exports = pool;
2. Logger 추가
// 개발: console.log
// 프로덕션: winston, morgan 등 사용
const logger = require('./utils/logger');
logger.error('할 일 조회 오류:', error);
3. 테스트 코드 작성
// tests/todos.test.js (Jest)
describe('POST /todos', () => {
it('should create a new todo', async () => {
const res = await request(app)
.post('/todos')
.send({ content: '테스트 할 일' });
expect(res.status).toBe(201);
expect(res.body).toHaveProperty('id');
});
});
🏆 최종 평가
강점
- ✅ RESTful API 설계 원칙 준수
- ✅ SQL Injection 방지
- ✅ 적절한 HTTP 상태 코드 사용
- ✅ 에러 핸들링 구현
- ✅ Express 미들웨어 활용
약점
- ❌ 치명적인 오타 1개 (즉시 수정 필요)
- ⚠️ 코드 스타일 불일치
- ⚠️ 반복되는 코드
- ⚠️ 라우터 미분리
728x90
'기술 학습' 카테고리의 다른 글
| express.Router() 란? (0) | 2026.01.29 |
|---|---|
| MVC란? (0) | 2026.01.29 |
| XSS(Cross-Site Scripting) 취약점이란 (0) | 2026.01.29 |
| Fetch API란 (0) | 2026.01.28 |
| 비동기(Asynchronous)란 (0) | 2026.01.28 |