EF Core 매핑 EntityTypeConfiguration


129

EF6에서는 일반적으로이 방법을 사용하여 엔티티를 구성 할 수 있습니다.

public class AccountMap : EntityTypeConfiguration<Account>
{
    public AccountMap()
    {
        ToTable("Account");
        HasKey(a => a.Id);

        Property(a => a.Username).HasMaxLength(50);
        Property(a => a.Email).HasMaxLength(255);
        Property(a => a.Name).HasMaxLength(255);
    }
}

클래스 I Inherit EntityTypeConfiguration이 클래스를 찾을 수 없기 때문에 EF Core에서 할 수있는 방법.

GitHub에서 EF Core 원시 소스 코드를 다운로드했는데 찾을 수 없습니다. 누군가 이것을 도울 수 있습니까?


8
왜 그 대답을 받아들이지 않습니까?
— Den

이제 beta5에서 maxLength (50)을 입력하면. db에서 nvarchar (max) 생성
— Herman

6
여기에 관심이있는 다른 사람을 위해 구현할 수 있는 IEntityTypeConfiguration<T>with one void Configure()메서드가 있습니다. 여기에 세부 정보 : github.com/aspnet/EntityFramework/pull/6989
— Galilyou

답변:


183

EF Core 2.0부터 IEntityTypeConfiguration<TEntity>. 다음과 같이 사용할 수 있습니다.

class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
  public void Configure(EntityTypeBuilder<Customer> builder)
  {
     builder.HasKey(c => c.AlternateKey);
     builder.Property(c => c.Name).HasMaxLength(200);
   }
}

...
// OnModelCreating
builder.ApplyConfiguration(new CustomerConfiguration());

이 기능과 2.0에 도입 된 기타 새로운 기능에 대한 자세한 내용은 여기 에서 확인할 수 있습니다 .


8
이것은 EF Core 2.0에 대한 최상의 답변입니다. 감사!
— Collin M. Barrett

2
이것은 훌륭합니다. 유창한 API 정의를 분리하는 방법을 찾고있었습니다. 감사합니다
— Blaze

"ToTable"및 "HasColumnName"등에 대한이 답변도 참조하십시오. ::: stackoverflow.com/questions/43200184/…
— granadaCoder

man 사용자 지정 구성 builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);이있는 경우 모든 사용자 지정 구성 을 적용합니다
— alim91

52

몇 가지 간단한 추가 유형을 통해이를 달성 할 수 있습니다.

internal static class ModelBuilderExtensions
{
   public static void AddConfiguration<TEntity>(
     this ModelBuilder modelBuilder, 
     DbEntityConfiguration<TEntity> entityConfiguration) where TEntity : class
   {     
       modelBuilder.Entity<TEntity>(entityConfiguration.Configure);
   }
}

internal abstract class DbEntityConfiguration<TEntity> where TEntity : class
{     
    public abstract void Configure(EntityTypeBuilder<TEntity> entity);
}

용법:

internal class UserConfiguration : DbEntityConfiguration<UserDto>
{
    public override void Configure(EntityTypeBuilder<UserDto> entity)
    {
        entity.ToTable("User");
        entity.HasKey(c => c.Id);
        entity.Property(c => c.Username).HasMaxLength(255).IsRequired();
        // etc.
    }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.AddConfiguration(new UserConfiguration());
}

1
어디 ForSqlServerToTable()있어요?
— im1dermike


1
이것 과 함께 HasColumnType 을 사용하는 방법 ? . 예를 들어. entity.Property(c => c.JoinDate).HasColumnType("date");
— Biju Soman

OnModelCreating을 요구하도록 업데이트되었습니다 DbModelBuilder. 여기에 구성을 추가하는 방법은 지금modelBuilder.Configurations.Add(new UserConfiguration());
— Izzy

2
@Izzy-DbModelBuilder는 Entity Framework 6.0이고 ModelBuilder는 EF Core입니다. 그것들은 다른 어셈블리이며이 경우 질문은 EF Core에만 해당됩니다.
— Jason

29

EF7에서는 구현중인 DbContext 클래스에서 OnModelCreating을 재정의합니다.

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Account>()
            .ForRelational(builder => builder.Table("Account"))
            .Property(value => value.Username).MaxLength(50)
            .Property(value => value.Email).MaxLength(255)
            .Property(value => value.Name).MaxLength(255);
    }

