ORM은 안티패턴이다.

김명성·2023년 7월 25일

Laurie Voss의 포스트를 번역한 글입니다.


1. In defence of SQL

SQL은 1970년대에 수백만 개의 행의 대규모(그 당시에는) 데이터 저장소가 등장한 시기에 발명되었습니다.
SQL이 그 당시 존재하던 다른 쿼리 언어보다 뛰어난 것은 아니었지만 (비록 읽기 쉬웠지만), 표준화되었기 때문에 다른 쿼리 언어보다 널리 사용된 것입니다.
SQL이 표준화되면서 데이터 저장소를 구축하는 사람들은 모든 클라이언트와 고객에게 이 데이터 베이스를 사용하려면 어떤 언어를 사용해야하는지 다시 교육할 필요 없이 표준이 된 SQL 표준에 맞게 쓸 수 있었습니다.
이로 인해 많은 문제를 해소하게 되었고, 이것은 큰 성공이었습니다.

그러나...

SQL은 어색합니다.

현재 우리가 일상적으로 사용하는 SQL은 그리 예쁘지 않습니다.

하지만 명심하세요. SQL이 실제로 표현하려는 것은 관계 대수(Relational Algebra)입니다.
관계 대수는 E.F. Codd(RDBMS의 거의 모든 이론적 기반을 개척한 사람)가 개발한 논리의 한 유형인데요,
익숙하지 않다면 관계 대수를 벤다이어그램(Venn Diagram)처럼 생각하는 것이 도움이 될 수 있습니다.
예를 들어, 집합 A에 있는 모든 과일 중에서, 집합 B의 가격으로, 집합 C의 농부에 의해 재배된 과일을 찾으세요.
이런 식입니다.

SQL이 실제로 해결하려는 것은 데이터 집합의 정렬, 집계, 특히 필터링하는 것이 아닙니다.

count(*)가 어색한 이유는 이 언어가 정말 상기한 용도로 디자인되지 않았기 때문입니다.
GROUP BY와 ORDER BY 절은 뒤늦게 추가된 것처럼 보이는 이유는 사실이기도 합니다.
(HAVING은 큰 문제이며, UNIQUE는 참사이며, LIMIT에 대해서는 언급도 하지 않겠습니다).
물론, 데이터 집합의 일반적인 사용에서는 대개 이러한 작업을 수행하고자 하기에 SQL이 이러한 기능을 제공하는 것입니다.
SQL은 일종의 충실한 노동말로서 하고자 하면 어려운 일이 아니기 때문입니다.
그러나 이 작업은 그리 빠르지 않을 수 있습니다.

따라서 맞습니다. 매일 사용하는 SQL은 추하고 어색합니다. 사실, 지독한 모습입니다.
그리고 종종 꽤 느릴 수도 있습니다.
이는 SQL이라는 언어 자체가 디자인되지 않은 작업을 요청하기 때문입니다.(엔진 자체가 그것을 수행할 수 있는지는 또 다른 문제입니다). 하지만 SQL은 작동하며, 그 발명 이후 40년 동안 우리는 SQL을 대체할 만큼 강력한 개선을 거의 찾지 못했습니다.


SQL의 대체제로서 ORM을 사용하는 것은 어떨까요?

나는 명백하게 주장합니다.
ORM은 어리석은 아이디어입니다.
ORM의 탄생은 SQL이 추하다는 사실에서 비롯됩니다. (그 이유는 관계 대수는 상당히 어렵고 다른 대부분의 프로그래밍과 매우 다르기 때문입니다.)
우리의 프로그램에는 이미 객체 지향 모델이 있으며 하나의 프로그래밍 언어를 알고 있습니다.
왜 두 번째 언어(SQL), 그리고 두 번째 모델을 배워야 할까요?
(* ORM을 사용하면 한 언어로 간편하게 해결 가능한데, 왜 SQL을 배워야 하는지 역설하는 질문 같습니다.)
그냥 이 녀석(RDBMS) 위에 추상화 계층(ORM)을 던져버리고, RDBMS가 존재하지 않는 것처럼 잊어버리면 되지 않을까요?

