본문으로 건너뛰기

Node.js 서버 요청,응답

작성일 : 2024.12.06

1. 기본 설정​

const express = require('express');
const app = express();

// 핵심 미들웨어
app.use(express.json());         // JSON 처리
app.use(cors());                 // CORS 
app.use(helmet());              // 보안
app.use(morgan('dev'));         // 로깅

2. HTTP 요청 처리 방식​

// 1) Query String: /users?page=1&limit=10
app.get('/users', (req, res) => {
    const { page, limit } = req.query;
});

// 2) Path Parameter: /users/123
app.get('/users/:id', (req, res) => {
    const { id } = req.params;
});

// 3) Request Body
app.post('/users', (req, res) => {
    const { name, email } = req.body;
});

// 4) Headers (인증 등)
app.get('/auth', (req, res) => {
    const token = req.headers.authorization;
});

3. 응답 처리​

// 성공 응답
res.status(200).json({
    success: true,
    data: users,
    meta: {
        page: 1,
        total: 100
    }
});

// 에러 응답
res.status(400).json({
    success: false,
    error: {
        code: 'INVALID_INPUT',
        message: '잘못된 입력입니다'
    }
});

4. 실용적인 에러 처리​

class AppError extends Error {
    constructor(message, status) {
        super(message);
        this.status = status;
        this.code = `ERR_${status}`;
    }
}

app.use((err, req, res, next) => {
    console.error(err.stack);
    
    if (err instanceof AppError) {
        return res.status(err.status).json({
            success: false,
            error: {
                code: err.code,
                message: err.message
            }
        });
    }

    res.status(500).json({
        success: false,
        error: {
            code: 'INTERNAL_ERROR',
            message: '서버 오류'
        }
    });
});

5. 유용한 패턴​

// 응답 헬퍼
const response = {
    success: (res, data, status = 200) => {
        res.status(status).json({
            success: true,
            data
        });
    },
    error: (res, error, status = 400) => {
        res.status(status).json({
            success: false,
            error: {
                message: error.message
            }
        });
    }
};

// 유효성 검사
app.post('/users',
    body('email').isEmail(),
    body('password').isLength({ min: 6 }),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return response.error(res, { message: '잘못된 입력' });
        }
    }
);

// 인증 미들웨어
const auth = async (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) {
        return response.error(res, { message: '인증 필요' }, 401);
    }
    try {
        req.user = await verifyToken(token);
        next();
    } catch (error) {
        response.error(res, { message: '유효하지 않은 토큰' }, 401);
    }
};

6. 주요 상태 코드​

  • 200: 성공
  • 201: 생성됨
  • 400: 잘못된 요청
  • 401: 인증 필요
  • 403: 권한 없음
  • 404: 찾을 수 없음
  • 500: 서버 오류

7. 파일 처리​

// 파일 업로드
const multer = require('multer');
const storage = multer.diskStorage({
    destination: 'uploads/',
    filename: (req, file, cb) => {
        cb(null, Date.now() + '-' + file.originalname);
    }
});
const upload = multer({ storage });

app.post('/upload', upload.single('file'), (req, res) => {
    res.json({ file: req.file });
});

// 파일 다운로드
app.get('/download/:filename', (req, res) => {
    res.download(`uploads/${req.params.filename}`);
});

8. 비동기 처리 최적화​

// 에러 캐치 래퍼
const asyncHandler = fn => (req, res, next) =>
    Promise.resolve(fn(req, res, next)).catch(next);

// 병렬 처리
app.get('/dashboard', async (req, res) => {
    const [users, products, orders] = await Promise.all([
        User.find(),
        Product.find(),
        Order.find()
    ]);
    res.json({ users, products, orders });
});

9. 라우터 모듈화​

// users.routes.js
const router = express.Router();

router.get('/', getUsers);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);

// app.js
app.use('/api/users', userRouter);

10. 미들웨어 체인​

app.post('/users',
    auth,                    // 인증
    validate,                // 유효성 검사
    checkDuplicates,        // 중복 확인
    asyncHandler(createUser) // 생성
);

11. 환경 설정​

require('dotenv').config();

const config = {
    port: process.env.PORT || 3000,
    dbUri: process.env.MONGODB_URI,
    jwtSecret: process.env.JWT_SECRET
};

12. 캐싱​

const cache = require('memory-cache');

app.get('/data', async (req, res) => {
    const cached = cache.get('data');
    if (cached) {
        return res.json(cached);
    }
    
    const data = await fetchData();
    cache.put('data', data, 5 * 60 * 1000); // 5분
    res.json(data);
});