ORM

Object-Relational Mapping

도구

코드 안의 객체와 데이터베이스의 표를 짝지어 두고, 질의문을 직접 쓰는 대신 코드에서 다루듯 읽고 쓰게 해 주는 도구입니다.

등장 배경

코드와 데이터베이스는 데이터를 담는 모양이 서로 다릅니다. 관계형 데이터베이스는 행과 열이 있는 테이블에 값을 담고, 프로그램은 회원 한 명을 이름과 가입일 같은 속성을 가진 객체 하나로 다룹니다. 그래서 둘을 잇는 자리에는 늘 번역 작업이 끼어 있었습니다.

번역은 손으로 했습니다. 개발자가 SQL 문장을 문자열로 적어 보내고, 돌아온 결과를 한 줄씩 돌며 첫 번째 칸은 이름, 두 번째 칸은 가입일 하는 식으로 객체에 옮겨 담았습니다. 회원 정보를 읽는 기능 하나에 이런 코드가 서른 줄씩 붙었고, 그 서른 줄에는 업무 규칙이 한 줄도 들어 있지 않았습니다.

문제는 고칠 때 드러났습니다. 회원 표에 전화번호 칸 하나를 더하면 목록 화면, 상세 화면, 수정 화면에 흩어진 번역 코드를 전부 찾아 고쳐야 했습니다. 한 군데를 빼먹으면 그 화면에서만 빈칸이 나왔고, 칸 순서를 잘못 세면 가입일 자리에 전화번호가 들어갔습니다. 컴파일은 통과하니 실행해 보기 전까지 알 수 없었습니다.

ORM 은 이 번역을 한 번만 적어 두고 나머지를 도구에 넘긴 것입니다. 표와 클래스를 어떻게 대응시킬지 한 곳에 선언해 두면, 읽고 쓰는 코드는 그 선언을 따라 도구가 만들어 냅니다.

해결하는 문제

이 도구가 대신 해 주는 일은 네 가지입니다.

  1. 대응 — 표 하나를 클래스 하나에, 행 하나를 객체 하나에, 칸 하나를 속성 하나에 짝지어 둡니다. 회원 표에 전화번호 칸이 생기면 회원 클래스에 속성 한 줄을 더하는 것으로 끝나고, 화면마다 흩어진 코드를 찾아다닐 일이 없어집니다.
  2. 질의 — 질의문을 문자열로 쓰는 대신 코드로 씁니다. "가입일이 지난달이고 결제가 없는 회원" 을 조건 객체로 조립해 넘기면 도구가 그에 맞는 문장을 만들어 보냅니다. 조건을 코드로 쓰면 오타가 실행 전에 걸리고, 사용자가 입력한 값이 문장에 섞여 들어가는 사고도 줄어듭니다.
  3. 상태 추적 — 읽어 온 객체를 코드에서 고치면 도구가 무엇이 바뀌었는지 기억해 두고, 저장할 때 바뀐 칸만 골라 씁니다. 여러 객체를 함께 고쳤다면 트랜잭션, 즉 전부 성공하거나 전부 없던 일이 되는 묶음으로 한 번에 보냅니다. 중간에 실패해서 절반만 저장된 상태가 남지 않습니다.
  4. 구조 변경 — 클래스 정의를 고치면 표를 어떻게 바꿀지에 대한 마이그레이션 파일을 만들어 줍니다. 코드는 배포하면 옛것이 사라지지만 데이터는 남아 있어서 이미 쌓인 백만 줄을 새 구조로 옮기는 절차가 따로 필요한데, 그 초안을 도구가 적어 주고 사람이 검토합니다.

넷을 합치면 앞에서 서른 줄이던 회원 조회가 두세 줄이 됩니다. 줄어든 자리가 대체로 실수가 나던 자리입니다.

대안

