Published on

nio 패키지의 Path를 사용합시다!

글쓴이

    Java에서 파일 경로를 처리하는 방법: File 객체의 사용과 한계

    자바 코드를 작성하다가 파일 경로를 다루는 경우는 상당히 흔하게 겪는 일이다. 보통은 이럴 때 File 객체를 사용해서 코드를 작성한다. 예를 들어, 파일의 메시지 ID를 추출하는 코드가 있다. 아래 코드는 맥 환경에서 잘 동작한다.

    private void loadData() throws IOException
    {
    	File dir = new File(CONFIG_PATH + "/data" );
    	assertTrue(dir.isDirectory());
    	
    	File[] list = dir.listFiles();
    	if( null == list )
    		return;
    	
    	for( File f: list )
    	{
    		String dataId = extractMessageID( f );
    		String data   = readFile( f );
    		dataRepo.put( dataId, data );
    	}
    	assertEquals( dataRepo.size(), list.length );
    }
    

    문제점

    위 예시 코드는 맥북에서 잘 작동하지만 윈도우에서 돌려보면 에러가 발생한다. 왜일까? 바로 Windows와 Unix 기반 운영체제에서 file.separator의 값이 다르기 때문이다. File.getPath() 메소드는 운영체제에 따라 반환값이 달라진다. 윈도우에서는 경로를 표현할 때 \를 사용하고, 맥과 리눅스에서는 /를 사용한다.

    즉, 운영체제마다 파일 경로를 표현하는 방식이 다르기 때문에 문제가 발생한다.

    방법 1 : 한줄로 해결하기

    그럼 이런 문제를 어떻게 해결해야 할까? 첫 번째 방법은 System.getProperty("file.separator")를 사용하는 것이다. 이는 현재 운영체제의 파일 경로 구분자를 반환하는 메서드로, 아래와 같이 수정하여 사용할 수 있다.

    private void loadData() throws IOException
    {
    	File dir = new File(CONFIG_PATH + System.getProperty("file.separator") +"data" );
    	assertTrue(dir.isDirectory());
    	
    	File[] list = dir.listFiles();
    	if( null == list )
    		return;
    	
    	for( File f: list )
    	{
    		String dataId = extractMessageID( f );
    		String data   = readFile( f );
    		dataRepo.put( dataId, data );
    	}
    	assertEquals( dataRepo.size(), list.length );
    }
    

    방법 2 : java.nio.file.Path 사용하기

    두 번째 방법은 java.nio.file.Path를 사용하는 것이다. nio 패키지의 Path를 사용하면 운영체제에 따른 파일 경로 구분자 식별 문제를 해결할 수 있다.

    private void loadData() throws IOException
    {
    	Path dir = Paths.get(CONFIG_PATH,"data");
    	assertTrue(Files.isDirectory(dir));
    	DirectoryStream<Path> paths = Files.newDirectoryStream(dir);
    	int pathsLength = 0;
    	if( null == paths )
    		return;
    	for( Path path: paths )
    	{
    		logger.info( "FILE : [{}]", path.normalize() );
    		logger.info( "DATA : [{}]", readFile(path) );
    		logger.info( "ID   : [{}]", extractMessageID( path ) );
    		String dataId = extractMessageID( path );
    		String data   = readFile( path );
    		dataRepo.put( dataId, data );
    		pathsLength++;
    	}
    	assertEquals( dataRepo.size(), pathsLength );
    }
    

    Path 의 장점

    1. Path 객체는 File과 상호호환된다. 즉, File 객체를 Path 객체로, 그 반대도 가능하다.
    2. Path를 사용하면 운영체제마다 파일 경로 구분자를 식별할 필요가 없다. 이는 코드의 가독성을 높이고 버그를 줄일 수 있다.
    3. PathFile보다 풍부한 에러 처리와 메시지를 제공한다. 오류 상황을 더 세세하게 다룰 수 있다.
    4. 또한, Path 객체는 File 객체보다 더 많은 메서드를 제공한다.

    결론

    파일 경로를 다룰 때는 운영체제에 따른 차이를 고려하는 것이 중요하다. File 객체 대신 Path 객체를 사용하면 이러한 문제를 효과적으로 해결할 수 있다.

    참조