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.