이것은 분명히 어리석은 것입니다.
왜 주요 사용 목적(primary use-case)과 일치하지 않는 방식으로 데이터를 저장하고, 배울 필요가 없는 언어(ORM)를 통해 접근하려 하는 것인가요?
당신의 해결책은 그냥 데이터베이스와 언어를 그대로 유지하고 그 위에 추상화로 감싸는 것인가요?
만약 데이터가 레거시 시스템에 저장되어 있고, 새로운 프론트엔드를 작성해야 할 필요성이 있다면 그렇게 할 수도 있지만, 사람들은 새로운 프로젝트에 ORM을 사용합니다. 왜 그럴까요?

ORM은 SQL 사용보다 느립니다.
왜냐하면 추상화 계층은 항상 그렇기 때문입니다.
그러나 다른 추상화 계층은 개발 속도를 높혀 성능 저하를 보완하는 대신, ORM은 아무것도 이점이 없습니다.
사실 단순 SELECT보다 복잡한 작업을 해야 하는 경우, 실제로 무엇을 하려고 하는지 추상화 된 RDBMS에 알리기 위해 SQL, 또는 pseudo-SQL 언어를 작성해야 하는 경우도 있습니다.

OMADS: 애플리케이션에 적합한 데이터 저장소

OMADS는 "Obviously More Appropriate Data Stores"의 약자입니다.
이 용어는 원래 "NoSQL" 용어를 비판하면서 제안된 것으로, NoSQL이라는 용어는 데이터 저장소가 SQL을 사용하지 않는다는 사실을 강조하지만, 저장소의 실질적인 특성을 설명하지 않는다는 비판으로 비롯된 용어입니다.
따라서 OMADS는 데이터 저장소를 No-SQL로 데이터베이스를 설명하는 것보다 보다 구체적으로 설명하는 용어를 제안하기 위한 시도였습니다.
OMADS는 다양한 유형의 데이터 저장소를 포괄하는 용어로 사용될 수 있습니다.
key-value stores, object stores, document stores, searchable indexes 등을 포함할 수 있습니다.
이러한 데이터 저장소들은 NoSQL과 같은 특정 표준에 국한되지 않으며, 각각의 특징과 적합한 사용 사례에 따라 선택할 수 있습니다.

ORM은 어리석은 개념이었고, 사람들은 이를 깨달았습니다.
그래서 똑똑한 프로그래머들은 이 터무니없는 구조를 살펴보고 진짜 문제점은 데이터베이스와 사용 사례가 서로 일치하지 않았다는 점을 알게 되었습니다.
실제 문제는 SQL 자체가 아니라 관계 모델이라는 점을 알게 된 것입니다.

그래서 ORM, SQL, RDBMS를 모두 버리고 키-값 저장소(key-value store), 객체 저장소(object-store), 문서 저장소(document stores), 검색 가능한 인덱스(searchable indexes)와 같은 다른 데이터 구조들을 작성했습니다.
그리고 이러한 데이터베이스들은 거의 모든 데이터베이스가 SQL 인터페이스를 기반으로 한 RDBMS가 활개하던 시기에 등장했기 때문에 "NoSQL"이라는 이름을 얻게 되었습니다.
그리고 "NoSQL"(Not Only SQL의 약자)이라는 용어는 OMADS를 의미하는 것은 아니지만 이렇게 알려지게 된 것입니다.

