관리 메뉴

개발그래머

Java WatchService를 이용하여 파일 감지하기 본문

Java

Java WatchService를 이용하여 파일 감지하기

임요환 2024. 4. 28. 21:25

개요

  • 운영 업무를 하다 보면 추가해야 될 것들, 변경해야 될 것들, 등록해줘야 할 것들이 생기는데 주로 csv파일(개발자) 혹은 엑셀 파일(업무담당자)로 전달받게 된다.
  • 유저 자동 가입 처리라던지, csv 파일에 해당하는 값으로 변경한다던지 소소한 잡일들이 많이 생기는데 생각외로 많은 시간을 할애하게 되고 업무의 흐름을 방해하여 자동화하기로 하였다.

 

WatchService 란?

  • Java NIO(Non-blocking I/O) 패키지의 일부로서 파일 시스템의 변경 사항을 감시하고 관찰하는 데 사용되며 파일 또는 디렉토리의 생성, 수정, 삭제 등의 이벤트를 감지할 때 활용됨
  • 파일 시스템의 특정 디렉토리에 대한 변경 사항을 모니터링하기 위해 사용되며 감시 대상 디렉토리에 대한 이벤트가 발생하면 이를 감지하고 애플리케이션에 알리며 이를 통해 파일 시스템의 변경 사항에 반응하여 적절한 조치를 취할 수 있음

사용법

@Component
class DirectoryWatchService {
    private val watchService: WatchService
    private val logger: Logger = LoggerFactory.getLogger(DirectoryWatchService::class.java)

    init {
        watchService = try {
            FileSystems.getDefault().newWatchService()
        } catch (e: IOException) {
            logger.error("IOException : ", e)
            throw e
        }
    }

    fun watch() {
        val fullFilePath = "/Users/im-yohwan/Downloads"
        val dir = Paths.get(fullFilePath);

        try {
            dir.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE)

            while (true) {
               val key = watchService.take()

                for (pollEvent in key.pollEvents()) {
                    val kind = pollEvent.kind()
                    val ev: WatchEvent<Path> = pollEvent as WatchEvent<Path>
                    val filename = ev.context()

                    when (kind) {
                        OVERFLOW -> {
                            logger.info("OVERFLOW")
                        }
                        ENTRY_CREATE -> {
                            logger.info("Created: $filename");
                        }
                        ENTRY_MODIFY -> {
                            logger.info("Modified: $filename");
                        }
                        ENTRY_DELETE -> {
                            logger.info("Deleted: $filename");
                        }
                    }
                }

                val valid = key.reset()
                if (!valid) {
                    break
                }

            }
        } catch (e: IOException) {
            logger.info("IOException : ", e)
        }
    }
}

 

fullFilePath에 감시를 원하는 경로를 지정하면 되고 pollEvents에서 어떠한 변경사항들이 발생하였는지 알 수 있다.

  • OVERFLOW = 이벤트가 손실되거나 폐기되었을 수 있음을 나타내는 특별 이벤트
  • ENTRY_CREATED = 디렉터리에 항목이 생성됨
  • ENTRY_MODIFIED = 디렉터리에 항목이 수정됨
  • ENTRY_DELETE = 디렉터리에 항목이 삭제됨

위 이벤트들을 바탕으로 원하는 액션을 줄 수 있으며 주로 ENTRY_CREATED를 사용하게 된다.

@Component
class DirectoryWatchScheduler(
    private val directoryWatchService: DirectoryWatchService
) {
    private val logger: Logger = LoggerFactory.getLogger(DirectoryWatchScheduler::class.java)

    @Scheduled(fixedDelay = 10000)
    fun watchDirectory() {
        logger.info("start watchDirectory")
        try {
            directoryWatchService.watch()
        } catch (e: Exception) {
            logger.info("Exception : ", e)
        }
    }
}

또한, Spring Scheduler와 같이 사용하여 해당 watchService가 다운되더라도 재실행할 수 있다.

주의사항

  • 리눅스 서버에서 vim으로 사용하여 파일 생성시 스왑파일도 감지가 되므로 꼭 특정 파일 이름을 정해두고 사용하는 것이 좋다.
  • 한글 파일 사용시 번거로울 수 있으므로 영어 이름 파일을 사용하는 것을 추천한다.
  • 팀원들과 어떤식으로 사용할지 상의하고 문서화하자.