0Pricing
C# Academy · 강의

전역 쿼리 필터와 소유 엔터티

전역 쿼리 필터로 테넌트 격리와 소프트 삭제를 적용하고 소유 엔터티 형식으로 값 객체를 매핑합니다.

전역 쿼리 필터와 소유 엔터티은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

전역 쿼리 필터란 무엇입니까?

전역 쿼리 필터는 특정 엔터티 형식에 대한 모든 LINQ 쿼리에 자동으로 적용되는 WHERE 조건식입니다. 어디에서나 조건을 반복하지 않고도 소프트 삭제, 멀티 테넌시, 행 수준 보안을 구현하는 데 적합합니다.

전역 필터를 사용한 소프트 삭제

IsDeleted 플래그를 추가하고 전역 필터를 구성하면, 필터를 명시적으로 무시하지 않는 한 삭제된 레코드가 반환되지 않습니다.

// Entity with soft-delete flag
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public bool IsDeleted { get; set; }
}

// Configure in OnModelCreating
modelBuilder.Entity<Post>()
    .HasQueryFilter(p => !p.IsDeleted);

// All queries now implicitly add WHERE IsDeleted = 0:
var posts = await _db.Posts.ToListAsync();

전역 필터를 사용한 멀티 테넌시

범위가 지정된 서비스에서 현재 테넌트를 읽는 필터를 사용하십시오. 모든 쿼리는 자동으로 현재 테넌트의 데이터 범위에 한정됩니다.

public class AppDbContext : DbContext
{
    private readonly ICurrentUserService _user;

    public AppDbContext(DbContextOptions<AppDbContext> opt, ICurrentUserService user)
        : base(opt) => _user = user;

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Order>().HasQueryFilter(
            o => o.TenantId == _user.TenantId);
    }
}
// Every Order query automatically filters by TenantId

전역 필터 무시

관리자가 모든 테넌트를 조회하거나 소프트 삭제된 레코드를 복원하는 경우처럼 필터를 우회해야 할 때가 있습니다. 해당 쿼리에만 IgnoreQueryFilters()를 사용하십시오.

// Admin: see ALL posts including deleted
var allPosts = await _db.Posts
    .IgnoreQueryFilters()
    .ToListAsync();

// Restore a specific deleted post
var deleted = await _db.Posts
    .IgnoreQueryFilters()
    .FirstOrDefaultAsync(p => p.Id == 42);

if (deleted is not null)
{
    deleted.IsDeleted = false;
    await _db.SaveChangesAsync();
}

탐색 속성의 필터

전역 필터는 JOIN에도 적용됩니다. 게시물 엔터티에 필터가 있으면 Include를 통해 로드한 관련 게시물에도 자동으로 필터가 적용됩니다.

// Blog with soft-delete filter on Posts
var blog = await _db.Blogs
    .Include(b => b.Posts) // only non-deleted posts are included
    .FirstOrDefaultAsync(b => b.Id == 1);

// blog.Posts contains only active posts — the filter applied in the JOIN

소유 엔터티 형식: 개념

소유 엔터티 형식은 하나의 소유 엔터티에만 독점적으로 속하는 클래스입니다. 기본 키가 없으며 기본적으로 같은 테이블에 저장됩니다. DDD의 값 개체에 적합합니다.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public Address BillingAddress  { get; set; } = new();
    public Address ShippingAddress { get; set; } = new();
}

public class Address
{
    public string Street  { get; set; } = "";
    public string City    { get; set; } = "";
    public string ZipCode { get; set; } = "";
    public string Country { get; set; } = "";
}

소유 엔터티 구성

OwnsOne을 사용하여 단일 소유 엔터티를 구성하십시오. EF Core는 기본적으로 탐색 속성 이름을 열 이름 앞에 붙입니다.

modelBuilder.Entity<Customer>(entity =>
{
    entity.OwnsOne(c => c.BillingAddress, addr =>
    {
        addr.Property(a => a.Street).HasMaxLength(300);
        addr.Property(a => a.Country).HasMaxLength(2);
    });

    entity.OwnsOne(c => c.ShippingAddress);
});
// Table: Customers with columns:
// BillingAddress_Street, BillingAddress_City, ...
// ShippingAddress_Street, ShippingAddress_City, ...

별도 테이블의 소유 엔터티