No-SQL(OMADS)
1. key-value store

  • Tools: Redis, DynamoDB(AWS), Cassandra(Apache)
  • 키와 값의 쌍으로 데이터를 저장하는 간단한 구조를 가진 DB.
  • 높은 성능과 확장성을 제공하며 데이터의 빠른 Read,Write가 가능합니다.
  • 주로 캐싱, 새션 매니징, 실시간 분석에 사용됩니다.
  1. object store
  • Tools: S3(AWS), Cloud Storage(Google)
  • 객체의 이진 형식으로 저장하는 DB로, 파일과 비슷한 개념의 데이터를 저장합니다.
  • 주로 대용량의 정적 파일, 이미지, 동영상 등을 저장하고 제공하는데 사용합니다.
  1. document store
  • Tools: MongoDB,Firestore(Google)
  • JSON과 같은 형식으로 문서를 저장하는 DB.
  • 유연한 스키마를 가지고 있어 다양한 형태의 문서를 저장하고, 복잡한 쿼리 및 인덱싱이 가능합니다.
  • 주로 웹앱, 로그 데이터 등에 사용됩니다.
  1. searchable indexes
  • Tools: ElasticSearch
  • 텍스트 검색에 특화된 DB.
  • 대량의 텍스트를 인덱싱하여 빠르고 정확한 검색을 제공합니다.
  • 검색 엔진, 분석 앱에 주로 사용됩니다.

나는 NoSQL 저장소들을 좋아합니다. 내 스타트업은 memcache 없이는 동작할 수 없을 정도입니다.
나는 카산드라가 멋지다고 생각합니다.
Redis는 멋지지만 약간 버그가 있는 편입니다.
MongoDB는 멋지며 MongoDB를 기반으로 한 제품 시스템을 구축할 가능성이 높습니다.
매일 HDFS를 사용하고 있고, 여전히 내 작은 머릿속을 놀라게 합니다. 사실, 이들에 대해 싫어하는 유일한 것은 "NoSQL"이라는 라벨인데, 많은 사람들이 이미 지적한 대로 실제로 어떤 내용을 전달하지 않고 그저 SQL이 아닌 것만을 얘기하는 것입니다.
또한 SQL에 대해 잘 모르는 사람들은 SQL에 대해 뭔가 잘못된, 나쁜 또는 구식이 있다고 생각을 심어주는 것 같습니다.
그래서 프로그래머들은 이러한 것을 사용하는 것을 싫어합니다.

HDFS
HDFS는 빅데이터 처리 및 분석에 많이 사용되는 하둡(Hadoop) 프레임워크의 핵심 구성 요소 중 하나로, 대용량 데이터의 저장과 처리에 특화된 파일 시스템입니다.
하둡과 연계되어 있는 데이터베이스는 HBase, Hive, Impala, Cassandra가 있습니다.


RDBMS는 어떤 용도에 적합한가요?

RDBMS는 어떤 애플리케이션에 완벽하게 적합한 것일까요? 그 답은 "모든 것"이고, "아무것도 없습니다".

우리는 요즈음 이것을 당연시합니다만, 관계 모델은 상당히 마법 같습니다.
개체들의 모델을 만들고, 데이터를 그 모델에 넣고, 결과를 얻을 수 있습니다.
대학에서 얼마나 많은 교수가 수강생 20명 미만인데 연봉이 10만 달러 이상을 받고 있나요?
우리 최신 제품을 구매한 고객 중 이전에 아무것도 구매하지 않았던 고객은 몇 명인가요?
지난 30개월 동안 화요일 판매 실적은 어떤가요?

당신은 미리 당신의 질문이 무엇인지 알 필요가 없습니다.

모든 행을 조사하기 위해 특별한 코드를 작성하거나 결과를 조합하는 가장 효율적인 전략을 알아내기 위해 어떤 특수한 코드도 작성할 필요가 없습니다.
데이터 간의 관계를 알고 있다면, 이러한 데이터에 대해 ad-hoc 질문을 할 수 있으며, 데이터베이스가 정답을 알고 있습니다. 나는 이 개념을 이해한 첫 번째 순간을 기억하며, 그것은 나를 놀라게 했습니다.
(* ad-hoc는 미리 계획하지 않고 특정 상황에서 즉석에서 만드는 것을 의미합니다. DB와 관련하여 미리 정의된 쿼리가 아니라, 특정 시점에 필요에 따라 사용자가 직접 작성하는 쿼리를 말합니다. 이러한 쿼리는 일회성이며 임시로 필요한 정보를 얻기 위해 사용됩니다.)

