왜 ORM을 사용해야 할까요?

발행: (2025년 12월 3일 오후 08:50 GMT+9)
12 min read
원문: Dev.to

Source: Dev.to

Cover image for Why would you ever use an ORM?

소프트웨어 개발은 언제나 여러 측면 사이의 균형을 찾는 과정입니다: 개발 속도(새 기능을 얼마나 빨리 배포하느냐), 애플리케이션 성능, 메모리 사용량, UI 품질, 그리고 비즈니스 로직의 깔끔함 등.

대부분의 경우, 개발 속도가 성능이나 메모리 사용량보다 더 중요합니다. 성능은 “충분히 좋다”는 수준으로, 메모리 사용량도 “허용 가능”한 수준으로 평가되는 경우가 많지만, 개발 속도는 직접적으로 비즈니스 수익성에 영향을 미칩니다. “너무 빠른” 개발 속도라는 것은 존재하지 않으며, 이는 기업이 제품을 만드는 데 얼마나 많은 비용을 쓰고, 위험에 얼마나 오래 노출될지를 결정합니다. 경쟁이 치열한 시장을 목표로 하고 경쟁사가 더 빨리 출시한다면, 그보다 더 중요한 것은 없습니다.

물론, 거의 움직이지 않거나 터무니없이 많은 메모리를 소비하는 애플리케이션은 경쟁에서 살아남을 수 없습니다. 하지만 애플리케이션 속도를 20 % 올리는 것과 개발 시간을 두세 달 절감하는 것을 비교한다면 선택은 명확합니다. 사용자는 추가된 20 % 속도를 눈치채지 못할 수도 있지만, 예산상의 구멍은 확실히 눈에 띕니다.

ORM이 개발 속도를 높이는 방법

ORM은 개발 시간을 절약하도록 설계된 도구입니다. 구체적으로 어떻게 작동할까요?

  • 코드량 감소 – 테이블 컬럼과 클래스 필드 사이의 값을 이동시키는 보일러플레이트 코드를 숨겨줍니다. 또한 타입 안전성을 보장합니다: 클래스 필드의 타입은 항상 데이터베이스 컬럼 타입과 일치합니다.
  • IDE 지원 – 자동 완성이 어디서든 동작해 개발자 경험과 정확성을 향상시키며, 이는 더 빠른 작업을 의미합니다.
  • 인지 부하 감소 – 짧고 가독성이 좋은 코드는 작성하기도, 이해하기도 빠릅니다. 특히 인간의 두뇌 용량이 제한 요소인 대규모 프로젝트에서 효과적입니다.
  • 고수준 추상화 – ORM은 원시 필드 대신 객체를 노출합니다. 공통 베이스 쿼리를 여러 구체적인 경우에 맞게 다형성을 이용해 수정하거나, 사용자 입력에 따라 조건을 동적으로 추가할 수 있습니다. 같은 작업을 순수 SQL로 구현하려면 훨씬 어렵습니다.
  • 오류 감소 – 수십 개의 필드를 손수 작성할 때 실수하기 쉽습니다. ORM은 그런 실수를 방지하고, 필드를 추가하거나 이름을 바꿀 때 모든 쿼리를 자동으로 업데이트해 인간 요인 오류를 완전히 없애며 디버깅 시간을 크게 절감합니다.
  • 리팩토링 단순화 – 스키마를 정리하고 재구성하는 작업이 ORM을 사용하면 크게 쉬워집니다. 스키마 리팩토링은 장기적인 개발 속도를 한 단계 끌어올릴 수 있습니다.

물론 새로운 추상화 계층은 진입 복잡성을 어느 정도 추가합니다. 테이블이 열 개 정도이고 거의 변하지 않는 프로젝트라면 ORM이 필요 없을 수도 있습니다. ORM은 높은 복잡성을 관리하기 위해 존재하며, 작은 프로젝트는 그런 복잡성을 갖고 있지 않기 때문입니다.

성능에 미치는 영향

앞서 개발은 타협을 찾는 과정이라고 말했습니다. ORM을 사용한다는 것이 성능 저하나 메모리 사용량 증가를 의미할까요? 반드시 그렇지는 않습니다.

  • ORM은 쿼리를 만들고 데이터를 매핑하는 데 약간의 오버헤드를 추가하지만, 데이터베이스 I/O에 비하면 그 오버헤드는 무시할 수준입니다.
  • 짧은 코드와 쉬운 알고리즘 최적화 덕분에 ORM 기반 로직이 장기적으로는 순수 SQL보다 더 빠를 수 있습니다. 순수 SQL도 최적화할 수 있지만 전체 그림을 파악하기 어렵고, 개발자는 작동하는 코드를 건드리는 것을 꺼려하는 경우가 많습니다.
  • 역설 – 처음 작성할 때는 순수 SQL이 약간 더 빠를 수 있지만, 여러 차례 개발을 거치면서 ORM 버전이 점진적인 정리와 안전한 리팩토링 덕분에 크게 앞서는 경우가 많습니다.