질의문을 직접 다루는 선택지가 셋 있습니다.

  1. 질의문 직접 작성 — SQL 문장을 사람이 쓰고, 돌아온 결과를 객체로 바꿔 주는 일만 얇은 도구에 맡깁니다. 나가는 문장이 적어 둔 그대로라서 성능을 다투는 화면에서 예측이 쉽습니다. 대신 표 구조가 바뀔 때 고칠 문장을 사람이 찾아야 합니다.
  2. 쿼리 빌더 — 질의문을 코드로 조립하지만 만들어지는 문장이 코드와 거의 한 줄씩 대응합니다. ORM 보다 감춰 주는 것이 적고 문자열 직접 작성보다는 안전한 중간 지점입니다.
  3. 섞어 쓰기 — 실무에서 가장 흔한 선택입니다. 화면 하나에 표 하나를 읽고 쓰는 일은 ORM 으로 하고, 표 여섯 개를 맞춰 집계하는 보고서 질의만 직접 씁니다. 도구 대부분이 문장을 직접 넘기는 창구를 열어 두고 있습니다.

고르는 기준은 둘입니다. 하나는 화면의 성격입니다. 만들고 읽고 고치고 지우는 일이 대부분이면 ORM 이 절약해 주는 양이 크고, 집계와 통계가 중심이면 감춰진 문장이 짐이 됩니다. 다른 하나는 팀의 사정입니다. 데이터베이스에 익숙한 사람이 적으면 실수가 줄지만, 나가는 문장을 아무도 보지 않는 팀에서는 문제를 늦게 발견합니다.

실무 용례

"이 목록은 ORM 으로 뽑지 말고 질의문을 직접 쓰죠. 지금 백 번 넘게 나가고 있어요."

도구가 만들어 내는 문장이 비효율적이니 그 화면만 손으로 쓰자는 제안입니다.

"회원 표에 칸 하나 늘었으니 모델 고치고 마이그레이션 파일 만들어 주세요."

클래스 선언을 고친 다음 그에 맞춰 표를 바꾸는 절차까지 함께 준비하라는 뜻입니다.

"ORM 을 쓰더라도 실제로 어떤 질의가 나가는지는 한 번씩 로그로 봐야 합니다."

도구가 감춘 것을 주기적으로 확인하자는 말입니다.

페이지덱은 문서와 회원과 권한을 관계형 데이터베이스에 담고 ORM 으로 다룹니다. 태오가 새 기능을 만들 때 대부분은 클래스에 속성을 더하고 저장 코드를 두 줄 쓰는 것으로 끝납니다. 예외는 사용량 통계 화면입니다. 팀별 문서 수와 편집 횟수를 월 단위로 묶어 보여 주는데, 도구가 만든 문장이 8초를 써서 그 화면만 질의문을 직접 써 0.4초로 줄였습니다.

배포 전에 태오가 확인하는 것은 나가는 질의의 개수입니다. 목록 화면 하나를 열었을 때 질의가 세 번 나가면 넘어가고 예순 번 나가면 코드를 다시 봅니다. 도구를 쓰면 코드 두 줄이 질의 예순 번이 되는 일이 겉으로 드러나지 않기 때문입니다.

흔한 오해

ORM 을 쓰면 데이터베이스를 몰라도 된다고 읽는 경우가 가장 많습니다. 질의문을 직접 쓰지 않으니 그렇게 느껴집니다. 하지만 도구는 문장을 쓰는 일을 대신할 뿐이고, 어떤 문장이 나가는지는 코드를 어떻게 썼는지에 달려 있습니다. 목록을 100건 가져온 다음 건마다 작성자 이름을 꺼내면 질의가 101번 나가는데, 이것이 N+1 문제입니다. 코드로는 반복문 한 줄이라 눈에 걸리지 않습니다. 이대로 두면 데이터가 열 배 될 때 응답 시간이 백 배로 늘고, 원인을 찾는 데 며칠이 걸립니다.

ORM 자체가 느리다는 생각도 자주 어긋납니다. 느린 것은 도구가 아니라 도구가 만들어 낸 문장입니다. 같은 문장을 사람이 썼어도 조건에 쓰는 칸에 인덱스, 즉 책 뒤 찾아보기처럼 미리 만들어 둔 색인이 없으면 똑같이 느립니다. 도구를 걷어내도 해결되지 않고, 나가는 문장을 보고 색인을 걸거나 가져오는 방식을 바꿔야 합니다.

같이 보면 좋은 페이지

마지막 업데이트 2026-09-16