애플리케이션을 만들 때, 앱의 목적과 선택한 데이터베이스의 목적이 일치하지 않는 경우에는, 기본적인 작업(예: 객체의 총 개수를 얻는 것)을 수행하기 위해 분산 문서 저장소(Document store)에서 미친 듯이 일주일이나 걸리는 깊이 우선 검색을 실행하게 될 수도 있습니다.

따라서 데이터의 모든 질문을 미리 알 수 없는 경우에 가장 안전한 선택은 RDBMS를 사용하는 것입니다.
그리고 프로젝트를 처음 시작할 때, 대개는 필요한 모든 질문을 알지 못합니다.

따라서 내 조언은 항상 RDBMS를 사용하는 것입니다. 그러나 RDBMS만 사용하지는 마십시오.


최적화하되 ad-hoc 쿼리 준비하기

당신의 데이터가 정말 거대한 해시 조회일까요? 그렇다면 key-value 저장소가 필요한 것입니다.
관련된 데이터에 대해 주로 단일 키를 통해 액세스합니까? 그렇다면 문서 저장소가 적합합니다.
전체 텍스트 검색이 필요한가요? 그러면 SQL이 아닌 텍스트 인덱싱 엔진을 사용하세요.
미리 예측할 수 없는 데이터 질문에 대답해야 하는가요? 그렇다면 데이터를 어떻게든 RDBMS에 저장하세요.
실시간이 아니더라도 원본 형태로 보다는 요약된 형태로 저장해도 됩니다.
그러면 공동창업자가 "Y에서 X가 몇 개인가요?"라고 묻게 되어도 "음, 그걸 알아보기 위해 반나절 동안 코드를 작성해야 해요"라는 대답 대신 몇 줄의 SQL을 작성해서 답을 얻을 수 있습니다.
그것이 SQL이 하는 일입니다.

Post-SQL

맨 위에 상기한 SQL의 탄생 조건을 살펴보면, 많은 새로운 데이터 저장소가 동시에 등장하고 공통 언어의 부재로 인해 문제와 분열이 발생했다는 것을 볼 수 있습니다.
같은 현상이 현재 NoSQL 진영에서도 벌어지고 있습니다.
Cassandra를 사용하여 앱을 작성하기로 결정하면, 그것이 우리 모두가 원하는 것이라고 확신해야 합니다.
왜냐하면 저장소를 바꾸면 모든 코드를 변경해야 하기 때문입니다.
이것은 궁극적으로 락인 상태이며, 특정 기업이 독점하지 않았기에 나타나는 불행한 부작용일 뿐입니다.

곧, ORM이 얼마나 어리석은가를 깨달은 영리한 사람들은 하나의 공통 API로 모든 NoSQL 저장소에 접근할 수 있는 실제로 유용한 추상화 계층을 찾는 문제를 깨닫게 될 것입니다.
아마도 Thrift 또는 Avro가 될 수도 있지만, 확실하지 않습니다.
그리고 SQL이 다시 나타날 가능성은 50% 정도라고 말할 수 있습니다.

