본문 바로가기
프로그래밍/JAVA

방어적 프로그래밍의 중요성: TaskScheduler의 null 체크(MID2-9-문제와 풀이4-문제2)

by Edgemine 2025. 3. 29.

SchedulerTest.java

public class SchedulerTest {
    public static void main(String[] args) {
        TaskScheduler scheduler = new TaskScheduler();
        scheduler.addTask(new CompressionTask());
        scheduler.addTask(new BackupTask());
        scheduler.addTask(new CleanTask());

        System.out.println("작업 시작");
        run(scheduler);
        System.out.println("작업 완료");
    }

    private static void run(TaskScheduler scheduler) {
        while (scheduler.getRemainingTasks() > 0) {
            scheduler.processNextTask();
        }
    }
}

TaskScheduler.java

import java.util.ArrayDeque;
import java.util.Queue;

public class TaskScheduler {
    private Queue<Task> tasks = new ArrayDeque<>();

    public void addTask(Task task) {
        tasks.offer(task);
    }

    public int getRemainingTasks() {
        return tasks.size();
    }

    public void processNextTask() {
//        tasks.poll().execute();
        Task task = tasks.poll();
        if (task != null) {
            task.execute();
        }
    }
}

processNextTask() 메서드를 작성하고 생긴 질문. 나는 큐에서 작업을 꺼내고 바로 execute를 시켰는데 정답에서는 null 체크를 후에 실행하도록 설계해놓았다.

처음에는 "굳이 null 체크를 필요가 없지 않을까?"라는 생각이 들었다. 왜냐하면 SchedulerTest run() 메서드에서는 이미 큐가 비어있지 않음을 확인한 후에만 processNextTask()이 호출되도록 만들어져있기 때문이었다.

By Claude 3.7 Sonnet

왜 null 체크가 필요할까?

  1. 클래스의 재사용성: TaskScheduler 클래스는 다른 곳에서도 사용될 수 있습니다. 모든 사용자가 반드시 getRemainingTasks()를 먼저 호출한다고 보장할 수는 없습니다. 만약 조건 확인 없이 바로 processNextTask()를 호출한다면, 큐가 비어있는 상태에서 NullPointerException이 발생할 수 있습니다.
  2. 미래의 코드 변경에 대한 안전성: 현재는 run() 메서드에서 조건을 확인하고 있지만, 향후 코드가 수정되거나 새로운 기능이 추가되면서 이 조건 검사가 제거될 가능성이 있습니다. 메서드 자체가 안전하게 구현되어 있다면 이런 변경에도 오류 없이 동작할 수 있습니다.
  3. 동시성 환경 고려: 멀티스레드 환경에서는 getRemainingTasks()와 processNextTask() 호출 사이에 다른 스레드가 큐를 조작할 가능성이 있습니다. 이런 경우 경쟁 조건(race condition)을 방지하기 위해 null 체크가 필요합니다.
  4. 좋은 API 설계 원칙: 견고한 API는 호출자가 특정 전제조건을 만족시켜야 한다는 가정을 최소화하는 것이 좋습니다. processNextTask() 메서드는 큐가 비어있더라도 예외를 발생시키지 않고 조용히 아무 일도 하지 않는 방식으로 설계되어, 더 유연하고 안정적인 API를 제공합니다.

방어적 프로그래밍의 중요성

위 사례에서 알 수 있듯이, 방어적 프로그래밍은 코드의 안정성과 재사용성을 높이는 데 매우 중요한 역할을 합니다. 특히 다음과 같은 상황에서 방어적 프로그래밍이 빛을 발합니다:

  • 예외 상황 처리: 예상치 못한 입력이나 상태에서도 오류 없이 동작하는 코드를 작성할 수 있습니다.
  • 코드 변경에 대한 유연성: 미래의 변경 사항에도 안정적으로 동작하는 코드를 만들 수 있습니다.
  • 다른 개발자와 협업: 다른 개발자가 클래스를 사용할 때 발생할 수 있는 실수를 방지합니다.
  • 멀티스레드 환경: 동시성 문제를 예방하고 안전한 코드를 작성할 수 있습니다.

'프로그래밍 > JAVA' 카테고리의 다른 글

[예제]가장 큰 수 찾기  (0) 2017.04.29
[예제]점수 합격 여부 구분  (0) 2017.04.29
[예제]3의배수와 5의배수  (0) 2017.04.29
[예제]5의 배수 구별하기  (0) 2017.04.29
[예제]홀수짝수 구별하기  (0) 2017.04.29