다들 EF Core 잘 쓰시나요?

EF Core를 쓰다보면 가끔 쿼리 성능이 느릴 경우가 있습니다.
SSMS에서 쿼리를 직접 실행하면 제가 원하는 인덱스를 타서 성능이 빠른데, 동일한 쿼리를 EF Core에서 사용하면 제가 원하는 인덱스를 타지 않는 경우가 있습니다. (프로파일러를 통해 검증)

또한 기본 키를 변경해야 하는 일이 생기면, SSMS에서는 ALTER문으로 변경이 가능하지만, EF Core에서는 기존 인덱스를 삭제하고 재생성해야 합니다.

이렇듯 SSMS와 EF Core에서 DB 작업 시 개념과 방법이 서로 다른 점이 많아 어려움을 겪는데 다들 편하게 사용이 가능하신가요??

5 Likes

EF Core 를 사용하는 이유는 아무래도, 코드 모델과 스키마를 언제나 일치 시킬 수 있다는 효율성과 안정성인 것 같습니다.

효율성과 안정성이라는 의미는…

많은 분들이 그러하듯, 처음 설계할 때, 마이그레이션으로 인한 관리가 아니라, 아래와 같은 방법을 선호합니다.

// Program.cs

// ...
var context = new MyDbContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();

// ...

위의 코드가 실행될 때 스키마에 문제가 있다면 EF 도구가 예외를 발생시키기에, 앱이 멈추는데, 디자인 도구가 표시해준 문제의 원인을 해결해나가면서 스키마를 빌드업해 나가기 때문에 코드 모델과 스키마 사이에 정합성을 담보할 수 있습니다.

우리가 컴파일러의 도움을 받으면서 코드를 작성하듯, EF 도구의 도움을 받으면서 스키마를 빌드업해 나가는 것이죠.

스키마의 뼈대가 어느 정도 완성되면, 초기 마이그레션을 커밋하고, 이때부터 마이그레이션을 통해 스키마를 관리하는 단계로 접어듭니다.(운영단계까지)

이 단계에서는 불가역적인 스키마 변경에 주의를 해야 하는데, EF 도구는 매 커밋마다 불가역적인 데이터 소실 위험이 감지되었을 때 경고해줍니다.

이러한 경고를 처리할 때, 많은 경우 마이그레이션을 수동으로 조작하는 등, DB 관리 능력이 요구됩니다.

그런데, 더 중요한 점은 데이터 베이스를 망칠 수 있는 실수를 리뷰해주는 존재가 있다는 점입니다. 우리는 마이그레이션이라는 샌드박스 위에서 안전하게 데이터를 유지할 수 있는 것이죠.

EF의 또 다른 장점은 DB의 설계를 코드로 하기 때문에, DDD 같은 디자인 패턴의 철학을 녹이는 것도 가능하다는 점입니다.

대표적으로 Aggregate Root 엔티티만 노출하고, 서브 엔티티는 숨기는 것이죠.
이는 코드가 서브 엔티티를 직접 수정하는 것을 방지하는 효과가 있습니다.

간단하게 설명하자면, User owns UserInfo 관계라면, IUserRepository만 제공하고, IUserInfoRepository는 제공하지 않는 것이죠.

이렇게 하면, 코드가 UserInfo 를 수정하는 경우, 반드시 IUserRepository를 통하는 것이 강제됩니다.

이는 Aggregate 를 하나의 Transaction 단위로 보라는 DDD의 규칙을

Aggregate 전체 상태가 한 번에 변경되던가 아니면 취소되던가.

unit of work 를 추상화한 DbContext 를 통해 구현하는 것입니다.

생쿼리에 익숙한 분들에게는 비효율적으로 보일 수 있는 부분이지만, 데이터 안정성을 위해서는 필요한 규칙이라고 생각합니다. (DDD 에서 괜히 Aggregate 개념을 강조한 것은 아니죠.)

물론, 생쿼리를 쓰면서도 동일한 규칙을 적용할 수도 있지만, 코드적 관리와 쿼리적 관리를 별도로, 그것도 수동으로 해야 합니다. 스키마가 복잡하게 성장할 수록 관리를 힘들게 하는 부분이라고 생각합니다.

EF 를 쓰면, 언어의 컴파일러와 EF 디자인 도구라는 도우미를 얻을 수 있고, 관리 포인트도 코드 하나라서 효율적입니다.

사실 아래의 글은 위 과정을 수행하면서 디자인 도구에게 뚜까 맞으면서 배운 것을 녹여 낸 것입니다.