OwnsOne 내부에서 ToTable()을 사용하면 소유 엔터티를 자체 테이블에 저장할 수 있습니다. 데이터가 크거나 선택 사항일 때 유용합니다.

modelBuilder.Entity<Customer>().OwnsOne(
    c => c.BillingAddress,
    addr => addr.ToTable("CustomerBillingAddresses"));

// Now BillingAddress columns are in a separate table
// with a FK back to CustomerId

값 개체 컬렉션을 위한 OwnsMany

OwnsMany는 소유 엔터티 컬렉션을 구성합니다. 예를 들어 자식 테이블에 저장된 이메일 주소 목록이나 전화번호 목록에 사용할 수 있습니다.

public class Customer
{
    public int Id { get; set; }
    public IList<PhoneNumber> PhoneNumbers { get; set; } = new List<PhoneNumber>();
}

public class PhoneNumber
{
    public string Number { get; set; } = "";
    public string Type   { get; set; } = "";
}

modelBuilder.Entity<Customer>()
    .OwnsMany(c => c.PhoneNumbers,
        phone => phone.ToTable("CustomerPhones"));

계층별 테이블과 소유 엔터티 비교

전역 필터는 TPH(계층별 테이블) 상속과도 잘 작동하며, 이 상속에서는 판별자 열이 하위 형식을 구분합니다. 형식별 쿼리에는 판별자를 기준으로 필터를 적용하십시오.

// Base entity
public abstract class Payment { public int Id { get; set; } }
public class CardPayment  : Payment { public string CardLast4 { get; set; } = ""; }
public class BankPayment  : Payment { public string IBAN { get; set; } = ""; }

// All Payment subtypes in one table with a Discriminator column
// No extra config needed — EF Core handles TPH by default
var cards = await _db.Set<CardPayment>().ToListAsync();
// SQL: SELECT * FROM Payments WHERE Discriminator = 'CardPayment'

실전: 서비스형 소프트웨어의 테넌트 격리

완전한 서비스형 소프트웨어 멀티 테넌시 설정은 전역 쿼리 필터와 범위가 지정된 테넌트 컨텍스트를 결합합니다. 따라서 쿼리별 코드를 작성하지 않아도 모든 쿼리가 자동으로 해당 테넌트의 범위에 한정됩니다.

// Scoped tenant service
public class TenantContext
{
    public Guid TenantId { get; set; }
}

// DbContext uses it
modelBuilder.Entity<Invoice>().HasQueryFilter(
    inv => inv.TenantId == _tenantCtx.TenantId);
modelBuilder.Entity<Customer>().HasQueryFilter(
    c => c.TenantId == _tenantCtx.TenantId);

// Queries are automatically scoped:
var invoices = await _db.Invoices.ToListAsync();
// WHERE TenantId = 'current-tenant-guid'

빠른 확인

특정 쿼리에서 전역 쿼리 필터를 일시적으로 우회하려면 어떤 메서드를 호출해야 합니까?

복습: 전역 필터와 소유 엔터티

핵심 요점:

  • 전역 쿼리 필터는 엔터티에 대한 모든 쿼리에 WHERE 조건식을 자동으로 추가
  • 소프트 삭제, 멀티 테넌시, 행 수준 보안에 적합
  • 관리 작업이나 복원 작업에서는 IgnoreQueryFilters()로 우회
  • 소유 엔터티는 값 개체를 모델링 — PK가 없고 소유자의 테이블에 저장
  • OwnsOne / OwnsMany로 소유 관계를 구성하고, ToTable()을 사용하여 테이블을 분리

자주 묻는 질문

“전역 쿼리 필터와 소유 엔터티” 강의는 무료인가요?

네 — “전역 쿼리 필터와 소유 엔터티” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“전역 쿼리 필터와 소유 엔터티”에서 뭘 배우나요?

전역 쿼리 필터로 테넌트 격리와 소프트 삭제를 적용하고 소유 엔터티 형식으로 값 객체를 매핑합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C# Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“전역 쿼리 필터와 소유 엔터티” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 즉시, 지연, 명시적 로딩
  2. 원시 SQL, 저장 프로시저와 보간
  3. 컴파일된 쿼리와 성능
  4. 전역 쿼리 필터와 소유 엔터티
← C# Academy(으)로 돌아가기