데이터베이스

ORM과 SQLAlchemy

한땀코딩 2020. 9. 6. 23:01

몇 달 전 SQLD를 준비할 때, 그라데이션 분노를 느꼈던 적이 있습니다. sql을 통해 하려는 맥락은 다 똑같은 것 같은데 db 종류에 따라 아주 조금씩, 미세하게 문법이 달랐던 것입니다. 아니 왜 이건 통일이 되어있지 않고 다 미묘하게 다를까. 이 고민은 문제를 풀면 풀수록 쌓여서 마지막엔 그냥 하나만 잘하자 마인드로 시험을 봤던 것 같습니다.

Is there a defined and accepted standard SQL language?

모든 DB가 공통적으로 사용하는 sql 의 기준 문법은 없는가? 이런 질문에 대해서도 ANSI standard가 있기는 하나, 모든 DB가 이 기준을 기반으로 각각의 subset을 만들어내고 있다고 답변이 올라와 있습니다. 상업적인 이유도 있겠고, 각각의 db가 의도하는 바가 조금씩 달라서 자기들의 입맛에 맞게끔 바꾸는 것도 있겠지만, 어쨌든 통일되지 않았다고 볼 수 있습니다.

ORM?

왜 sql syntax가 통일되지 않은 것으로 글을 시작하는가? 이건 ORM의 장점을 설명하기 위해서도 있습니다. 한참 Flask로 웹 서비스 개발을 하던 도중, DB가 필요하여 어떤 걸 사용할지 알아보다가 ORM에 대한 글을 발견했습니다. 그때 이해한 ORM은 한마디로 말해서 SQL 문법 없이 개발 중인 언어로 데이터베이스에 접근할 수 있게 해주는 라이브러리였습니다.

ORM은 Object Relational Mapping의 준말로, 위키피디아의 정의에 따르면 객체지향 언어를 이용하여 호환되지 않는 type system 간의 데이터 전환을 의미한다고 한다. 범용적인 의미는 이러하고, 세부적으로 sql과 연관지어 본다면, 관계형 데이터베이스에 객체지향 언어로 접근할 수 있게끔 매핑을 해주는 다리 역할을 한다고 볼 수 있다.

(type system: 특정 형태의 데이터를 중심으로 정해진 시스템, 규칙을 의미한다.)

얼핏 보면 SQL 문법을 몰라도 데이터베이스에 접근할 수 있기 때문에 ORM을 무조건 쓸 것 같지만, ORM은 나름대로의 장점과 단점이 있습니다. 몇가지만 나열해보자면,

장점

  • SQL을 별도로 익히지 않아도 DB를 활용할 수 있다.
  • DB를 변경할 때 쿼리를 하나하나 수정하지 않아도 된다.
  • SQL injection 를 방지할 수 있다.

단점

  • SQL을 아는 사람이라면 ORM을 또 별도로 배워야 한다.
  • 복잡한 쿼리가 필요한 경우 성능 저하를 일으키거나 ORM으로 치환해서 작성하기가 난해할 수 있다.

이런 장단점을 살펴보면, 위에서 왜 DB마다 문법이 다른 것에 대한 이야기를 했는지 아실 수 있을 것 같습니다. DB를 바꾸려고 한다면, ORM으로 개발을 했다면 수정할 내용이 많이 줄어들겠죠? 물론 이것이 가장 큰 장점은 아니지만, 여러모로 개발 속도를 올려줄 수 있을 가능성이 높습니다.

SQLAlchemy

객체지향 언어로 DB를 접근할 수 있는 만큼, 수많은 객체지향 언어가 각각의 ORM 라이브러리를 가지고 있습니다. 그중 파이썬에서 사용하는 것이 SQLAlchemy입니다. Django 같은 프레임워크는 독자적인 ORM을 가지고 있다고 하는데, 일반적으로 가장 잘 알려져 있는 것은 SQLAlchemy입니다.

SQLAlchemy는 오픈소스이고, 지금도 커밋이 활발하게 이루어지고 있습니다.

sqlalchemy/sqlalchemy

간략한 사용방법