Thrift, Avro
Thrift와 Avro는 데이터 직렬화와 RPC(Remote Procedure Call) 프레임워크로서, 서로 다른 플랫폼과 언어 간에 데이터를 효율적으로 교환하고 통신하기 위해 개발된 기술입니다.
Thrift는 Facebook에서 개발한 오픈소스 프로토콜과 프레임워크입니다. 다양한 프로그래밍 언어를 지원하며, 서버-클라이언트 간 효율적인 통신을 위해 사용됩니다. Thrift는 IDL(Interface Definition Language)을 사용하여 서비스에 대한 인터페이스를 정의하고, 자동으로 해당 언어로 코드를 생성해줍니다. 이를 통해 서로 다른 언어로 작성된 시스템 간의 통신이 간단하고 효율적으로 이루어질 수 있습니다.
Thrift는 이진 직렬화(Binary Serialization)를 기본으로 하며, 데이터를 작은 크기로 압축하여 네트워크 전송을 최적화합니다. 주로 다양한 마이크로서비스 아키텍처와 클라이언트-서버 시스템에서 사용되며, 크로스 플랫폼 환경에서 데이터 통신을 처리하는 데 적합합니다.
Avro는 Apache 소프트웨어 재단에서 개발한 오픈소스 데이터 직렬화 시스템입니다. Thrift와 유사하게, 다양한 언어와 플랫폼 간에 데이터를 교환하고 저장하기 위해 사용됩니다. Avro는 JSON과 바이너리 직렬화를 모두 지원하며, 데이터 스키마를 포함하여 데이터를 저장하므로 자체 포맷과 스키마를 갖고 있습니다.
Avro의 주요 특징 중 하나는 동적 스키마 기능입니다. 이는 데이터의 스키마를 런타임에 읽어오는 기능을 의미합니다. 따라서 데이터 스키마의 변경이 쉽고 유연하며, 서로 다른 버전의 데이터 간의 호환성을 보장합니다. Avro는 대용량 데이터 처리와 빅데이터 분산 시스템에서 효과적으로 사용됩니다.

SQL의 승리

SQL의 승리가 아니라고 할 수 없습니다.
조금 이상하긴 하지만, SQL은 여러 줄의 API 호출이나 미친 수준의 관계 대수 언어보다 훨씬 간결하고 읽기 쉽기 때문입니다.
그리고 SQL 자체가 본질적으로 느리다는 것은 아닙니다.
Cassandra에서 SELECT * FROM table WHERE ...을 실행한다면 동일한 조건을 API 호출로 지정하는 것보다 느리지 않습니다.
사실, MongoDB 문서에서는 해당 SQL 쿼리를 나열합니다.
이것은 SQL의 사용성에 대한 명확한 지지입니다.

프로그래머들은 새롭고 멋진 것들을 정말 좋아합니다. 그래서 SQL이 거의 40년 동안 남아있는 것은 아무도 신경 쓰지 않는다는 것을 의미합니다.(우리는 그게 아님을 이미 알고 있습니다)
또는 다른 것으로 SQL을 대체할만큼 충분히 강력한 것이 현재까지 등장하지 않았다는 것을 의미합니다.
그러니 가서 OMADS를 사용하고, RDBMS를 주머니에 넣고, 고대의 SQL에게 그렇게 잔인하지 말아봅시다.


2. ORM is an anti-pattern

ORM은 Anti-Pattern이라고 부릅니다.
저는 이전에 ORM에 대해 언급했던 적이 있었는데( 상기한 1편), 그때는 SQL과의 관계로 큰 토론 안에서 이야기했기 때문에 두 가지 문제를 혼동한 것 같습니다.
이제 ORM에 집중하겠습니다. 가능한 간결하게 설명하도록 하겠습니다.
이전에 SQL 기사를 쓸 때, 사람들은 화가 나게 만드는 첫 문장에서 읽기를 중단하는 경향이 있다는 것을 명확히 인식하게 되었습니다.(첫 문장을 읽은 뒤 그 뒤에 해당 문제에 대한 내용을 다루더라도 댓글을 남기는 경우가 종종 있습니다.)
(
어그로가 심해 번역글에는 원문 첫 문장을 옮기지 않았습니다. 첫번째 참조 링크를 통해 확인해주세요.)

Anti-Pattern이 무엇인가요?

위키피디아에서 Anti-Pattern에 대한 포괄적인 목록을 찾을 수 있어서 기쁩니다.
저는 ORM을 Anti-Pattern으로 부르는 이유는 Anti-Pattern 저자가 Anti-Pattern과 단순히 나쁜 습관을 구별하기 위해 사용한 두 가지 기준에 부합하기 때문입니다. 특히,

  1. 초기에는 유용하다고 보일 수 있지만, 장기적으로는 좋은 결과보다는 나쁜 결과가 더 많음
  2. 반복 가능하고 검증된 대안이 존재함

에 ORM은 부합합니다.

