Entity Framework(Code First)는 개발자가 도메인 중심의 POCO 클래스를 작성하고, 이를 기반으로 데이터베이스 스키마를 자동 생성하거나 기존 데이터베이스와 매핑할 수 있도록 해주는 ORM 기술입니다. 이 과정에서 "규칙보다 구성 우선" 원칙에 따라, 클래스가 기본 규칙을 따르지 않을 경우 DataAnnotations 또는 Fluent API를 통해 명시적인 설정을 추가할 수 있습니다. 본 문서에서는 주로 DataAnnotations를 활용한 구성 방법을 설명합니다.
[Key] – 기본 키 정의
기본 키를 지정하지 않으면 EF는 Id 또는 클래스명 + Id(예: BlogId) 형태의 속성을 자동으로 기본 키로 인식합니다. 만약 해당 규칙을 따르지 않는 경우, [Key] 특성을 사용하여 명시적으로 기본 키를 지정해야 합니다.
[Key]
public int EntryIdentifier { get; set; }
값 형식(int 등)의 기본 키 속성은 기본적으로 IDENTITY(자동 증가) 컬럼으로 생성됩니다.
[Required] – 필수 필드 설정
해당 속성이 반드시 값이 있어야 함을 나타냅니다. 이 특성은 다음과 같은 영향을 미칩니다:
- 데이터베이스 컬럼을
NOT NULL로 생성 - MVC 등의 프레임워크에서 클라이언트 및 서버 측 유효성 검사 수행
[Required(ErrorMessage = "제목은 필수 입력 항목입니다.")]
public string Title { get; set; }
[StringLength], [MaxLength], [MinLength] – 문자열 길이 제한
문자열 속성의 최대/최소 길이를 지정할 수 있으며, 이 정보는 데이터베이스 스키마에도 반영됩니다.
[StringLength(100, MinimumLength = 3, ErrorMessage = "블로거 이름은 3~100자 사이여야 합니다.")]
public string AuthorName { get; set; }
[MaxLength]는 최대 길이만 제한하며, [StringLength]은 최소/최대 모두 지정 가능합니다.
[Column] – 컬럼 이름 및 타입 지정
속성과 매핑되는 데이터베이스 컬럼의 이름이나 데이터 타입을 직접 지정할 수 있습니다.
[Column("ArticleContent", TypeName = "nvarchar(max)")]
public string Content { get; set; }
이 경우 속성명은 Content이지만, 데이터베이스에는 ArticleContent라는 이름의 nvarchar(max) 컬럼으로 생성됩니다.
[Table] – 테이블 이름 지정
클래스와 매핑되는 테이블 이름을 명시적으로 지정합니다. 기본적으로 복수형 이름(Blogs)이 사용됩니다.
[Table("BlogEntries")]
public class Blog
{
// ...
}
[NotMapped] – 데이터베이스 매핑 제외
해당 속성을 데이터베이스에 매핑하지 않도록 합니다. 계산된 읽기 전용 값 등에 유용합니다.
[NotMapped]
public string Summary
{
get
{
return Title?.Length > 50 ? Title.Substring(0, 50) + "..." : Title;
}
}
[ComplexType] – 복합 타입 정의
기본 키 없이 여러 속성을 그룹화한 클래스를 다른 엔티티 내에 포함할 수 있습니다. 이 클래스는 자체 테이블이 아닌 포함된 엔티티의 테이블에 컬럼으로 확장됩니다.
[ComplexType]
public class ArticleMetadata
{
public DateTime CreatedAt { get; set; }
public DateTime? ModifiedAt { get; set; }
[MaxLength(200)]
public string Tags { get; set; }
}
// 사용 예:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public ArticleMetadata Metadata { get; set; } // → Blog 테이블에 Metadata_CreatedAt 등으로 생성됨
}
[ConcurrencyCheck] – 낙관적 동시성 제어
엔티티를 수정하거나 삭제할 때, 해당 속성의 원래 값을 조건 절에 포함시켜 동시성 충돌을 감지합니다.
[ConcurrencyCheck]
public string AuthorName { get; set; }
UPDATE 쿼리 실행 시 WHERE 절에 기본 키 외에도 AuthorName의 초기 값이 포함되어, 중간에 변경되었을 경우 DbUpdateConcurrencyException이 발생합니다.
[Timestamp] – 바이너리 동시성 토큰
더 강력한 동시성 제어를 위해 rowversion 또는 timestamp 컬럼을 사용합니다. 이 특성은 byte[] 타입의 속성에만 적용 가능하며, 하나의 엔티티 클래스당 하나만 가질 수 있습니다.
[Timestamp]
public byte[] RowVersion { get; set; }
EF는 이 컬럼을 자동으로 NOT NULL이며, 행이 변경될 때마다 데이터베이스에서 새로운 값을 할당받도록 처리합니다.
[DatabaseGenerated] – 데이터베이스 생성 값 관리
속성 값이 데이터베이스에서 자동 생성되는 방식을 지정합니다.
DatabaseGeneratedOption.Identity: 자동 증가 (기본 키에 기본 적용)DatabaseGeneratedOption.Computed: 계산된 값 (예:GETDATE())DatabaseGeneratedOption.None: 애플리케이션에서 직접 제공
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; set; }
Computed 속성은 EF가 INSERT/UPDATE 시 해당 컬럼을 포함하지 않으며, 삽입 후 DB에서 반환된 값을 자동으로 로드합니다. 이 속성에는 [Required]를 붙이지 않아도 되며, 대부분의 값 형식은 기본값이 존재하므로 데이터베이스에서도 NOT NULL이 됩니다.
[ForeignKey] – 외래 키 관계 설정
기본 네이밍 규칙(클래스명 + Id)을 따르지 않을 경우, 외래 키 관계를 명시적으로 지정합니다.
public class Post
{
public int Id { get; set; }
public int BlogEntryId { get; set; } // 규칙에 맞지 않음
[ForeignKey("BlogEntryId")]
public virtual Blog Blog { get; set; }
}
[InverseProperty] – 다중 관계 매핑
두 엔티티 간에 여러 관계가 존재할 경우, 어떤 내비게이션 속성이 서로 연결되는지를 명확히 지정합니다.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("CreatedBy")]
public virtual ICollection<Article> ArticlesAuthored { get; set; }
[InverseProperty("UpdatedBy")]
public virtual ICollection<Article> ArticlesEdited { get; set; }
}
public class Article
{
public int Id { get; set; }
public virtual Person CreatedBy { get; set; }
public virtual Person UpdatedBy { get; set; }
}
이 설정을 하지 않으면 EF는 모호성을 해결하지 못하고 불필요한 외래 키를 생성할 수 있습니다.