Nullable 마이그레이션 전략

Tip

새 프로젝트를 시작하시겠습니까? .NET 6개 이상의 템플릿에서 만든 새 프로젝트에는 이미 <Nullable>enable</Nullable> 설정이 있습니다. 마이그레이션 전략은 필요하지 않습니다. nullable 경고 해결로 건너뛰세요.

기존 코드베이스를 유지 관리하시겠습니까? 먼저 Nullable 참조 형식 을 읽어 컨텍스트, 주석 및 null 상태를 이해합니다. 이 문서에서는 이러한 개념에 익숙하고 출시를 계획할 준비가 되었다고 가정합니다.

nullable 참조 형식이 도입되기 전에 시작된 큰 프로젝트에서 nullable 참조 형식을 켜면 컴파일러가 한 번에 많은 경고를 생성합니다. 마이그레이션은 작업의 순서를 정하는 일입니다. 즉, 기본 컨텍스트를 선택하고, 파일별 또는 섹션별로 경고를 표시하며, 전체 프로젝트에 대해 <Nullable>enable</Nullable>로 수렴해 가는 것입니다. 올바른 시퀀스는 코드베이스의 활성 정도와 단일 패스에서 취할 수 있는 위험에 따라 달라집니다.

최종 상태는 모든 경우에 동일합니다. 즉, 프로젝트는 <Nullable>enable</Nullable>를 설정하고 #nullable 전처리기 지시문을 포함하지 않습니다.

기본 컨텍스트 선택

nullable 컨텍스트에는 주석 (nullable 참조 형식을 선언하는지 여부 ? ) 및 경고 (컴파일러가 진단을 내보내는지 여부)의 두 가지 독립적인 플래그가 있습니다. 이러한 값을 단일 <Nullable> 값으로 설정합니다.

기본값 Annotations 경고 적합한 대상
disable (암시적) off off 이 패스에서는 새로운 기능 작업을 수행하지 않는 안정적인 라이브러리입니다.
enable on on 자주 사용하는 새 파일이 있는 활성 코드베이스입니다. 새 코드가 옵트인되기 시작합니다.
warnings off on 2단계 마이그레이션: 먼저 경고를 처리하고 나중에 주석을 추가합니다.
annotations on off 내부 경고를 수정하기 전에 공용 API에 주석을 추가합니다.

프로젝트 마이그레이션 목표와 가장 일치하는 전략을 선택합니다.

  • 기본값으로 사용하지 않도록 설정합니다. 각 파일을 마이그레이션할 때 파일 맨 위에 <Nullable>disable</Nullable>을 설정하고 #nullable enable을 추가하세요. 기존 파일은 터치할 때까지 nullable-oblivious 상태로 유지합니다. 이 옵션은 새 기능 작업이 드물기 때문에 안정적인 라이브러리에 대한 마찰이 가장 낮습니다.
  • 기본값으로 사용하도록 설정합니다. 아직 마이그레이션하지 않은 모든 파일의 맨 위에 <Nullable>enable</Nullable>을(를) 설정하고 #nullable disable을(를) 추가하세요. 모든 새 파일은 처음부터 nullable 인식되므로 마이그레이션 백로그만 축소할 수 있습니다. 개발이 활성화된 경우 이 선택이 더 좋습니다.
  • 기본값으로 경고합니다. <Nullable>warnings</Nullable>을 설정합니다. 2단계 마이그레이션에 대해 이 기본값을 선택합니다. 모든 참조 형식이 여전히 알 수 없는 것으로 처리되는 동안 경고를 처리한 다음 주석을 켭니다. 2단계 분할은 각 단계의 차이에 초점을 맞췄습니다.
  • 주석을 기본값으로 사용합니다. <Nullable>annotations</Nullable>을 설정합니다. 경고를 해결하려고 하기 전에, 먼저 ?를 허용하는 멤버에 null를 사용해 공용 API에 주석을 추가하세요. 컴파일러는 아직 경고를 내보내지 않으므로 방해하지 않고 API 표면을 해결할 수 있습니다.

