
ORM(Object-Relational-Mapping)을 사용하면 일반적으로 쿼리 작성과 데이터베이스와의 상호작용이 훨씬 편리하지만, 복잡한 쿼리의 경우에는 ORM만으로는 처리하기 어려울 수 있다. 이러한 상황에서 다음과 같은 방법들을 고려해 볼 수 있다.
예를 들어 내가 자주 사용하는 TypeORM에서 제공하는 Raw SQL기능을 활용하여 해결할 수 있는데 이를 통해 추상화 기능을을 벗어나 데이터베이스에 직접 SQL 쿼리를 실행할 수 있으며 이는 성능 최적화나 복잡한 쿼리를 처리해야 하는 상황에서 유용하다.
TypeORM에서는 EntityManager의 query메소드를 사용하여 Raw SQL쿼리를 실행할 수 있다.
async function executeComplexQuery() {
const entityManager = getManager();
const results = await entityManager.query(`
SELECT u.*, o.order_count
FROM users u
LEFT JOIN (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
) o ON u.id = o.user_id
WHERE u.active = true
`);
console.log(results);
}
SQL Injection성능 최적화Select 필드 최소화Join 사용 최소화lazy loading, eager loading 같은 기능을 적절히 활용하는 것도 중요하다.인덱스 활용Lazy loading, Eager loading이란?
lazy loading
- 연관된 엔티티나 컬렉션을 실제로 사용하는 순간까지 데이터 로딩을 지연시키는 방법이다., 즉 처음에 엔티티를 로드할 때는 연관된 데이터를 로드하지 않고, 해당 데이터에 접근할 때 비로소 해당 데이터를 로드하는 방식이다.
Eager loading
- 연관된 엔티티나 컬렉션을 최초의 엔티티 로드 시에 함께 로드하는 방법이다. 즉, 처음부터 필요한 모든 데이터를 사전에 로드하는 방식으로, 연관 데이터에 별도의 쿼리 없이 즉시 접근할 수 있다.
데이터베이스 쿼리 결과를 캐싱하여 재사용함으로써 데이터베이스 부하를 줄이고 성능을 향상시킬 수 있다. ORM에서 제공하는 캐싱 기능을 활용하거나, 외부 캐시 시스템 ( Redis ) 를 도입할 수 있다.
복잡한 쿼리가 발생하는 원인 중 하나는 비즈니스 로직이 데이터베이스 쿼리 내에 과도하게 포함되어 있는 경우이다. 비즈니스 로직을 데이터베이스 쿼리와 분리하여, 가능한 한 애플리케이션 레벨에서 처리하도록 한다.
사실상 가장 중요하고도 많이 투자를 하게 될 부분이라고 생각한다 애플리케이션의 성능을 주기적으로 모니터링하고 분석하여 병목 지점을 찾아내고 개선한다. 이 과정에서 ORM이 생성하는 쿼리를 확인하고, 필요한 경우 최적화를 수행할 수 있다.
ORM을 사용하면 개발 생산성이 향상되지만, 성능 문제가 발생할 수 있으므로 적절한 최적화 전략과 함께 사용하는 것이 중요하다. 필자가 프로젝트 중 겪었던 N+1 쿼리 문제등도 이처럼 ORM에서 제공하는 Query Builder로 sql 쿼리를 동적으로 작성할 수 있는 기능을 사용하여 복잡한 쿼리를 더욱 간단하게 작성하는 방식으로 해결한 바 있었다.