0Pricing
Java Academy · 강의

WatchService: 파일 변경 모니터링

WatchService를 등록해 실시간으로 파일 생성, 수정, 삭제 이벤트를 감지합니다.

WatchService: 파일 변경 모니터링은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

WatchService란 무엇인가요

WatchService는 디렉터리에서 생성, 수정, 삭제와 같은 파일 시스템 이벤트를 감시합니다. OS 수준의 알림(Linux의 inotify, macOS의 FSEvents)을 사용하므로 폴링보다 훨씬 효율적입니다.

import java.nio.file.*;

WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("/home/user/watched");

dir.register(watcher,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_MODIFY,
    StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("Watching: " + dir);

이벤트 폴링

이벤트가 발생할 때까지 블로킹하려면 watcher.take()을 호출한 다음 이벤트를 처리합니다.

while (true) {
    WatchKey key;
    try {
        key = watcher.take(); // blocks until events arrive
    } catch (InterruptedException e) {
        break;
    }
    
    for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();
        Path changed = ((WatchEvent<Path>) event).context();
        System.out.println(kind.name() + ": " + changed);
    }
    
    key.reset(); // must reset to receive further events
}

비차단 폴링

비차단 방식으로 확인하려면 watcher.poll()을 사용하고, 지정한 시간 동안 기다리려면 poll(timeout, unit)을 사용합니다.

// Non-blocking — returns null if no events
WatchKey key = watcher.poll();
if (key != null) {
    // process events
    key.reset();
}

// Wait up to 5 seconds:
WatchKey key2 = watcher.poll(5, java.util.concurrent.TimeUnit.SECONDS);
if (key2 != null) {
    // process events
    key2.reset();
}

OVERFLOW 이벤트

애플리케이션이 처리하는 속도보다 빠르게 이벤트가 누적되면 일부 이벤트가 유실되었다는 뜻으로 OVERFLOW 이벤트가 발생합니다. 이를 적절히 처리합니다.

for (WatchEvent<?> event : key.pollEvents()) {
    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
        System.out.println("OVERFLOW: some events missed, rescanning directory");
        // rescan dir to find current state
        continue;
    }
    // process normal events
}

완전히 작동하는 예제

백그라운드 스레드에서 실행되는 완전한 파일 감시기입니다.

class FileWatcher implements Runnable {
    private final WatchService watcher;
    
    FileWatcher(Path dir) throws IOException {
        watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE);
    }
    
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                WatchKey key = watcher.take();
                for (WatchEvent<?> e : key.pollEvents())
                    System.out.println(e.kind() + ": " + e.context());
                key.reset();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

여러 디렉터리 감시

같은 WatchService에 여러 디렉터리를 등록할 수 있습니다. 반환된 WatchKey를 사용하여 이벤트가 어느 디렉터리에서 발생했는지 식별합니다.

Map<WatchKey, Path> keyMap = new HashMap<>();

for (Path dir : List.of(Path.of("/logs"), Path.of("/config"))) {
    WatchKey key = dir.register(watcher,
        StandardWatchEventKinds.ENTRY_MODIFY);
    keyMap.put(key, dir);
}

WatchKey key = watcher.take();
Path dir = keyMap.get(key);
System.out.println("Changed in: " + dir);

전체 경로 확인

이벤트의 context()는 전체 경로가 아니라 파일 이름만 반환합니다. 감시 중인 디렉터리를 기준으로 경로를 확인합니다.

WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path fullPath = watchedDir.resolve(filename);
System.out.println("Full path: " + fullPath);

디렉터리 재귀 감시

WatchService는 등록된 디렉터리만 감시하고 하위 디렉터리는 감시하지 않습니다. 재귀적으로 감시하려면 디렉터리 트리를 순회하면서 각 하위 디렉터리를 등록합니다.

WatchService watcher = FileSystems.getDefault().newWatchService();
Path root = Path.of("/project");

try (var stream = Files.walk(root)) {
    stream.filter(Files::isDirectory)
          .forEach(dir -> {
              try { dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
              } catch (IOException e) { throw new RuntimeException(e); }
          });
}

리소스 정리

사용을 마치면 OS 리소스를 해제할 수 있도록 항상 WatchService를 닫습니다.

try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
    Path dir = Path.of("/tmp/watch");
    dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
    
    WatchKey key = watcher.poll(10, java.util.concurrent.TimeUnit.SECONDS);
    if (key != null) {
        key.pollEvents().forEach(e -> System.out.println(e.context()));
        key.reset();
    }
} // watcher.close() called automatically

실용 사례: 설정 즉시 새로 고침

파일이 변경될 때 WatchService를 사용하여 설정을 다시 불러옵니다.

class ConfigWatcher {
    private volatile Map<String, String> config = new HashMap<>();
    
    void loadConfig(Path file) throws IOException {
        config = Files.readAllLines(file).stream()
            .filter(l -> l.contains("="))
            .collect(Collectors.toMap(
                l -> l.split("=")[0].trim(),
                l -> l.split("=")[1].trim(),
                (a,b) -> b
            ));
    }
    
    // Call loadConfig again on ENTRY_MODIFY event
}

WatchService의 제한 사항

중요한 제한 사항은 다음과 같습니다.

  • 개별 파일이 아니라 디렉터리만 감시합니다.
  • 기본적으로 재귀 감시를 지원하지 않으므로 각 하위 디렉터리를 직접 등록해야 합니다.
  • macOS에서는 내부적으로 폴링을 사용하므로 Linux의 inotify보다 반응성이 낮습니다.
  • 변경이 짧은 시간에 급격히 발생하면 일부를 놓칠 수 있습니다(OVERFLOW).
  • 감시 중인 디렉터리가 삭제되면 WatchKey가 유효하지 않게 됩니다.

확인 문제

WatchKey에서 이벤트 처리를 마친 후 해당 키에 대한 추가 이벤트를 WatchService가 전달하도록 하려면 무엇을 호출해야 합니까?

복습: WatchService

핵심 내용은 다음과 같습니다.

  • 디렉터리를 항목 생성/MODIFY/DELETE 이벤트와 함께 등록합니다.
  • take()은 블로킹되고, poll()은 비차단이며, poll(timeout, unit)은 지정된 시간 동안 기다립니다.
  • 이벤트 처리를 마친 후에는 항상 key.reset()을 호출합니다.
  • context()는 파일 이름만 반환하므로 전체 경로가 필요하면 감시 중인 디렉터리를 기준으로 경로를 확인합니다.
  • try-with-resources를 사용하여 WatchService를 닫습니다.
  • 재귀 감시를 하려면 각 하위 디렉터리를 등록해야 합니다.

자주 묻는 질문

“WatchService: 파일 변경 모니터링” 강의는 무료인가요?

네 — “WatchService: 파일 변경 모니터링” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“WatchService: 파일 변경 모니터링”에서 뭘 배우나요?

WatchService를 등록해 실시간으로 파일 생성, 수정, 삭제 이벤트를 감지합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“WatchService: 파일 변경 모니터링” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Path: 파일 위치 표현
  2. Files 유틸리티: 읽기, 쓰기, 복사, 이동
  3. Files.walk로 디렉터리 트리 순회
  4. WatchService: 파일 변경 모니터링
← Java Academy(으)로 돌아가기