다음을 통해 공유


파괴적 변경

.NET 라이브러리는 기존 사용자의 안정성과 미래를 위한 혁신 간의 균형을 찾는 것이 중요합니다. 라이브러리 작성자는 완벽할 때까지 코드를 리팩터링하고 재고하는 쪽으로 기울지만, 기존 사용자를 깨는 것은 특히 하위 수준 라이브러리에 부정적인 영향을 미칩니다.

프로젝트 형식 및 호환성이 손상되는 변경

.NET 커뮤니티에서 라이브러리를 사용하는 방식에 따라 브레이킹 체인지가 최종 사용자 개발자에게 미치는 영향이 달라집니다.

  • 하위 및 중간 수준의 라이브러리인 직렬 변환기, HTML 파서, 데이터베이스 개체 관계형 매퍼 또는 웹 프레임워크는 호환성을 깨뜨리는 변경의 영향을 가장 많이 받습니다.

    빌드 블록 패키지는 최종 사용자 개발자가 애플리케이션을 빌드하고 다른 라이브러리에서 NuGet 종속성으로 사용하는 데 사용됩니다. 예를 들어 애플리케이션을 빌드하고 오픈 소스 클라이언트를 사용하여 웹 서비스를 호출합니다. 클라이언트에서 사용하는 종속성에 대한 호환성이 손상되는 업데이트는 해결할 수 있는 것이 아닙니다. 변경해야 하는 오픈 소스 클라이언트이며 제어할 수 없습니다. 호환되는 버전의 라이브러리를 찾거나 클라이언트 라이브러리에 수정 사항을 제출하고 새 버전을 기다려야 합니다. 최악의 경우는 서로 호환되지 않는 세 번째 라이브러리 버전에 의존하는 두 개의 라이브러리를 사용하려는 경우입니다.

  • UI 컨트롤 제품군과 같은 상위 수준 라이브러리는 호환성이 손상되는 변경에 덜 민감합니다.

    상위 수준 라이브러리는 최종 사용자 애플리케이션에서 직접 참조됩니다. 호환성이 손상되는 변경이 발생하는 경우 개발자는 최신 버전으로 업데이트하도록 선택하거나 호환성이 손상되는 변경에 맞게 애플리케이션을 수정할 수 있습니다.

✔️ 라이브러리가 어떻게 사용되는지 생각해 보세요. 호환성이 손상되는 변경은 이를 사용하는 애플리케이션 및 라이브러리에 어떤 영향을 미치나요?

✔️ 하위 수준 .NET 라이브러리를 개발할 때 호환성이 손상되는 변경을 최소화하세요.

✔️ 라이브러리의 주요 다시 쓰기를 새 NuGet 패키지로 게시하는 것이 좋습니다.

호환성이 손상되는 변경 유형

호환성에 영향을 줄 수 있는 변경은 서로 다른 범주에 속하며 그 영향은 각기 다릅니다.

호환성을 깨는 소스 변경

원본 호환성이 손상되는 변경은 프로그램 실행에 영향을 주지 않지만 다음에 애플리케이션을 다시 컴파일할 때 컴파일 오류가 발생합니다. 예를 들어 새 오버로드는 이전에 모호하지 않았던 메서드 호출에서 모호성을 만들 수 있으며, 이름이 바뀐 매개 변수는 명명된 매개 변수를 사용하는 호출자를 중단합니다.

public class Task
{
    // Adding a type called Task could conflict with System.Threading.Tasks.Task at compilation
}

소스 코드 변경으로 인한 손상은 개발자가 애플리케이션을 다시 컴파일할 때만 해로우며, 가장 적은 영향을 미치는 변경입니다. 개발자는 자체의 끊어진 소스 코드를 쉽게 수정할 수 있습니다.

동작 호환성이 손상되는 변경

동작 변경은 호환성이 손상되는 변경의 가장 일반적인 유형입니다. 거의 모든 동작 변경으로 인해 소비자에게 논리 오류가 발생할 수 있습니다. 메서드 시그니처, 발생된 예외, 입출력 데이터 형식 등의 라이브러리 변경 사항은 라이브러리 사용자에게 부정적인 영향을 미칠 수 있습니다. 사용자가 이전에 끊어진 동작에 의존한 경우 버그 수정이라도 호환성이 손상되는 변경으로 간주할 수 있습니다.