ORM의 장점

  1. 간단함: 일부 ORM 레이어는 "SQL 사용을 제거한다"고 말할 수 있습니다. 하지만 이 약속은 아직 실현되지 않았습니다. ORM은 더 현실적으로 SQL을 쓰지 않을 필요를 줄여주지만 필요할 때는 사용할 수 있게 해줍니다. 간단한 모델과 프로젝트 초기에는 확실히 이것은 장점입니다.
    하지만 ORM을 사용하면 빠르게 프로젝트를 시작할 수 있지만 그렇게 빠르게 달려가는 방향이 잘못되었을 가능성이 높다는 점을 명심하세요.

  2. 코드 생성: ORM을 통해 모델의 사용자 수준 코드를 제거함으로써 Scaffolding 패턴인 코드 생성을 할 수 있습니다. 스키마의 간단한 설명을 통해 모든 테이블에 대한 기능적 인터페이스를 얻을 수 있습니다. 더 놀랍게도 스키마 설명을 변경하고 코드를 다시 생성하여 CRUD를 제거할 수 있습니다. 이것은 초기에는 확실히 작동합니다.

  3. Efficent is.. "Enough Good": 내가 본 모든 ORM 레이어는 효율성 향상을 주장하지 않습니다. 모두 코드의 민첩성을 위해 효율성을 희생하는 것을 꽤 명시적으로 밝힙니다. 성능이 느려지면 언제든지 더 효율적인 직접 SQL을 사용하여 ORM 메소드를 재정의할 수 있습니다.