23
20 개의 엔티티 유형 구성이 있다면 하나의 거대한 방법에 넣을까요?
— 덴

6
기본적으로 그렇게 보입니다. 기본 클래스를 확장하고 형식화 된 EntityBuilder <Foo>를 전달하는 메서드가있는 고유 한 FooMapper / FooModelBuilder 클래스를 만들 수 있습니다. 새로운 의존성 주입 및 IConfiguration 인터페이스를 사용하여 자동으로 검색 / 호출하도록 할 수도 있습니다.
— Avi Cherry

1
천만에요. 답변에 찬성 투표 (그리고 질문자가 수락하도록 권장)하는 것이 훨씬 좋습니다!
— Avi Cherry

나는 보통 이것을한다 :)
— Den

4
새로운 의존성 주입 도구를 사용해 보시겠습니까? 확인 IEntityMapperStrategyA의 인터페이스 void MapEntity(ModelBuilder, Type)서명을하고 bool IsFor(Type). 원하는만큼 인터페이스를 구현 한 다음 (원하는 경우 둘 이상의 엔티티를 매핑 할 수있는 클래스를 만들 수 있도록) IEnumerable모든 .NET Framework를 주입하는 다른 클래스 (전략 공급자)를 IEntityMapperStrategies만듭니다. '특수 유형'에서 여기 를 참조 하십시오 . 그것을 당신의 맥락에 주입하십시오.
— Avi Cherry

22

현재 최신 베타 8을 사용하고 있습니다. 다음을 시도해보세요.

public class AccountMap
{
    public AccountMap(EntityTypeBuilder<Account> entityBuilder)
    {
        entityBuilder.HasKey(x => x.AccountId);

        entityBuilder.Property(x => x.AccountId).IsRequired();
        entityBuilder.Property(x => x.Username).IsRequired().HasMaxLength(50);
    }
}

그런 다음 DbContext에서 :

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        new AccountMap(modelBuilder.Entity<Account>());
    }

3
나는 이것과 비슷한 일을 끝냈다. 그래도 생성자 대신 정적 메서드를 사용하기로 결정했습니다.
— Matt Sanders

이 방법론을 사용하고 있으며 지금까지 상속 외에는 문제가 없었습니다. 예제의 AccountMap을 새 것으로 상속하고 대체 키를 추가하려는 경우 가장 좋은 방법은 무엇입니까?
— chris

14

리플렉션을 사용하여 각 엔터티에 대한 별도의 매핑 클래스를 사용하여 EF6에서 작동하는 방식과 매우 유사한 작업을 수행 할 수 있습니다. 이것은 RC1 final에서 작동합니다.

먼저 매핑 유형에 대한 인터페이스를 만듭니다.

public interface IEntityTypeConfiguration<TEntityType> where TEntityType : class
{
    void Map(EntityTypeBuilder<TEntityType> builder);
}

그런 다음 각 엔티티 (예 : 클래스)에 대한 매핑 클래스를 만듭니다 Person.

public class PersonMap : IEntityTypeConfiguration<Person>
{
    public void Map(EntityTypeBuilder<Person> builder)
    {
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Name).IsRequired().HasMaxLength(100);
    }
}

지금의 반사 마법 OnModelCreating귀하의 DbContext구현 :

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    // Interface that all of our Entity maps implement
    var mappingInterface = typeof(IEntityTypeConfiguration<>);

    // Types that do entity mapping
    var mappingTypes = typeof(DataContext).GetTypeInfo().Assembly.GetTypes()
        .Where(x => x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));

    // Get the generic Entity method of the ModelBuilder type
    var entityMethod = typeof(ModelBuilder).GetMethods()
        .Single(x => x.Name == "Entity" && 
                x.IsGenericMethod && 
                x.ReturnType.Name == "EntityTypeBuilder`1");

    foreach (var mappingType in mappingTypes)
    {
        // Get the type of entity to be mapped
        var genericTypeArg = mappingType.GetInterfaces().Single().GenericTypeArguments.Single();

        // Get the method builder.Entity<TEntity>
        var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg);

        // Invoke builder.Entity<TEntity> to get a builder for the entity to be mapped
        var entityBuilder = genericEntityMethod.Invoke(builder, null);

        // Create the mapping type and do the mapping
        var mapper = Activator.CreateInstance(mappingType);
        mapper.GetType().GetMethod("Map").Invoke(mapper, new[] { entityBuilder });
    }
}