일부 사람들은 ORM이 과도한 쿼리를 쉽게 생성하게 만든다고 주장합니다. 특히 SQL을 이해하지 못하는 주니어 개발자가 사용할 때 그렇다고 합니다. 진실은 간단합니다: ORM을 올바르게 사용하려면 SQL을 반드시 알아야 합니다. 자신의 언어 수준 표현식이 어떤 SQL을 생성하는지 이해해야 합니다. ORM은 SQL을 대체하는 것이 아니라, SQL 위에 얹힌 추상화 계층일 뿐입니다.

메모리 소비

메모리 소비 역시 단순히 말할 수 없습니다.

// Example: a DataTable row may occupy far more memory than a simple typed field
Int32 value; // occupies 4 bytes (plus possible overhead in managed environments)

저는 개발자들이 DataTable 객체에 전체 테이블을 로드한 뒤 마치 엑셀 시트처럼 다루는 모습을 본 적이 있습니다. 이런 구조에서는 Int32가 스택에 8 바이트, 힙에 24 바이트(CLR, x64) 정도를 차지할 수 있습니다. ORM은 데이터를 타입이 지정된 필드에 저장하므로 메모리 사용량을 크게 줄여줍니다.

데이터베이스 쿼리 결과는 본질적으로 평면 테이블입니다. 예를 들어 100 개 이상의 컬럼을 가진 orders 테이블이 5 개의 필드를 가진 positions 테이블과 1‑대‑다 관계로 조인된다고 가정해 보세요. 결과를 “그대로” 저장하면 각 position 레코드가 왼쪽 테이블의 100 개 이상의 필드를 모두 복제하게 됩니다. 메모리 낭비가 10배, 심지어 100배까지도 될 수 있습니다. 좋은 ORM은 모든 데이터를 복제하지 않고, 자식 행을 단일 부모 인스턴스 아래에 그룹화합니다.

스키마 동기화

ORM의 첫 번째 임무는 데이터베이스 스키마와 애플리케이션 객체를 동기화하는 것입니다. 이것이 매핑의 핵심이며, 테이블 컬럼을 클래스 필드에 로드하고 다시 저장하는 작업을 의미합니다. 이를 올바르게 수행하려면 ORM은 컴파일 시점에 “원본” 스키마 정의를 가져와야 합니다. 이 정의는 데이터베이스(데이터베이스‑first), 코드(코드‑first), 혹은 별도 모델(모델‑first)에서 올 수 있습니다.

  • 코드‑first는 코드를 기반으로 스키마를 데이터베이스에 반영합니다. 여러 환경을 다룰 때는 편리하지만, 서로 독립적인 서비스들이 각각 데이터베이스를 자신들만의 방식으로 재구성하려 할 때는 문제가 발생합니다. 기존 스키마를 로드하고, 정확히 비교한 뒤 복잡한 DDL을 생성해야 하기 때문에 동기화가 복잡해집니다.
  • DB‑first모델‑first는 엔티티 클래스의 코드 생성을 기반으로 합니다. 이는 더 간단합니다: 데이터베이스(또는 모델 파일)에서 스키마를 로드하고 엔티티 파일을 재생성하면 됩니다.

실제로 스키마 동기화는 프로젝트마다 요구 사항이 다릅니다. 예를 들어 데이터베이스 주석을 엔티티 파일에 가져오거나, 텍스트 컬럼을 통해 다대다 관계를 처리하거나, 커스텀 로직으로 enum을 매핑하는 등 맞춤 규칙을 위해 생성 과정을 확장해야 할 수도 있습니다. 기본 제공 ORM 생성기는 종종 폐쇄적이며 쉽게 커스터마이징할 수 없습니다.

이러한 경직성은 ORM에 대한 흔한 비판 중 하나입니다. 해결책 중 하나는 오픈소스 생성기를 사용하는 것입니다. 예를 들어 MIT 라이선스를 가진 OrmFactory는 프로젝트 요구에 맞게 수정할 수 있습니다. 이 도구는 데이터베이스와 직접 작업해 스키마를 프로젝트에 내보내며, 스키마 설계와 동기화를 담당하고 ORM은 매핑이라는 본연의 역할에 집중하도록 해 줍니다.

Back to Blog

관련 글

더 보기 »

Bf-트리: 페이지 장벽을 깨다

안녕하세요, 저는 Maneshwar입니다. 저는 FreeDevTools – 온라인 오픈‑소스 허브를 개발하고 있습니다. 이 허브는 dev tools, cheat codes, 그리고 TLDRs를 한 곳에 모아 쉽게 이용할 수 있게 합니다.