웹 애플리케이션을 개발할 때, 성능은 항상 중요한 고려 사항입니다. 특히 백엔드 개발에서 Node.js와 MySQL 조합을 통해 웹 애플리케이션의 성능을 최적화하는 방법에 대한 경험담을 공유하고자 합니다.

프로젝트 배경

저는 최근에 중소기업의 프로젝트를 맡아서 진행하게 되었는데, 이 웹 애플리케이션은 사용자들이 게시물을 작성하고, 코멘트를 달며, 서로 소통할 수 있는 플랫폼이었습니다. 초기 단계에서 시스템의 성능 문제로 인해 사용자 경험이 저하되는 상황이 발생했습니다. 이에 따라 Node.js와 MySQL을 활용하여 성능을 개선하기로 결심했습니다.

1. 데이터베이스 설계 최적화

성능 향상의 첫 단계는 데이터베이스 설계의 최적화였습니다. MySQL에서 데이터베이스를 설계할 때, 1:N 관계를 고려하여 테이블 구조를 설정했습니다. 예를 들어, 사용자와 게시물, 그리고 게시물과 댓글 간의 관계를 명확히 정의했습니다.

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE comments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    post_id INT NOT NULL,
    user_id INT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (post_id) REFERENCES posts(id),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

이 구조를 통해 데이터의 무결성을 보장하고, 쿼리의 성능을 높일 수 있었습니다.

2. 인덱싱을 통한 쿼리 성능 향상

다음으로, 자주 조회되는 컬럼에 인덱스를 추가하여 쿼리 성능을 향상시켰습니다. 특히 posts 테이블의 created_at 컬럼과 user_id 컬럼에 인덱스를 추가했습니다.

ALTER TABLE posts ADD INDEX idx_user_id (user_id);
ALTER TABLE posts ADD INDEX idx_created_at (created_at);

이러한 인덱스는 검색 속도를 비약적으로 향상시키며, 특히 사용자가 게시물을 작성한 시간순으로 조회할 때 큰 효과를 보였습니다.

3. Node.js의 비동기 처리

Node.js의 장점을 활용하여 비동기 처리를 통해 성능을 높였습니다. 예를 들어, 사용자가 게시물을 작성할 때, 게시물과 댓글을 동시에 삽입하는 쿼리를 비동기적으로 처리하였습니다.

const mysql = require('mysql2/promise');

async function createPost(userId, title, content) {
    const connection = await mysql.createConnection({ / DB Configuration / });
    
    try {
        await connection.beginTransaction();

        const [postResult] = await connection.execute(
            'INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)',
            [userId, title, content]
        );

        const postId = postResult.insertId;

        await connection.execute(
            'INSERT INTO comments (post_id, user_id, content) VALUES (?, ?, ?)',
            [postId, userId, '첫 댓글입니다!']
        );

        await connection.commit();
    } catch (error) {
        await connection.rollback();
        throw error;
    } finally {
        await connection.end();
    }
}

이 코드는 데이터베이스 트랜잭션을 활용하여 안정성을 높이고, 비동기 처리를 통해 빠른 응답을 제공합니다.

4. 캐싱 전략 도입

마지막으로, Redis와 같은 캐시 서버를 도입하여 자주 조회되는 데이터를 캐싱함으로써 데이터베이스의 부하를 줄이기로 했습니다. 예를 들어, 사용자의 게시물 목록을 조회할 때, 캐시를 통해 빠르게 응답할 수 있도록 했습니다.

const redis = require('redis');
const redisClient = redis.createClient();

async function getPosts(userId) {
    const cacheKey = user:${userId}:posts;
    const cachedPosts = await redisClient.get(cacheKey);

    if (cachedPosts) {
        return JSON.parse(cachedPosts);
    }

    const connection = await mysql.createConnection({ / DB Configuration / });
    const [posts] = await connection.execute(
        'SELECT * FROM posts WHERE user_id = ? ORDER BY created_at DESC',
        [userId]
    );

    await redisClient.set(cacheKey, JSON.stringify(posts), 'EX', 3600); // 1시간 캐시
    await connection.end();

    return posts;
}

이렇게 캐싱을 통해 데이터베이스 쿼리 수를 줄이고, 사용자에게 더 빠른 응답을 제공할 수 있었습니다.

마무리

Node.js와 MySQL을 조합하여 웹 애플리케이션의 성능을 향상시키는 과정은 여러 방면에서의 최적화가 필요했습니다. 데이터베이스 설계의 최적화, 인덱싱, 비동기 처리, 캐싱 전략 등 다양한 방법을 통해 성능을 개선할 수 있었습니다. 이러한 경험을 바탕으로 앞으로도 지속적인 성능 개선을 위해 노력할 것입니다.