DataContext및 .Where사용 하는 참조는 무엇입니까 ? 나는 이것을 위해 별도의 프로젝트를 만들었고 참조를 찾지 못하는 것 같습니다.
— Ruchan

.Whereis System.Linq, DataContext코드가 추가 된 클래스입니다 (내 EF DbContextimpl)
— Cocowalla

12

EF Core 2.2부터는 DbContext 클래스에서 상속 된 클래스의 OnModelCreating 메서드에서 한 줄에 모든 구성 (IEntityTypeConfiguration 인터페이스를 구현 한 클래스)을 추가 할 수 있습니다.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //this will apply configs from separate classes which implemented IEntityTypeConfiguration<T>
    modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}

그리고 이전 답변에서 언급했듯이 EF Core 2.0부터 IEntityTypeConfiguration 인터페이스를 구현하고 Configure 메서드에서 FluentAPI를 사용하여 매핑 구성을 설정할 수 있습니다.

public class QuestionAnswerConfig : IEntityTypeConfiguration<QuestionAnswer>
{
    public void Configure(EntityTypeBuilder<QuestionAnswer> builder)
    {
      builder
        .HasKey(bc => new { bc.QuestionId, bc.AnswerId });
      builder
        .HasOne(bc => bc.Question)
        .WithMany(b => b.QuestionAnswers)
        .HasForeignKey(bc => bc.QuestionId);
      builder
        .HasOne(bc => bc.Answer)
        .WithMany(c => c.QuestionAnswers)
        .HasForeignKey(bc => bc.AnswerId);
    }
}

6

이것이 제가 현재 작업중인 프로젝트에서하고있는 일입니다.

public interface IEntityMappingConfiguration<T> where T : class
{
    void Map(EntityTypeBuilder<T> builder);
}

public static class EntityMappingExtensions
{
     public static ModelBuilder RegisterEntityMapping<TEntity, TMapping>(this ModelBuilder builder) 
        where TMapping : IEntityMappingConfiguration<TEntity> 
        where TEntity : class
    {
        var mapper = (IEntityMappingConfiguration<TEntity>)Activator.CreateInstance(typeof (TMapping));
        mapper.Map(builder.Entity<TEntity>());
        return builder;
    }
}

용법:

컨텍스트의 OnModelCreating 메서드에서 :

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder
            .RegisterEntityMapping<Card, CardMapping>()
            .RegisterEntityMapping<User, UserMapping>();
    }

매핑 클래스의 예 :

public class UserMapping : IEntityMappingConfiguration<User>
{
    public void Map(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("User");
        builder.HasKey(m => m.Id);
        builder.Property(m => m.Id).HasColumnName("UserId");
        builder.Property(m => m.FirstName).IsRequired().HasMaxLength(64);
        builder.Property(m => m.LastName).IsRequired().HasMaxLength(64);
        builder.Property(m => m.DateOfBirth);
        builder.Property(m => m.MobileNumber).IsRequired(false);
    }
}

Visual Studio 2015의 접기 동작을 활용하기 위해 제가 좋아하는 또 하나는 'User'라는 엔티티에 대한 것입니다. 매핑 파일의 이름을 'User.Mapping.cs'로 지정하면 Visual Studio가 솔루션 탐색기에서 파일을 접습니다. 엔티티 클래스 파일 아래에 포함되도록합니다.


귀하의 솔루션에 감사드립니다. 내 프로젝트가 끝날 때 솔루션 코드를 최적화 할 것입니다. 나중에 확인합니다.
— Miroslav Siska 2016

'IEntityTypeConfiguration <T>'만 가정 할 수 Configure(builder)있으며 2016에는 존재하지 않았습니까? TypeConfiguration을 가리 키도록 배선을 약간 변경하면 '추가'인터페이스가 필요하지 않습니다.
— WernerCD

3

이 솔루션으로 끝났습니다.

public interface IEntityMappingConfiguration
{
    void Map(ModelBuilder b);
}

public interface IEntityMappingConfiguration<T> : IEntityMappingConfiguration where T : class
{
    void Map(EntityTypeBuilder<T> builder);
}

public abstract class EntityMappingConfiguration<T> : IEntityMappingConfiguration<T> where T : class
{
    public abstract void Map(EntityTypeBuilder<T> b);

    public void Map(ModelBuilder b)
    {
        Map(b.Entity<T>());
    }
}