ORM의 문제점

  1. 불충분한 추상화
    ORM의 가장 뚜렷한 문제점은 구현 세부 사항을 충분히 추상화하지 못한다는 것입니다.
    주요 ORM 라이브러리들의 문서는 SQL 개념이 가득합니다.
    일부 ORM은 SQL과 동등한 개념을 설명하면서도 해당 SQL과 같은 것으로 표시하지 않는 반면, 다른 ORM은 해당 라이브러리를 SQL을 생성하는 절차적 함수들의 집합으로 취급합니다.
    추상화의 핵심은 단순화하는 것입니다.
    SQL을 이해해야 하는데 SQL을 사용하도록 요구하는 ORM 추상화는 학습해야 할 양을 두 배로 늘립니다.

    먼저 실행하려는 SQL이 무엇인지 배워야 하고, 그런 다음 ORM에게 그것을 작성하도록 API를 배워야 합니다. Hibernate에서 복잡한 SQL을 수행하려면 사실상 SQL과 거의 비슷한 세 번째 언어인 HQL을 배워야 합니다.
    그리고 그런 후에야 ORM이 SQL로 번역해 줍니다.
    ORM의 옹호자는 이것이 모든 프로젝트에 적용되지 않는다고 할 것이고, 모든 사람이 복잡한 조인을 해야 하는 것은 아니라고 주장할 수 있습니다.
    ORM은 "80/20" 솔루션이며, 80%의 사용자는 SQL 기능의 20%만 사용할 필요가 있고, ORM으로 해결할 수 있을 것이라고 합니다.
    그러나 제 개인적인 경험으로는 15년 동안 데이터베이스 기반 웹 애플리케이션을 개발한 결과, 이는 저에게 해당하지 않았습니다.
    프로젝트의 초기에만 조인 또는 초보적인 조인으로 시작할 수 있습니다.
    그 이후에는 쿼리를 최적화하고 통합해야 합니다. 80%의 사용자가 SQL 기능의 30%만 사용할 수 있다고 해도, 모든 사용자는 작업을 완료하기 위해 추상화를 깨야 합니다.

  2. 부적절한 추상화
    프로젝트가 진짜로 관계형 데이터 특성이 필요하지 않다면, ORM은 완벽하게 작동할 것입니다.
    그러나 그럴 경우에는 다른 문제가 발생합니다. 바로 잘못된 데이터 저장소를 사용하고 있다는 것입니다.
    관계형 데이터 저장소의 오버헤드는 엄청나며, 이것이 NoSQL 데이터베이스가 훨씬 더 빠른 이유 중 하나입니다.
    그러나 데이터가 관계적이라면 그 오버헤드는 가치가 있습니다.
    데이터베이스는 단순히 데이터를 저장하는 것뿐만 아니라 데이터를 표현하며, 관계를 기반으로 데이터에 대한 질문에 대답할 수 있습니다. 이는 프로시저 코드에서는 어려운 일입니다.
    그러나 데이터가 관계적이 아니라면, 처음부터 SQL을 사용하여 불필요한 큰 오버헤드를 추가한 다음 이를 더해 ORM을 사용해 또 다른 추상화 계층을 추가하는 문제가 발생합니다.
    그 반대로, 데이터가 관계적이라면 객체 매핑은 결국 실패할 것입니다.
    SQL은 관계형 대수에 관한 것이며, SQL의 출력은 개체가 아니라 질문에 대한 답입니다.
    객체 "A"가 X의 인스턴스이고 Y의 수를 가지며, 각각의 Y는 Z에 속한다면, 객체를 메모리에서 올바르게 나타내는 것은 무엇인가요?
    X의 속성만 가져오면, 언제 Y를 가져올 쿼리를 실행해야 합니까? 그리고 그 중에서 하나를 가져올지 모두 가져올지 어떻게 할 건가요?
    실제로는 상황에 따라 달라집니다.
    이것이 바로 SQL이 질문에 대한 답이라고 말한 것입니다.
    메모리에서 객체의 표현은 어떤 것을 할지에 따라 달라집니다.
    그리고 객체 지향 디자인의 특성이 아닌 것은 관계형 데이터입니다.
    객체는 관계가 아니고, 관계는 객체가 아닙니다.

  3. Death by a thousand queries
    이로 인해 ORM의 또 다른 문제가 자연스럽게 발생합니다.
    먼저 비효율성입니다. 객체를 가져올 때 어떤 속성(테이블의 열)이 필요합니까?
    ORM은 알 수 없으므로 모두 가져옵니다(또는 추상화를 깨야하는 명령을 내려야 합니다).
    초기에는 문제가 되지 않을 수 있지만, 1000개의 레코드를 한 번에 가져올 때 30개의 열을 가져와야 할 때 3개의 열만 필요한 경우 효율성의 심각한 원인이 됩니다.
    많은 ORM 레이어들은 또한 조인을 판별하는 것에서 능숙하지 않고 관련 객체에 대한 수십 개의 개별 쿼리로 돌아갈 수 있습니다.
    앞서 언급했듯이 많은 ORM 레이어들은 효율성이 희생된다고 명시적으로 밝히고 있으며, 문제가 되는 쿼리를 최적화하는 메커니즘을 제공합니다.
    경험상 하나의 "마법 총알" 쿼리를 최적화하는 경우가 드물다는 문제가 있습니다.
    데이터베이스를 지원하는 응용 프로그램의 문제는 한 쿼리의 효율성이 아니라 쿼리의 수입니다.
    ORM의 컨텍스트 민감성 부족으로 인해 쿼리를 합칠 수 없으며, 캐싱 등의 메커니즘을 사용하여 보상해야 합니다.

대안은 무엇인가요?

