Show GN: SQLBraid - SQL을 그대로 쓰는 TypeScript 라이브러리

2 hours ago 2

배경 저는 java 기반 자체 프레임워크를 쓰는 회사에서 프레임워크 코어 개발자 역할 겸임으로 일하고 있습니다. 해당 프레임워크는 내부적으로 mybatis/ibatis 와 비슷한 xml 문법으로 쿼리를 작성하고 여느 SI 회사처럼 수천줄 이상의 긴 SQL에 대부분의 로직을 작성합니다. 개인프로젝트 할 때는 js 쪽을 자주 다루는데요, typescript에서 db를 다룰 때 제가 익숙한 방식으로 쿼리를 짤 방법이 마땅치 않더라고요. ORM 은 복잡한 쿼리를 다루기가 어려워서 쿼리 빌더를 찾아봤는데, 얘네들도 결국 CTE나 함수, 복잡한 조건이 들어가는 경우는 한번에 쿼리를 만들지 못하고 변수를 나눠서 만든 다음 합성해서 써야하는게 불편했습니다. 그래서 없으면 내가 만들어봐야지 하고 SQL을 그대로 쓸 수 있지만 raw driver를 직접 쓰진 않고, 업무에 꼭 필요한 dynamic SQL 기능은 mybatis 처럼 하나의 SQL statement 에 모두 코딩할 수 있는 라이브러리를 만들었습니다. 만들다보니 node 계열은 드라이버마다 인터페이스가 제각각이라, 어쩌다보니 드라이버 호출 인터페이스 추상화도 들어가고, java에서처럼 공통된 트랜잭션 관리가 없어서 트랜잭션 추상화도 들어갔습니다. 사용법 Tagged template 를 적극적으로 사용해서 기본적으로 모든 statement 를 Prepared Statement 로 처리합니다. 예시: const users = await db.all(sql.rows<UserRow>` SELECT id, name FROM users WHERE organization_id = ${organizationId} AND status = ${status} `); 일반적으로 ${value} 는 항상 bind parameter 로 처리되고, 각 드라이버별 placeholder는 자동으로 처리해줍니다. 만약 table 명을 동적으로 처리하고 싶다거나 fragment 를 사용하실 때를 위해서 sql.ident, sql.fragment, sql.list 같은 헬퍼도 미리 마련해두었습니다. Why SQL first? raw sql vs query builder vs orm 은 어디서나 논쟁이 되는 주제입니다. 레딧에서도 논의가 많은 주제고, go의 sqlc나 rust 의 sqlx를 추천하는 사람들을 많이 봤습니다. 어떤 게 무조건 정답이라고 하긴 어렵습니다. 각자의 장단점이 있으니까...

Read Entire Article