기술 학습

Express To-Do List 프로젝트 코드 리뷰

hawon6691 2026. 1. 29. 13:04
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 (즉시 수정)

  1. Line 58: pool.quertpool.query 오타 수정

🟡 Medium Priority (권장)

  1. 세미콜론 일관성 유지
  2. 코드 스타일 통일 (ESLint/Prettier)
  3. 에러 메시지 상수화

🟢 Low Priority (선택적)

  1. 라우터 분리
  2. 미들웨어로 공통 로직 추출
  3. 입력 검증 강화

💡 추가 제안

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