sql을 통해 단순한 쿼리만이 아니라 꽤 복잡한 것들이 가능하고, 한 줄의 데이터만이 아니라 bulk insert 같은 것도 가능하지만 이번 글에서는 세부적인 것보다는 sqlite를 기반으로 db를 생성하고 간단하게 데이터를 넣고 조회하는 과정만 요약하겠습니다. 개인적으로도 아직 기능에 대해 이것저것 알아가고 있는 과정이라, 이후에 한번 더 상세하게 여러 가지 표현을 정리해서 올려볼까 합니다.

db 생성하기

간단한 예시로 bts라는 이름의 sqlite db 파일에 방탄소년단 멤버들의 이름과 나이를 저장해서 추출해보는 과정까지 적어보겠습니다.

import sqlalchemy
from sqlalchemy import create_engine

engine = create_engine('sqlite:///bts.db', echo=True)
# echo 옵션은 생성하면서 create 쿼리문을 터미널에 출력한다. 

from sqlalchemy import MetaData
meta = MetaData()
from sqlalchemy import Table, Column, Integer, String, MetaData

pip을 이용하여 sqlalchemy를 설치하고, 간단한 테이블을 만들기 위해 필요한 것들을 import 해옵니다. create_engine은 db를 생성하게 해주는 함수로, 위의 예시는 bts.db라는 이름의 sqlite db를 생성하게 됩니다. 아직 테이블을 명시하진 않았기 때문에 물리적으로 파일은 보이지 않습니다. 

MetaData는 설명을 읽어보면 쉽게 생각해서 테이블에 대한 정보를 담아두기 위한 오브젝트입니다. 아래 테이블을 만드는 코드를 보면 어떻게 활용되는지 볼 수 있습니다. 

members = Table(
    'members', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String),
    Column('age', Integer),
)

meta.create_all(engine)

이렇게 하면 물리적인 db를 디렉토리에서 확인할 수 있습니다. 

데이터 insert

ins = members.insert().values(name = 'RM', age = 26)

conn = engine.connect()
result = conn.execute(ins)

insert 함수를 이용하여 입력해줄 데이터를 적은 뒤 execute함수에 넘겨주면 db에 추가가 됩니다. 다른 멤버들도 추가하려면 아래처럼 작성할 수 있습니다. 

conn.execute(members.insert(), [
   {'name':'Jin', 'age' : 28},
   {'name':'Suga', 'age' : 27},
   {'name':'J-Hope', 'age' : 26},
   {'name':'Jimin', 'age' : 25},
   {'name':'V', 'age' : 25},
   {'name':'Jungkook', 'age' : 23},
])

데이터 select

sel = members.select()
result = conn.execute(sel)

for row in result:
    print(row)

select 함수를 활용하여 위와 같이 모든 데이터를 조회할 수 있습니다. 행 하나하나가 결과에 담기기 때문에 출력하여 보려면 반복문을 위와 같은 형태로 돌 수 있습니다. 

(1, 'RM', 26)
(2, 'Jin', 28)
(3, 'Suga', 27)
(4, 'J-Hope', 26)
(5, 'Jimin', 25)
(6, 'V', 25)
(7, 'Jungkook', 23)

결과는 이런 식으로 출력이 됩니다. 

where 절을 활용하여 나이가 25 이상인 멤버들만 조회하고 싶다면 아래와 같이 작성하면 됩니다. 

sel = members.select().where(members.c.age > 25)
result = conn.execute(sel)

for row in result:
    print(row)
(1, 'RM', 26)
(2, 'Jin', 28)
(3, 'Suga', 27)
(4, 'J-Hope', 26)

 

이렇게 간단하게 sqlite db를 만들고 데이터 입력과 간단하게 조회하는 방법을 작성해보았습니다. 

기회가 닿는 대로 sqlalchemy의 조금 더 세부적인 사용방법과 JavaScript의 ORM인 sequelize 활용에 대해서도 다뤄 보겠습니다. 

'데이터베이스' 카테고리의 다른 글

이미지와 오브젝트 스토리지  (1) 2020.11.01