SQLAlchemy 2.0 Style 이 반가운 이유

2021.05.11

SQLAlchemy 사용법에 큰 변화가 생겼습니다. 개발 초기의 흔적과 주요 API 기능의 점진적인 도입으로 생겨났던 코드 부스러기들이 대거 정리되었습니다. 

파이썬 2.0에서 3.0으로 부드럽게 넘어가던 과정을 벤치마킹해서 SQLAlchemy의 1.4에서 2.0으로 넘어가는 방법이 공식문서에 있으나, 아직 국내에 소개 자료가 없어서 본 포스팅을 작성하게 되었습니다.

왜 SQLAlchemy 2.0 Style 이 반가웠을까요?

SQL은 그 자체로 강력하지만, 조건에 따라 다양한 쿼리를 생성하거나, 여러 개의 쿼리를 연결하여 사용할 때 불편합니다. raw SQL은 길면 길수록 더 읽기 어렵고, 디버깅은 더 어렵습니다. 단순한 조건의  환자군을 추출할 때도 가뿐히 100 라인이 넘는 쿼리가 필요합니다.

그래서 라인웍스의 데이터 엔지니어들은 SQLAlchemy 로 쿼리를 모듈화하여 임상 코호트를 추출하는 과정을 확장성 있고 조합 가능하게 만들고 있습니다. select 할 컬럼 선택, where 절의 조건, 서브 쿼리를 마치 레고 블럭처럼 만들고, 개별로 테스트하고 조합하여 사용합니다.

그럼에도 SQL을 SQLAlchemy 로 바꿔 쓰는데 비용이 듭니다. 개발자가 두 언어의 번역가 역할을 하려면 양쪽에 익숙해야합니다. SQL과 동일한 결과를 내도록 SQLAlchemy 로 작성하고 검증하는데 시간과 노력이 들어갑니다. 처음 SQLAlchemy 를 접한 당시를 돌아보면, SQL을 SQLAlchemy로 바꾼 예시를 보지 않고는 쉽게 진도가 나가지 않았던 기억이 납니다. 

그런 점에서 SQLAlchemy 2.0 스타일은 SQL과 비슷한 구조로 쿼리를 빌드할 수 있다는 점이 매력적입니다. SQL을 이해하던 직관과 일관성이 생겼습니다.

이걸 가능하게 하는게 generatvie style (a.k.a. method chaining, fluent interfaces)입니다. 

간단한 SQL 문을 예로 들어보겠습니다.

select x, y from table where id = 5

위 문장을 기존 스타일로 쓰면 다음과 같습니다.

stmt = select([table.c.x, table.c.y], table.c.id == 5)

from, where, order by 등 기본적인 질의 기능을 SQLAlchemy에서 쓰기 위해 select() 에 위치가 지정된 인자(argument)로 넘겨줘야 했습니다. 이것은 SQL 문법의 구조와 언뜻 비슷하게 보이지만, 막상 옮겨보면 헷갈립니다.

위 문장을 2.0 스타일로 바꿔보겠습니다.

stmt = select(table.c.x, table.c.y).where(table.c.id == 5)

SQL 문장의 최소단위에서 그 다음에 올 수 있는 문법을 연결하며 쿼리를 만들 수 있어서 SQL을 사용하는 직관과 유사하게 프로그래밍 할 수 있게 됩니다. 

SQLAlchemy 에서 추구하는 스타일 

여기서 잠깐 SQLAlchemy가 왜 이런 변화를 주는지, 이로 인해 어떤 장점이 있는지 보겠습니다. 

SQLAlchemy 는 SQL의 요소를 구조적인 요소와 데이터 요소로 나누고 있습니다.

  1. SQL의 구조적인 요소는 그 위치로 전달됩니다.
    1. 예를 들면, stmt = select(table.c.x, table.c.y)x, y 컬럼을 첫번째와 두번째 인자로 전달받습니다. 
  2. SQL의 데이터 요소는 배열로 전달됩니다.
    1. 예를 들면, stmt.where(table.c.y.in_([1,2,3]))where y in (1,2,3) 과 동일합니다. in_(1,2,3) 처럼 사용되지 않는다는 뜻입니다.

레거시 호출 패턴에서 select()의 두번째 인자가 where 조건이었던 관계로, 이런 원칙에 충돌이 날수밖에 없었습니다. 즉, 레거시 호출 패턴에 따르면 select(table.c.x, table.c.y) 의 두번째 인자인 table.c.ywhere 조건으로 인지한다는 뜻입니다.

2.0 부터 select()를 사용할때 generative style 만 허용하기 때문에, 위 원칙과 부합하는 인터페이스를 만들기 쉬워졌습니다.

나가며 

실제로 데이터를 다루는 개발자들과 얘기하다보면 유지보수나 클린코드 관점에서 SQLAlchemy을 사용하고 싶으나, raw SQL을 바꾸는데 드는 시간과 노력이 크다보니 손도 못 대는 경우가 많다고 합니다. 2.0 스타일이 확산되면, 개발자들이 쿼리를 유지보수하는게 전보다 쉬워질 것 같습니다.

공식문서는 현재 최신 버전인 1.4에서 실행했을때 2.0 deprecation 경고가 없다면 SQLAlchemy 2.0과 상호호환되는 것이므로, 경고 메시지를 잘 확인하여 점진적으로 2.0으로 옮겨가길 권하고 있습니다. 

이후 포스팅 연재로 2.0 스타일로 옮기며 겪은 시행착오를 공유할 예정입니다.

Cinyoung Hur

Lead Data Engineer, Software Developer

Cinyoung Hur