Express 개발 강좌, 사용자 정의 미들웨어 만들기

Express는 Node.js를 기반으로 한 웹 애플리케이션 프레임워크로, 강력한 미들웨어 시스템을 제공합니다. 이 강좌에서는 Express의 미들웨어를 이해하고, 사용자 정의 미들웨어를 만드는 방법에 대해 자세히 설명하겠습니다. 사용자 정의 미들웨어는 애플리케이션의 요구 사항에 맞추어 기능을 확장할 수 있는 매우 유용한 도구입니다.

1. Express와 미들웨어의 이해

미들웨어는 요청(Request)과 응답(Response) 객체를 처리하는 함수입니다. Express에서는 미들웨어를 통해 요청을 가로채고 변경하거나, 요청에 대한 응답을 처리할 수 있습니다. 미들웨어는 다음과 같은 용도로 사용될 수 있습니다:

  • 요청 로그 기록
  • 요청의 인증 및 권한 부여
  • 요청 데이터 파싱
  • 정적 파일 제공
  • 오류 처리

1.1 미들웨어의 기본 구조

미들웨어 함수는 다음과 같은 기본 형식을 가집니다:

function middleware(req, res, next) {
    // 요청 처리 로직
    next(); // 다음 미들웨어로 이동
}

여기서:

  • req: 요청 객체로, 클라이언트의 요청 정보를 담고 있습니다.
  • res: 응답 객체로, 서버가 클라이언트에게 보낼 응답 정보를 담고 있습니다.
  • next: 다음 미들웨어로 제어를 넘기는 함수입니다. 이 함수를 호출하지 않으면 요청이 응답을 받지 못하고 대기 상태에 빠집니다.

2. 사용자 정의 미들웨어 만들기

사용자 정의 미들웨어는 애플리케이션의 특정 요구 사항을 충족하기 위해 개발자가 직접 작성하는 미들웨어입니다. 이번 섹션에서는 몇 가지 예제를 통해 사용자 정의 미들웨어를 만드는 방법을 알아보겠습니다.

2.1 기본 사용자 정의 미들웨어 예제

우선, 간단한 사용자 정의 미들웨어를 만들어 보겠습니다. 이 미들웨어는 모든 요청에 대해 콘솔에 로그를 남기는 역할을 합니다.

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

// 사용자 정의 미들웨어
function requestLogger(req, res, next) {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
    next();
}

// 미들웨어 등록
app.use(requestLogger);

// 기본 경로
app.get('/', (req, res) => {
    res.send('Welcome to the Express App!');
});

// 서버 시작
app.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});

위의 코드를 실행하면 모든 요청에 대해 요청 시간과 메서드, URL이 로그에 출력됩니다. app.use(requestLogger);를 통해 미들웨어를 등록하고, 미들웨어가 요청을 처리한 후 next();를 호출하여 다음 단계로 진행합니다.

2.2 요청 데이터 파싱 미들웨어

이번에는 요청 본문(body)을 파싱하는 사용자 정의 미들웨어를 만들어 보겠습니다. 이 미들웨어는 JSON 형식의 데이터를 받아서 요청 객체에 추가합니다.

function bodyParser(req, res, next) {
    let body = '';
    req.on('data', chunk => {
        body += chunk.toString(); // 데이터 조각을 문자열로 변환
    });
    req.on('end', () => {
        req.body = JSON.parse(body); // 요청 본문을 JSON으로 파싱
        next(); // 다음 미들웨어 또는 라우터로 이동
    });
}

// 미들웨어 등록
app.use(bodyParser);

// POST 경로
app.post('/data', (req, res) => {
    res.send(`Received data: ${JSON.stringify(req.body)}`);
});

위 코드에서는 req.on('data')req.on('end') 이벤트를 사용하여 요청 본문을 수집하고 JSON 형식으로 파싱합니다. 이 미들웨어를 사용하기 위해서 덧붙여 app.use(bodyParser);를 등록합니다.

2.3 인증 미들웨어

다음은 인증을 위한 미들웨어입니다. 사용자가 특정 경로에 접근할 때 유효한 토큰을 가진 경우에만 접근을 허용합니다.

