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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Path: 파일 위치 표현
- Files 유틸리티: 읽기, 쓰기, 복사, 이동
- Files.walk로 디렉터리 트리 순회
- WatchService: 파일 변경 모니터링