기능을 추가하고 잘못된 동작을 개선하는 것은 좋은 일이지만, 신경 쓰지 않으면 기존 사용자가 업그레이드하기가 매우 어려워질 수 있습니다. 개발자가 동작 호환성이 손상되는 변경 내용을 처리하도록 돕는 한 가지 방법은 설정 뒤에 숨기는 것입니다. 설정을 통해 개발자가 최신 버전의 라이브러리로 업데이트하는 동시에 중대한 변화에 대해 옵트인 또는 옵트아웃할 수 있습니다. 이 전략을 통해 개발자는 시간이 지남에 따라 소비 코드가 적응할 수 있도록 하면서 최신 상태를 유지할 수 있습니다.

예를 들어 ASP.NET Core MVC에는 사용하도록 설정되고 사용하지 않도록 설정된 기능을 수정하는 호환성 버전 개념이 있습니다 MvcOptions.

✔️ 기존 사용자에게 영향을 미치는 경우 기본적으로 새 기능을 해제하고 개발자가 설정을 사용하여 기능을 옵트인하도록 하는 것이 좋습니다.

.NET API의 동작 호환성이 손상되는 변경에 대한 자세한 내용은 .NET 동작 변경 호환성을 참조하세요.

바이너리 호환성을 깨뜨리는 변경

라이브러리의 공용 API를 변경할 때 이진 호환성이 손상되는 변경이 발생하므로 이전 버전의 라이브러리에 대해 컴파일된 어셈블리는 더 이상 API를 호출할 수 없습니다. 예를 들어, 메서드의 서명을 새 매개 변수를 추가하여 변경하면, 이전 버전의 라이브러리에 대해 컴파일된 어셈블리가 MissingMethodException 오류를 발생시킬 수 있습니다.

이진 호환성 파괴 변경은 전체 어셈블리를 중단시킬 수도 있습니다. 어셈블리의 이름을 바꾸면 어셈블리 AssemblyName 의 강력한 명명 키를 추가, 제거 또는 변경하는 것처럼 어셈블리의 ID가 변경됩니다. 어셈블리의 ID를 변경하면 어셈블리를 사용하는 컴파일된 모든 코드가 중단됩니다.

❌ 어셈블리 이름을 변경하지 마세요.

❌ 강력한 명명 키를 추가, 제거 또는 변경하지 마세요.

✔️ 인터페이스 대신 추상 기본 클래스를 사용하는 것이 좋습니다.

인터페이스에 아무것도 추가하면 인터페이스를 구현하는 기존 형식이 실패하게 됩니다. 추상 기본 클래스를 사용하면 기본 가상 구현을 추가할 수 있습니다.

형식 및 멤버를 제거하려면 ObsoleteAttribute 배치를 고려합니다. 더 이상 사용되지 않는 API를 사용하지 않는 코드를 업데이트하기 위한 지침이 특성에 있어야 합니다.

형식 및 메서드 ObsoleteAttribute 를 호출하는 코드는 특성에 제공된 메시지와 함께 빌드 경고를 생성합니다. 이 경고는 구식의 API를 사용하는 사람들이 API가 제거될 때 대부분 더 이상 사용하지 않도록 마이그레이션 할 시간을 제공합니다.

public class Document
{
    [Obsolete("LoadDocument(string) is obsolete. Use LoadDocument(Uri) instead.")]
    public static Document LoadDocument(string uri)
    {
        return LoadDocument(new Uri(uri));
    }

    public static Document LoadDocument(Uri uri)
    {
        // Load the document
    }
}

✔️ 하위 및 중간 수준 라이브러리에서 형식 및 메서드를 ObsoleteAttribute 무기한으로 유지하는 것이 좋습니다.

API를 제거하는 것은 이진 호환성이 손상되는 변경입니다. 오래된 형식 및 메서드를 유지 관리하는 것은 비용이 저렴하고 라이브러리에 많은 기술적 부담이 추가되지 않는 경우 고려합니다. 형식 및 메서드를 제거하지 않으면 위에서 언급한 최악의 시나리오를 방지하는 데 도움이 될 수 있습니다.

.NET API 변경 내용이 이진 호환성을 손상하는 것에 대한 자세한 내용은 .NET 퍼블릭 계약 호환성을 참조하세요.

참고하십시오