public static class ModelBuilderExtenions
{
    private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface)
    {
        return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
    }

    public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly)
    {
        var mappingTypes = assembly.GetMappingTypes(typeof (IEntityMappingConfiguration<>));
        foreach (var config in mappingTypes.Select(Activator.CreateInstance).Cast<IEntityMappingConfiguration>())
        {
            config.Map(modelBuilder);
        }
    }
}

샘플 사용 :

public abstract class PersonConfiguration : EntityMappingConfiguration<Person>
{
    public override void Map(EntityTypeBuilder<Person> b)
    {
        b.ToTable("Person", "HumanResources")
            .HasKey(p => p.PersonID);

        b.Property(p => p.FirstName).HasMaxLength(50).IsRequired();
        b.Property(p => p.MiddleName).HasMaxLength(50);
        b.Property(p => p.LastName).HasMaxLength(50).IsRequired();
    }
}

과

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
}

컴파일 시간 오류가 발생합니다. " 연산자 '! x.IsAbstract'는 ModelBuilderExtenions.GetMappingTypes ()의 '! x.IsAbstract'(System.Type.IsAbstract)에있는 '방법 그룹'유형의 피연산자에 적용 할 수 없습니다 . . mscorlib에 대한 참조를 추가해야합니까? .NET Core 1.0 프로젝트에 어떻게 수행합니까?
— RandyDaddis 2011 년

.net 핵심 프로젝트 (netstandard 사용)의 경우 System.Reflection 네임 스페이스에서 GetTypeInfo () 확장을 사용해야합니다. x.GetTypeInfo (). IsAbstract 또는 x.GetTypeInfo (). GetInterfaces ()로 사용
— animalito maquina

내 솔루션의 일부를 사용했으며 훌륭하게 작동했습니다. 감사!
— Diego Cotini

2

IEntityTypeConfiguration을 구현하십시오.

public abstract class EntityTypeConfiguration<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : class
{
    public abstract void Configure(EntityTypeBuilder<TEntity> builder);
}

그런 다음 엔티티 컨텍스트에 추가하십시오.

public class ProductContext : DbContext, IDbContext
{
    public ProductContext(DbContextOptions<ProductContext> options)
        : base((DbContextOptions)options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfiguration(new ProductMap());
    }

    public DbSet<Entities.Product> Products { get; set; }
}


1

Entity Framework Core 2.0에서 :

Cocowalla의 답변을 가져와 v2.0에 적용했습니다.

    public static class ModelBuilderExtenions
    {
        private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface)
        {
            return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
        }

        public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly)
        {
            // Types that do entity mapping
            var mappingTypes = assembly.GetMappingTypes(typeof(IEntityTypeConfiguration<>));

            // Get the generic Entity method of the ModelBuilder type
            var entityMethod = typeof(ModelBuilder).GetMethods()
                .Single(x => x.Name == "Entity" &&
                        x.IsGenericMethod &&
                        x.ReturnType.Name == "EntityTypeBuilder`1");

            foreach (var mappingType in mappingTypes)
            {
                // Get the type of entity to be mapped
                var genericTypeArg = mappingType.GetInterfaces().Single().GenericTypeArguments.Single();

                // Get the method builder.Entity<TEntity>
                var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg);

                // Invoke builder.Entity<TEntity> to get a builder for the entity to be mapped
                var entityBuilder = genericEntityMethod.Invoke(modelBuilder, null);

                // Create the mapping type and do the mapping
                var mapper = Activator.CreateInstance(mappingType);
                mapper.GetType().GetMethod("Configure").Invoke(mapper, new[] { entityBuilder });
            }
        }


    }

그리고 다음과 같이 DbContext에서 사용됩니다.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
    }

엔터티에 대한 엔터티 유형 구성을 만드는 방법은 다음과 같습니다.

    public class UserUserRoleEntityTypeConfiguration : IEntityTypeConfiguration<UserUserRole>
    {
        public void Configure(EntityTypeBuilder<UserUserRole> builder)
        {
            builder.ToTable("UserUserRole");
            // compound PK
            builder.HasKey(p => new { p.UserId, p.UserRoleId });
        }
    }

나를 위해 일하지 않았다. 예외 :Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
— Tohid

추신 : 해결책을 찾았습니다 : &&! t.IsGenericType. 일반 클래스 ( class EntityTypeConfigurationBase<TEntity> : IEntityTypeConfiguration<TEntity>) 인 기본 클래스가 있기 때문입니다 . 이 기본 클래스의 인스턴스를 만들 수 없습니다.
— Tohid