프로젝트 파일은 전역 기본값을 제어합니다. #nullable 전처리기 지시문은 코드 영역에 대해 기본값을 재정의합니다.

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

소스 파일 내에서 이 지시문은 코드 영역에 프로젝트의 nullable 설정을 적용하거나 적용하지 않도록 지정합니다.

#nullable disable
public static class LegacyHelper
{
    // This file is nullable-oblivious. Reference types use the legacy rules.
    public static string GetGreeting(string name) =>
        name == null ? "hello" : $"hello {name}";
}
#nullable restore

#nullable enable
public static class MigratedHelper
{
    // This file is fully migrated. Reference types are non-nullable by default.
    public static string GetGreeting(string? name) =>
        name is null ? "hello" : $"hello {name}";
}
#nullable restore

파일별로 마이그레이션

대규모 프로젝트를 마이그레이션하는 가장 예측 가능한 방법은 파일별로 경고 또는 주석 파일을 사용하도록 설정하는 것입니다. 패턴은 선택한 기본값에 관계없이 동일합니다.

  1. 파일을 선택합니다. 종속성 그래프에서 가장 깊은 리프 형식으로 시작한 다음 바깥쪽으로 이동합니다. 형식에 주석을 추가하면 호출자에 새 경고가 발생하므로 상향식으로 작업하면 재작업이 최소화됩니다.
  2. #nullable 파일을 새 동작에 옵트인하는 지시문을 추가합니다. 두 플래그를 모두 원하는 경우 사용합니다 #nullable enable . #nullable enable warnings는 경고 전용으로 사용합니다.
  3. Resolve nullable warnings에 설명된 기술을 사용하여 파일의 경고를 해결하세요.
  4. 다음 파일에 대해 반복합니다.
  5. 프로젝트의 모든 파일에 지시문이 있는 경우 지시문을 제거하고 프로젝트 수준에서 설정합니다 <Nullable>enable</Nullable> .

코드베이스에 이미 있는 <Nullable>enable</Nullable>경우 반대 방향으로 진행하는 것입니다. 준비가 될 때까지 이동되지 않은 파일에서 경고를 표시하지 않습니다. #nullable disable를 사용해 파일을 제외한 다음, 억제 항목을 하나씩 제거합니다.

두 단계로 마이그레이션

2단계 마이그레이션은 nullable 참조 형식에 포함된 두 가지 종류의 작업을 구분합니다. 어떤 형태의 안정성이 더 중요한지에 따라 어느 쪽이든 단계를 시퀀싱할 수 있습니다.

먼저 경고, 주석

잠재 버그 System.NullReferenceException 수정이 우선순위일 때는 경고를 먼저 표시합니다.

  1. 1단계: 경고 해결 프로젝트 기본값을 .로 warnings설정합니다. 참조 형식은 nullable-oblivious로 유지되므로 형식 시스템은 아직 변경되지 않습니다. 컴파일러는 기존 코드에서 이미 System.NullReferenceException가 발생할 수 있는 모든 위치에 경고를 표시합니다. 프로젝트에 경고가 남지 않을 때까지 null 검사를 추가하고, 제어 흐름을 재구성하거나, 특성(attribute)을 적용합니다. 각 수정 사항으로 인해 주석이 존재하기 전에도 프로덕션 코드의 복원력이 향상됩니다.
  2. 2단계: 주석 추가 프로젝트 기본값을 .로 전환합니다 enable. 이제 참조 형식은 기본적으로 null 비허용이며, var 지역 변수는 null 허용이 됩니다. 새 경고는 변수 사용 방법과 일치하지 않는 선언을 반영합니다. ?를 허용해야 하는 형식에 null를 추가합니다. null이 아닌 입력이 필요한 API를 강화합니다.