EF 모델 패턴 연구 - :man_teacher: 튜토리얼, 팁, 강좌 - 닷넷데브 (dotnetdev.kr)

물론 기본적으로 EF를 써야 한다는 입장에 기반한 것이고, 제가 그런 입장을 가진 가장 큰 이유는 앞서 설명한 부분들 - 타입 안정성과 효율성 - 입니다.

설계 단계에서 이러한 EF의 장점을 뽑을 대로 다 뽑았다면, 운영 단계에서 쿼리 성능을 올리는 것은 개인의 역량과 선호의 영역일 것 같습니다.

예를 들어, 어떤 분들은 EF가 Linq로부터 생성한 쿼리를 프로시저로 정의하고, 그 프로시저를 직접 호출해서 쿼리(Read) 성능을 올리기도 하더군요.

특히 CQRS 를 도입하는 경우, 그런 경향이 강한 것 같습니다. (이 방식에 대한 반대 여론도 만만치 않습니다.)

물론 이를 위해서는 EF 가 생성한 쿼리를 이해하고 개선할 수 있는 높은 숙련도가 요구됩니다.

불행하게도 저는 해당되지 않기에, 코드로만 처리할 수 있는 EF 에 더 매달리는 것인지도 모르겠습니다.

8 Likes

말씀하신 것처럼 EF를 잘 쓰려면 결국 생성되는 쿼리를 어느정도 예측할정도의 레벨이 필수적이라고 봅니다.
일반적인 DB 설계이론이나 방법론적인 부분은 ORM 에 안맞는 경우가 많아서 발생하는 문제라고 생각하고 ORM 을 쓴다면 ORM에 맞는 형태의 테이블 디자인부터 해야한다고 봅니다.

저같은 경우는 최초설정 이후의 마이그레이션은 진행하지 않고 코드와 DB를 수작업으로 매칭하는 편이고,
또한 복잡한 조회쿼리의 경우는 sqlkata 나 직접 쿼리로 dapper로 처리합니다.

EF 만 고집하면 오히려 개발이나 유지보수가 너무 힘들어지는 경우가 종종 생기더라구요.

5 Likes

혹시 SplitQuery 를 사용해 보셨나요?

직접 쿼리 사용하는 것 만큼 정교할 수는 없겠지만,

성능에 상당히 도움이 된다고 느꼈습니다.

1 Like

EF 를 사용하면서 속도에 민감해지고 튜닝에 민감해지는 시기에 접어든다면
EF 를 계속 사용할 지 심각하게 고민을 해봐야 한다고 생각하는 편이에요.

특히 DBA 와 협업을 하는 환경이라면 더더욱 그렇슴다.

저는 갠적으루 EF 가 참 좋지만… 점점 블랙박스가 되어가는 느낌을 지울 수가 없어요.
앞서 언급하신 튜닝 문제도 그렇고,
IQueryable 이 가져다주는 유연함이 도리어 해가되는 상황도 많더라구요.

닷넷 프로젝트라하면 기본으로 EF 를 깔고 가지만
저는 EF 만능주의는 옳지 않다는 입장이에요.

사실 편의성이 가장 큰 유혹인데
이게 또 여러 사람들이 각자의 생각을 가지고 작업을 하다보면 이게 맞나... 하는 상황을 자주 만나게 됩니다.

문제를 해결해야하는 상황 앞에서 편의는 크게 의미가 없지만
이걸 포기 못하는 사람들이 더러 있거등요. =ㅁ=;;

Raw SQL 을 어떠한 경우에도 사용하지 않는 환경이 아니라면
DB 핸들링에 SQL 은 필수불가결할 것이고 그렇다면
저는 EF 보다는 차라리 MicroORM 이 더 나은 대안이다… 라고 생각하는 편입니다.

8 Likes

지금 튜닝 단계에 접어들면서 EF를 계속 사용하는 것이 맞는지 고민 중입니다. Index Rebuild 작업, Table Partition 작업이나 쿼리 성능 확인을 위해 실행 계획을 살펴보려면 결국 SQL Server에서 작업하게 됩니다.

물론, 초기에는 EF를 사용하면 매우 편리합니다. BigSquare님이 말씀하신 것처럼 코드 모델과 스키마를 일치시킬 수 있어서 테이블 디자인이 변경되더라도 마이그레이션을 통해 쉽게 적용할 수 있습니다. 하지만 결국 성능을 검사할 때 한계에 부딪히게 됩니다 ㅠㅠ

3 Likes

전 극혐합니다.
너무 불편해요

DT 가 젤루 편함…
좀 거시기 하면 Dapper 정도