0

내가 맞아?

public class SmartModelBuilder<T> where T : class         {

    private ModelBuilder _builder { get; set; }
    private Action<EntityTypeBuilder<T>> _entityAction { get; set; }

    public SmartModelBuilder(ModelBuilder builder, Action<EntityTypeBuilder<T>> entityAction)
    {
        this._builder = builder;
        this._entityAction = entityAction;

        this._builder.Entity<T>(_entityAction);
    }
}   

구성을 전달할 수 있습니다.

 protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);



        new SmartModelBuilder<Blog>(builder, entity => entity.Property(b => b.Url).Required());

    } 

받아 들여지는 대답은 이것보다 낫습니다. 둘 다 OnModelCreating ()이 엄청나게 어수선한 것과 동일한 부정적인 부작용이 있지만 허용되는 답변에는 도우미 클래스가 필요하지 않습니다. 귀하의 답변이 개선되지 않은 부분이 있습니까?
— 항해 유도

0

Microsoft가 ForSqlServerToTable을 구현하는 방식과 유사한 접근 방식을 따랐습니다.

확장 방법 사용 중 ...

부분 여러 파일에서 같은 클래스 이름을 사용하려는 경우 플래그가 필요합니다

public class ConsignorUser
{
    public int ConsignorId { get; set; }

    public string UserId { get; set; }

    public virtual Consignor Consignor { get; set; }
    public virtual User User { get; set; }

}

public static partial class Entity_FluentMappings
{
    public static EntityTypeBuilder<ConsignorUser> AddFluentMapping<TEntity> (
        this EntityTypeBuilder<ConsignorUser> entityTypeBuilder) 
        where TEntity : ConsignorUser
    {
       entityTypeBuilder.HasKey(x => new { x.ConsignorId, x.UserId });
       return entityTypeBuilder;
    }      
}

그런 다음 DataContext OnModelCreating 에서 각 확장에 대해 호출하십시오.

 public class DataContext : IdentityDbContext<User>
{

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);

        builder.Entity<ConsignorUser>().AddFluentMapping<ConsignorUser>();
        builder.Entity<DealerUser>().AddFluentMapping<DealerUser>();           

    }

이렇게 하면 다른 빌더 메서드에서 사용 하는 것과 동일한 패턴을 따릅니다.

당신은 무엇을합니까?



0

외부에서 엔티티를 구성 할 수있는 프로젝트가 있습니다. DbContext.OnModelCreating 각 엔터티를 상속하는 별도의 클래스로 구성합니다.StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration

먼저 구성하려는 클래스가있는 StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration<TEntity>위치 에서 상속하는 클래스를 만들어야 TEntity합니다.

using StaticDotNet.EntityFrameworkCore.ModelConfiguration;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class ExampleEntityConfiguration
    : EntityTypeConfiguration<ExampleEntity>
{
    public override void Configure( EntityTypeBuilder<ExampleEntity> builder )
    {
        //Add configuration just like you do in DbContext.OnModelCreating
    }
}

그런 다음 Startup 클래스에서 DbContext를 구성 할 때 모든 구성 클래스를 찾을 위치를 Entity Framework에 지정하기 만하면됩니다.

using StaticDotNet.EntityFrameworkCore.ModelConfiguration;

public void ConfigureServices(IServiceCollection services)
{
    Assembly[] assemblies = new Assembly[]
    {
        // Add your assembiles here.
    };

    services.AddDbContext<ExampleDbContext>( x => x
        .AddEntityTypeConfigurations( assemblies )
    );
}

공급자를 사용하여 유형 구성을 추가하는 옵션도 있습니다. 저장소에는 사용 방법에 대한 완전한 문서가 있습니다.

https://github.com/john-t-white/StaticDotNet.EntityFrameworkCore.ModelConfiguration


여러 질문에 동일한 답변을 게시하지 마십시오. 동일한 정보가 실제로 두 질문에 모두 답하는 경우 한 질문 (일반적으로 최신 질문)을 다른 질문의 중복으로 닫아야합니다. 다음과 같은 방법으로이를 표시 할 수 있습니다 중복으로 닫 투표 또는, 당신이 충분한 명성을하지 않은 경우, 플래그 인상 이 중복의 것을 나타냅니다. 그렇지 않으면 이 질문에 대한 답변을 조정 하고 동일한 답변을 여러 곳에 붙여 넣지 마십시오.
— elixenide
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.