1장. 데이터베이스 접근 방식의 시작
초기의 애플리케이션에서 데이터베이스 접근 방식은 단순했다.
필요한 데이터를 가져오기 위해 SQL을 직접 작성하고 실행하는 형태였다.
const user = await db.query("SELECT * FROM users WHERE id = 1");
데이터가 필요하면 SQL을 작성하고, 실행 결과를 그대로 사용하는 방식이다.
별도의 구조 없이도 바로 사용할 수 있었기 때문에 자연스럽게 널리 사용되었다.
코드가 늘어나면서
기능이 하나씩 추가되기 시작하면
비슷한 형태의 쿼리들이 점점 늘어나기 시작한다.
await db.query("SELECT * FROM users WHERE id = 1");
await db.query("SELECT * FROM users WHERE email = 'test@test.com'");
await db.query("SELECT * FROM users WHERE status = 'ACTIVE'");
조건만 조금씩 다른 쿼리들이 반복되면서
SQL 문자열이 코드 곳곳에 퍼지게 된다.
이 시점까지는 아직 큰 문제가 없어 보인다.
로직 안으로 들어오는 SQL
단순 조회를 넘어서
조건에 따라 조회 로직이 달라지기 시작하면
SQL은 점점 문자열을 조합하는 형태로 변하게 된다.
let query = "SELECT * FROM users WHERE 1=1";
if (email) {
query += ` AND email = '${email}'`;
}
if (status) {
query += ` AND status = '${status}'`;
}
if (isActive) {
query += ` AND is_active = 1`;
}
const users = await db.query(query);
처음에는 유연하게 조건을 처리할 수 있는 방법처럼 보이지만,
조건이 늘어날수록 코드의 복잡도는 빠르게 증가한다.
점점 드러나는 문제
이 방식은 자연스럽게 몇 가지 문제를 만들어낸다.
- 문자열 조합 실수 (공백, AND 누락 등)
- SQL Injection 위험
- 조건이 많아질수록 가독성 저하
- 디버깅 어려움
그리고 중요한 변화가 하나 생긴다.
👉 데이터 조회 코드가 점점 복잡해지기 시작한다
코드의 관심사가 뒤섞인다
처음에는 단순히 데이터를 가져오기 위한 코드였지만,
이제는 SQL을 어떻게 구성하느냐가 더 중요한 코드가 된다.
결과적으로 하나의 코드 안에
- 어떤 데이터를 가져올지에 대한 로직과
- SQL을 조합하는 로직이
함께 섞이게 된다.
이 시점부터는
👉 비즈니스 로직
👉 데이터 접근 로직
이 명확하게 구분되지 않는다.
다음 단계로의 흐름
이 문제를 해결하기 위해
SQL을 더 안전하고 구조적으로 다루려는 시도가 등장한다.
문자열을 직접 조합하는 방식에서 벗어나
👉 더 나은 형태로 데이터 접근을 구성하려는 흐름이 만들어지기 시작한다.