이 시점에서 ORM에 대한 기본적인 설계 결함을 보여줬기 때문에 ORM은 어떤 Anti-Pattern이라는 증거를 제시한 것으로 간주될 수 있습니다. 그러나 Anti-Pattern이 되기 위해서는 대안이 있어야 합니다.
실제로 두 가지가 있습니다.

  1. 객체 사용
    만약 데이터가 객체인 경우 관계형 데이터베이스 사용을 그만두세요.
    프로그래밍 세계는 현재 대량의 데이터를 우아하고 자체 포함된 데이터 구조로 저장하고 접근할 수 있는 키-값 저장소로 넘쳐나고 있습니다.
    모든 웹 앱을 만들기 위한 Step One이 MySQL을 설치해야 한다는 법은 없습니다.
    최근 몇 년 동안 SQL이 악명을 떨치는 이유 중 하나는 관계형 데이터베이스를 모든 데이터 표현 문제에 너무 많이 적용했기 때문입니다. 이는 게으른 설계의 일부입니다.

  2. Model에서 SQL 사용
    프로그래밍에서 한 가지 진실된 방법이 있다고 주장하는 것은 매우 위험합니다.
    그러나 제 경험 상으로는 객체 지향 코드에서 관계형 데이터를 나타내는 최고의 방법은 여전히 Model 레이어를 통해 입증되었습니다.
    데이터 표현을 코드의 한 영역으로 캡슐화하는 것은 기본적으로 좋은 아이디어입니다.
    그러나 모델 레이어의 역할은 객체를 나타내는 것이 아니라 질문에 대답하는 것입니다.
    응용 프로그램의 데이터 요구에 대답하는 API를 제공하세요.
    이러한 답변은 가능한 간단하고 효율적으로 처리되어야 합니다. 때로는 이러한 답변이 매우 구체적이며, 노련한 객체 지향 개발자에게 조차도 잘못 보이는 방식일 수 있지만 경험을 통해 여러 쿼리 메소드를 리팩터링할 수 있는 공통점을 찾는 것이 능숙해질 것입니다.
    마찬가지로, 때로는 결과가 단일 객체 X일 수도 있으며, 이를 쉽게 나타낼 수 있습니다.
    그러나 때로는 출력이 집계 데이터의 그리드이거나 단일 정수 카운트일 수도 있습니다.
    이러한 출력물들을 너무 많은 추상화 레이어로 둘러싸는 유혹을 떨쳐버리고, 데이터를 그 자체로 처리하세요.
    무엇보다도 객체 지향의 오류를 극복하세요.
    객체 지향은 추상화의 아름답고 매우 유연한 방법 중 하나입니다.
    하지만 관계형 데이터는 그 한계 중 하나이며, 객체가 할 수 없는 것을 할 수 있다고 가장하는 것은 모든 ORM의 근본적이고 근원적인 문제입니다.

요약하자면 (TL;DR)
1. ORM은 SQL 기반 모델 코드보다 처음에는 이해하기 쉽고 빠르게 작성됩니다.
2. 프로젝트가 복잡해질수록 이점이 사라지고, 추상화가 깨지며, 개발자는 SQL을 이해하고 사용해야 합니다.
3. 완전히 경험적으로 말하자면, ORM의 추상화는 20%의 프로젝트에는 해당하지 않을지라도 거의 100%의 프로젝트에서 무너집니다.
4. 객체는 관계형 쿼리 결과를 적절하게 나타낼 수 없습니다.
5. 객체 매핑의 불충분함으로 인해 ORM을 사용한 애플리케이션에서는 지속적이고 분산된 효율성 문제가 발생하며, ORM을 완전히 버리지 않으면 쉽게 해결할 수 없습니다.
6. 모든 것을 위해 관계형 저장소와 ORM을 사용하는 것보다 디자인에 더 신중히 접근하세요.
7. 데이터가 객체인 경우 객체 저장소("NoSQL")를 사용하세요. 관계형 데이터베이스보다 훨씬 빠를 것입니다.
8. 데이터가 관계적인 경우, 관계형 데이터베이스의 오버헤드는 가치가 있습니다.
9. 관계형 쿼리를 모델 레이어에 캡슐화하지만, 응용 프로그램의 특정 데이터 요구에 대한 API를 디자인하세요. 너무 많은 추상화 계층으로 묶이지 않도록 하세요.
10. 객체 지향 디자인은 효율적인 방식으로 관계형 데이터를 나타낼 수 없으며, ORM은 이러한 객체 지향 디자인의 근본적인 한계를 해결할 수 없습니다.

참조:
https://seldo.com/posts/orm_is_an_antipattern
https://seldo.com/posts/in_defence_of_sql

2개의 댓글

comment-user-thumbnail
2023년 7월 25일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

답글 달기
comment-user-thumbnail
2023년 7월 27일

좋은 글 변역해주셔서 감사합니다.

답글 달기