-
NullReferenceException, Null 참조 오류 등 Null 예외 처리에 대한 고찰과 지침서닷넷/C# 2024. 7. 12. 11:26반응형
NullReferenceException 에러가 발생 할 때마다 아, 다음에는 좀 더 꼼꼼하게 확인해야지 해도 시간이 지나면 같은 문제를 겪고는 한다.
그래서 매 테스트 때마다 하나 씩 확인하고, 잊지 않기 위해 이렇게 지침서를 작성한다.
그리고 문득 이런 고찰을 하게 된다.
왜 프로그램은 Null인 것을 참조하면 그냥 넘어가지 않고, 기어이 오류 메시지를 뱉어내는 걸까?
왜 Null 참조 오류를 무시 하지 못 하게 하는 걸까?
○ 그 이유
Null 객체에 접근하려고 하면 메모리 접근 오류가 발생하여 프로그램이 비정상적으로 종료되거나 프로그램의 성격에 따라서 데이터 손실, 보안 문제 등을 일으킬 수 있다. 이러한 문제는 심각한 버그를 야기한다. 이를 방지하고 예방하기 위해 개발자에게 문제를 인지시키는 방법이자, 오류 메시지를 출력하고 그 이후의 동작을 중단시킨다.
만약, 이 오류가 메시지 조차 뜨지 않고 무시가 된 채로 프로그램이 정상적으로 실행이 된다면 Null 참조가 일어나는 곳에서 데이터 누수가 일어난다거나 생각치 못 한 문제들이 연쇄적으로 발생하면서 프로그램을 사용하는 사용자들뿐만 아니라 해당 프로그램을 통해 이익을 얻는 사람들에게 불행이 닥쳐올 것이고, 이는 비용 손실 또한 일어날 수 밖에 없다.
데이터베이스를 배울 때 무결성의 중요함을 배운다. 이는 데이터를 다루는 프로그램과 그 프로그램을 만드는 개발자 또한 무관한 영역이 아니다.
Null 참조는 무결성을 해치는 심각한 버그이므로, 이 버그가 나타났을 때 프로그램에서 오류를 뱉어내어 개발자가 이를 절대 무시할 수 없게 만들어진 시스템은 개발자 입장에서는 정말 감사한 일이다.
이 버그를 단순히 try catch로 오류를 무시로 일관하는, 조치를 취하는 누를 범하지 말자.
문제를 인지하고 근본적인 것에 다가감으로써 적절한 조치를 취하도록 하자.
using System; class Program { static void Main() { MyClass obj = null; // Null 참조 오류를 무시하고 프로그램 계속 실행 try { obj.Method(); } catch { // 오류를 무시하고 넘어감 } Console.WriteLine("Program continues execution."); } } class MyClass { public void Method() { Console.WriteLine("Method called"); } }
이 경우, 프로그램은 Null 참조 오류를 무시하고 계속 실행되지만, 실제로는 예기치 않은 동작이 발생할 수 있다.
using System; class Program { static void Main() { MyClass obj = null; // Null 참조 오류를 처리하고 프로그램 중단 try { obj.Method(); } catch (NullReferenceException ex) { Console.WriteLine($"NullReferenceException caught: {ex.Message}"); // 필요한 경우 프로그램 중단 Environment.Exit(1); } Console.WriteLine("This line will not be executed if an exception occurs."); } } class MyClass { public void Method() { Console.WriteLine("Method called"); } }
이 경우, 프로그램은 Null 참조 오류를 감지하고 적절한 메시지를 출력하며 필요 시 프로그램을 중단하여 이후의 예기치 않은 동작을 방지한다.
● 요약
Null 참조 오류를 무시하지 않고 오류 메시지를 출력하는 것은 프로그램의 안정성과 안전성을 보장하기 위한 중요한 방법이다. 이를 통해 개발자는 문제를 신속하게 파악하고 해결할 수 있으며, 프로그램의 예측 가능한 동작을 유지할 수 있다.
○ Null 참조 오류 해결 방법
● Null 참조 오류를 해결하기 위한 선행 학습, 발생 조건 알기
1. 객체가 null일 때 인스턴스 멤버에 접근
MyClass obj = null; obj.Method(); // NullReferenceException 발생
2. 컬렉션이 null일 때 접근
List<int> list = null; int count = list.Count; // NullReferenceException 발생
3. 배열 요소가 null일 때 접근
MyClass[] array = new MyClass[10]; MyClass item = array[0]; item.Method(); // NullReferenceException 발생
4. 메서드 반환 값이 null일 때 접근
MyClass obj = GetMyClass(); obj.Method(); // GetMyClass가 null을 반환하면 NullReferenceException 발생
● 해결 방법
1. null 확인: 모든 객체 접근 전에 null인지 확인
if (obj != null) { obj.Method(); }
2. Null-conditional 연산자 "?.": 객체가 null인 경우 null을 반환하고, 그렇지 않으면 멤버에 접근
obj?.Method();
3. Null-coalescing 연산자 "??": 객체가 null일 경우 기본 값을 제공
MyClass obj = GetMyClass() ?? new MyClass();
4. Null-coalescing 할당 연산자 "??=": 객체가 null인 경우 기본 값을 할당
obj ??= new MyClass();
5. nullable 타입과 null 병합 연산자 사용: nullable 타입을 사용할 때 기본 값을 설정
int? number = null; int value = number ?? 0;
6. Optional Chaining 사용: "?." 연산자를 사용하여 객체가 null인지 확인한 후 null이 아니면 멤버에 접근하는 기능
public class Person { public string Name { get; set; } public Person? Parent { get; set; } } var child = new Person { Name = "Child" }; var parent = child.Parent?.Name ?? "Unknown";
7. try-catch 블록 사용: 예외가 발생할 수 있는 코드 블록을 감싸서 처리
try { obj.Method(); } catch (NullReferenceException ex) { // 예외 처리 코드 Console.WriteLine("NullReferenceException caught"); }
추가적으로 null 참조 문제를 방지하기 위해 확인하거나 테스트할 수 있는 몇 가지 사항과 방법이 더 있다.
○ 추가적인 확인 및 테스트 방법
1. Nullable Reference Types 사용: C# 8.0부터는 Nullable Reference Types를 사용하여 컴파일 타임에 null 참조 문제를 방지할 수 있다.
#nullable enable public class MyClass { public string? NullableProperty { get; set; } public string NonNullableProperty { get; set; } public MyClass(string nonNullableProperty) { NonNullableProperty = nonNullableProperty; } }
2. Data Annotations 및 Code Contracts 사용: Data Annotations와 Code Contracts를 사용하여 메서드와 속성의 유효성을 검사할 수 있다.
using System.ComponentModel.DataAnnotations; public class MyClass { [Required] public string NonNullableProperty { get; set; } }
3. Unit Tests 작성: Unit Test를 작성하여 null 참조 예외가 발생하지 않도록 테스트할 수 있다.
using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyClassTests { [TestMethod] public void TestMethod_NullCheck() { MyClass obj = null; Assert.ThrowsException<NullReferenceException>(() => obj.Method()); } }
4. Defensive Programming: 가능한 모든 경로에서 null이 발생하지 않도록 방어적인 코딩을 수행한다.
public void Process(MyClass obj) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } obj.Method(); }
5. Guard Clauses 사용: 메서드 시작 부분에서 인수의 유효성을 검증하는 Guard Clauses를 사용한다.
public void Process(MyClass obj) { _ = obj ?? throw new ArgumentNullException(nameof(obj)); obj.Method(); }
반응형'닷넷 > C#' 카테고리의 다른 글
Microsoft의 Text-to-Speech (TTS), Azure Cognitive Services를 C#에서 사용하는 방법 (0) 2023.07.27 C# Interlocked.Increment에 대한 고찰 (0) 2023.07.20 C# 기초지식, 정보 정리 (면접 질문 대비, 꼭 알아야 하는 것) (0) 2023.04.11 C# 시간 계산 두 번 째 (TimeSpan, DateTimeOffset) (0) 2023.04.06 C# JSON 파일 읽기/쓰기 (Deserialize/Serialize) (0) 2023.04.04