먼저 주석을 추가한 다음 경고를 표시합니다.

공개 API 표면을 안정화하는 것이 우선이라면 주석을 우선 적용하세요. 이 시퀀스는 라이브러리에 적합합니다. 주석이 추가된 서명을 제공하므로 소비자가 올바른 계약을 확인하고 자체 일정에 따라 내부 경고를 닫을 수 있습니다.

  1. 1단계: 주석 추가 프로젝트 기본값을 .로 annotations설정합니다. 참조 형식은 기본적으로 null을 허용하지 않지만 컴파일러는 경고를 내보내지 않으므로 노이즈가 방해가 되지 않습니다. 공용 API 전체를 검토하여, 정상적으로 ?를 반환하거나 받을 수 있는 모든 멤버에 null를 추가합니다. 조여서는 안 되는 서명을 조입니다. 경고가 꺼져 있으므로 구현을 동시에 풀지 않고 포커스가 있는 커밋에서 API 셰이프를 해결할 수 있습니다.
  2. 2단계: 경고 해결 프로젝트 기본값을 .로 전환합니다 enable. 1단계에서 추가한 주석은 이제 null 상태 분석에 반영되므로, 컴파일러가 생성하는 경고의 품질이 처음부터 더 높아집니다. 즉, 각 경고는 동작이 이미 공개한 계약 조건과 일치하지 않는 코드를 정확히 가리킵니다. nullable 경고 해결의 기법을 사용하여 이를 해결하세요.

정렬 순서 선택

각 순서는 단계를 더 작고 더 검토 가능한 차이로 구분합니다. 한 단계는 동작만 변경하고 다른 단계는 형식만 변경합니다. 단점은 각 파일을 두 번 방문한다는 것입니다. 모든 변경이 위험을 수반하는 성숙하고 안정적인 코드의 경우 두 패스는 일반적으로 가치가 있습니다. 실행 중인 코드를 강화하려는 경우 먼저 경고를 선택합니다. 안정적인 계약을 게시하려는 경우 먼저 주석 을 선택합니다.

생성된 코드는 제외됩니다.

컴파일러는 프로젝트 설정에 관계없이 nullable 컨텍스트가 비활성화된 것처럼 생성된 것으로 표시된 파일을 처리합니다. 다음 조건 중 어느 것이라도 충족되면 파일이 생성된 것으로 간주됩니다.

  • .editorconfig 규칙은 파일에 대해 generated_code = true을 설정합니다.
  • 파일의 첫 번째 주석에는 <auto-generated> 또는 <auto-generated/>이 포함됩니다.
  • 파일 이름은 .로 TemporaryGeneratedFile_시작합니다.
  • 파일 이름은 , .designer.cs.generated.cs또는 .g.cs.로 .g.i.cs끝납니다.

nullable 지원 출력을 생성하는 생성기는 생성된 파일의 맨 위에 #nullable enable를 출력하여 다시 활성화할 수 있습니다.

완료되면

모든 파일이 프로젝트 기본값 <Nullable>enable</Nullable> 에 참여하고 요소가 설정된 후:

  • 원본의 모든 #nullable 지시문을 제거합니다.
  • 마이그레이션 중 경고를 없애기 위해서만 추가한 null! 및 default! 이니셜라이저를 제거합니다. 이를 적절한 초기화로 바꾸거나 형식을 nullable 참조 형식으로 만듭니다.
  • 공개 API를 표본 검사합니다. null를 반환하거나 허용하는 모든 멤버에는 ? 주석을 달아야 합니다. 패키지가 발송되면 어노테이션은 계약의 일부가 됩니다.

이제 새 프로젝트와 동일한 상태에 있습니다. nullable 참조 형식은 형식 시스템의 일부이며, 새 경고는 선언과 코드 간의 실제 불일치를 반영합니다. 문제가 발생할 때마다 해당 경고를 해결하려면 null 허용 경고 해결을 사용하세요.