function authMiddleware(req, res, next) {
    const token = req.headers['authorization'];
    
    if (token === 'valid_token') {
        next(); // 인증 성공, 다음 미들웨어로 이동
    } else {
        res.status(403).send('Forbidden: Invalid token'); // 인증 실패
    }
}

// 인증 미들웨어 등록
app.use('/protected', authMiddleware);

// 보호된 경로
app.get('/protected/data', (req, res) => {
    res.send('This is protected data');
});

인증 미들웨어에서는 요청 헤더에서 authorization 필드를 확인하고, 유효한 토큰이 아닐 경우 인증을 거부합니다. app.use('/protected', authMiddleware);를 통해 보호된 경로에 미들웨어를 적용합니다.

3. 에러 처리 미들웨어

Express에서는 에러 처리 미들웨어를 따로 구성하여 애플리케이션의 오류를 중앙에서 관리할 수 있습니다. 에러 처리 미들웨어는 네 개의 매개변수를 받아야 하며, 주로 마지막에 넣어주는 것이 좋습니다.

function errorHandler(err, req, res, next) {
    console.error(err.stack); // 에러 스택을 콘솔에 출력
    res.status(500).send('Something broke!'); // 사용자에게 에러 메시지 전송
}

// 에러 처리 미들웨어 등록
app.use(errorHandler);

위의 코드는 애플리케이션 내에서 발생한 모든 오류를 처리합니다. 오류가 발생하면 errorHandler 미들웨어가 호출되어 오류 로그를 출력하고 사용자에게 적절한 메시지를 보냅니다.

4. 사용자 정의 미들웨어의 활용

이제 몇 가지 사용자 정의 미들웨어를 만들었으니, 이들을 조합하여 더 복잡한 기능을 구현해 볼 수 있습니다.

4.1 미들웨어 체인

여러 미들웨어를 체이닝하여 요청 처리 흐름을 제어할 수 있습니다. 예를 들어, 인증과 요청 데이터를 함께 처리할 수 있습니다.

app.use(authMiddleware);
app.use(bodyParser);

app.post('/secure-data', (req, res) => {
    res.send(`Secure data received: ${JSON.stringify(req.body)}`);
});

위 코드는 /secure-data 경로에 대해 인증 미들웨어와 본문 파싱 미들웨어를 순차적으로 적용합니다. 인증이 성공한 후에만 요청 본문을 파싱하여 그 결과를 응답합니다.

5. 성능 최적화 및 주의사항

사용자 정의 미들웨어를 개발할 때 성능과 관련된 몇 가지 주의사항이 있습니다:

  • 미들웨어는 가능한 적게 사용하여 요청 처리 성능에 미치는 영향을 최소화해야 합니다.
  • 미들웨어가 요구된 작업을 수행하는 데 필요한 최소한의 로직만 포함되어야 합니다.
  • 소셜 미디어와 애플리케이션 간의 데이터 흐름을 제어하기 위해 불필요한 계산을 피해야 합니다.

5.1 비동기 처리

비동기 코드를 작성할 경우, async/await를 사용하여 미들웨어를 작성할 수 있습니다. 이럴 경우에는 next() 호출 대신 직접 오류를 throw하여 에러 처리 미들웨어에 전달할 수 있습니다.

async function asyncMiddleware(req, res, next) {
    try {
        // 비동기 작업 수행
        const result = await someAsyncFunction();
        req.data = result; // 결과를 요청 객체에 저장
        next();
    } catch (err) {
        next(err); // 오류를 에러 처리 미들웨어로 전달
    }
}

6. 결론

Express에서 사용자 정의 미들웨어를 만드는 것은 요청을 처리하고 기능을 확장하는 강력한 방법입니다. 이 강좌를 통해 기본적인 미들웨어 생성 방법, 비동기 처리, 인증, 에러 처리 등을 살펴보았습니다. 이러한 기술들을 결합하여 보다 복잡하고 유용한 웹 애플리케이션을 개발할 수 있습니다.

미들웨어는 애플리케이션의 구조를 깔끔하게 유지하면서도 필요한 기능을 쉽게 추가하는 데 도움을 줍니다. 여러분의 Express 애플리케이션에서 사용자 정의 미들웨어를 적극 활용하여 보다 나은 웹 서비스를 구축하시기 바랍니다.