1. 클래스 File: 어떻게 시작되었나
비록 새로운 프로젝트에서 File 클래스를 사용하지 않는 편이 좋다는 것을 이미 확인했지만, 여전히 이 클래스가 쓰이는 레거시 시스템이 많습니다. 따라서 이를 이해하고 다룰 수 있어야 합니다. File 클래스는 디스크의 파일이나 디렉터리를 표현하는 옛 방식입니다. 하지만 혼동하지 마세요. File 객체는 파일 자체가 아니라 경로의 "포인터"/여권일 뿐입니다. 예를 들어, File f = new File("hello.txt"); 라는 표현은 디스크에 파일을 생성하지 않습니다 — 단지 경로를 기술한 것뿐입니다.
File 객체를 어떻게 만들까?
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file = new File("example.txt"); // 상대 경로
File folder = new File("mydir"); // 디렉터리
System.out.println("절대 경로: " + file.getAbsolutePath());
System.out.println("파일이 존재합니까? " + file.exists());
System.out.println("파일인가요? " + file.isFile());
System.out.println("디렉터리인가요? " + folder.isDirectory());
}
}
기억해야 할 점:
File 객체를 만든다고 해서 디스크에 파일이 생성되는 것은 아닙니다. 그저 "바로가기"일 뿐입니다. 실제 파일을 만들려면 명시적으로 생성해야 합니다(예: 데이터를 기록하거나 file.createNewFile() 호출).
File 클래스의 주요 메서드
- exists() — 파일/디렉터리가 존재하는지 확인.
- isFile() — 파일인가? (폴더가 아님)
- isDirectory() — 디렉터리인가?
- getAbsolutePath() — 절대 경로 가져오기.
- length() — 파일 크기(바이트).
- getName() — 파일 또는 폴더 이름.
- delete() — 파일/디렉터리 삭제(비어 있어야 함).
예제: 파일 정보 출력
File file = new File("example.txt");
System.out.println("이름: " + file.getName());
System.out.println("경로: " + file.getAbsolutePath());
System.out.println("크기: " + file.length() + " 바이트");
System.out.println("존재하나요? " + file.exists());
2. 클래스 Path: 파일에 대한 현대적 관점
Path는 새로운 파일 API(NIO.2, Java 7부터)의 일부입니다. 파일/디렉터리로의 경로를 추상화하며, 경로는 로컬 디스크뿐 아니라 ZIP 아카이브 내부나 원격 리소스(해당 프로바이더가 있을 때)일 수도 있습니다.
File이 종이 지도라면, Path는 GPS 내비게이터입니다.
Path 객체 생성 방법
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
public static void main(String[] args) {
Path path1 = Paths.get("example.txt"); // 상대 경로
Path path2 = Paths.get("folder", "file.txt"); // 경로 조각 결합
System.out.println("경로 1: " + path1.toAbsolutePath());
System.out.println("경로 2: " + path2.toAbsolutePath());
}
}
장점: 구분자에 신경 쓸 필요가 없습니다. Paths.get이 OS에 맞는 형식을 선택합니다.
File과 Path 간 변환
- File → Path: file.toPath()
- Path → File: path.toFile()
File file = new File("test.txt");
Path path = file.toPath();
Path anotherPath = Paths.get("data.txt");
File anotherFile = anotherPath.toFile();
경로 작업: resolve, relativize, normalize
경로 결합(resolve)
Path dir = Paths.get("myfolder");
Path file = dir.resolve("notes.txt"); // myfolder/notes.txt
System.out.println(file); // myfolder/notes.txt
상대 경로(relativize)
Path pathA = Paths.get("folderA/file1.txt");
Path pathB = Paths.get("folderA/subfolder/file2.txt");
Path relative = pathA.relativize(pathB);
System.out.println(relative); // ../subfolder/file2.txt
경로 정규화(normalize)
Path messy = Paths.get("folder/../folder/file.txt");
Path clean = messy.normalize();
System.out.println(clean); // folder/file.txt
예제: 파일 경로 구축
Path home = Paths.get(System.getProperty("user.home"));
Path docs = home.resolve("Documents");
Path myFile = docs.resolve("myfile.txt");
System.out.println("파일 경로: " + myFile.toAbsolutePath());
3. 클래스 Files: 만능 도구
Files 클래스는 java.nio.file 패키지에 있으며, Path 기반으로 파일과 디렉터리를 다루는 정적 메서드 유틸리티 모음입니다. 존재 여부 확인, 생성/삭제, 읽기/쓰기, 복사/이동, 속성 조회, 디렉터리 트리 순회 등 거의 모든 것을 처리합니다.
Files 클래스의 주요 메서드
- Files.exists(path) — 파일/디렉터리가 존재하는지 확인.
- Files.isDirectory(path) — 디렉터리인지 확인.
- Files.size(path) — 파일 크기.
- Files.createFile(path) — 파일 생성.
- Files.createDirectory(path) — 디렉터리 생성.
- Files.delete(path) — 파일/디렉터리 삭제.
- Files.copy(src, dest) — 파일 복사.
- Files.move(src, dest) — 이동/이름 변경.
- Files.getLastModifiedTime(path) — 마지막 수정 시각.
- Files.readAllBytes(path) — 파일 전체를 바이트 배열로 읽기.
- Files.readAllLines(path) — 텍스트 파일의 모든 줄 읽기.
- Files.write(path, bytes) — 파일에 바이트 쓰기.
예제: 파일 검사
import java.nio.file.*;
public class FilesDemo {
public static void main(String[] args) {
Path path = Paths.get("example.txt");
System.out.println("파일이 존재합니까? " + Files.exists(path));
System.out.println("디렉터리인가요? " + Files.isDirectory(path));
if (Files.exists(path)) {
try {
System.out.println("파일 크기: " + Files.size(path) + " 바이트");
} catch (Exception e) {
System.out.println("크기 가져오는 중 오류: " + e.getMessage());
}
}
}
}
4. 실습: 객체 생성, 속성 출력
파일과 디렉터리용 객체 생성
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CompareDemo {
public static void main(String[] args) {
// 기존 방식
File oldFile = new File("test.txt");
File oldDir = new File("mydir");
// 새로운 방식
Path newFile = Paths.get("test.txt");
Path newDir = Paths.get("mydir");
System.out.println("=== File API ===");
System.out.println("파일: " + oldFile.getAbsolutePath());
System.out.println("존재하나요? " + oldFile.exists());
System.out.println("파일인가요? " + oldFile.isFile());
System.out.println("디렉터리인가요? " + oldDir.isDirectory());
System.out.println("\n=== Path API ===");
System.out.println("파일: " + newFile.toAbsolutePath());
System.out.println("존재하나요? " + java.nio.file.Files.exists(newFile));
System.out.println("파일인가요? " + java.nio.file.Files.isRegularFile(newFile));
System.out.println("디렉터리인가요? " + java.nio.file.Files.isDirectory(newDir));
}
}
파일 속성 출력
import java.nio.file.*;
public class FileProperties {
public static void main(String[] args) {
Path path = Paths.get("test.txt");
System.out.println("절대 경로: " + path.toAbsolutePath());
System.out.println("파일 이름: " + path.getFileName());
System.out.println("부모 폴더: " + path.getParent());
if (Files.exists(path)) {
try {
System.out.println("크기: " + Files.size(path) + " 바이트");
System.out.println("마지막 수정: " + Files.getLastModifiedTime(path));
} catch (Exception e) {
System.out.println("정보 가져오는 중 오류: " + e.getMessage());
}
} else {
System.out.println("파일을 찾을 수 없습니다.");
}
}
}
5. 비교: File vs Path/Files
| 기능 | File (java.io) | Path + Files (java.nio.file) |
|---|---|---|
| 경로 표현 | 로컬 디스크만 | 로컬 디스크, ZIP, 네트워크 리소스 |
| 경로 결합 | 슬래시로 수동 연결 | resolve로 쉽게 |
| 존재 여부 확인 | exists() | Files.exists(path) |
| 내용 읽기/쓰기 | 없음 | 예 (Files.readAll..., Files.write) |
| 속성 가져오기 | 기본(length(), lastModified()) | 확장 속성과 뷰 |
| 호환성 | 모든 Java 버전과 호환 | Java 7 이상 |
| 심링크 처리 | 제한적 | 완전 지원 |
요약: 언제 무엇을 사용할까?
- 새 프로젝트에는 Path와 Files를 사용하세요.
- 레거시 코드 유지보수에는 가끔 File로 작업해야 할 때가 있습니다.
6. 유용한 팁
상대 경로와 절대 경로
상대 경로는 현재 작업 디렉터리를 기준으로 한 경로이고, 절대 경로는 파일 시스템의 루트부터 시작하는 전체 경로입니다.
Path relative = Paths.get("data", "file.txt");
Path absolute = relative.toAbsolutePath();
System.out.println("절대 경로: " + absolute);
크로스 플랫폼
Path와 Files는 OS의 특성(구분자, 인코딩 등)을 고려합니다. "C:\\folder\\file.txt" 같은 경로를 직접 문자열로 작성하지 말고 Paths.get과 resolve를 사용하세요.
File과 Path 간 변환
File 기반의 기존 코드가 있다면 새 API로 쉽게 전환할 수 있습니다:
File legacy = new File("legacy.txt");
Path modern = legacy.toPath();
if (Files.exists(modern)) {
// 이제 NIO.2의 모든 기능을 사용할 수 있습니다
}
파일 존재 여부 확인
Path path = Paths.get("notes.txt");
if (Files.exists(path)) {
System.out.println("파일을 찾았습니다!");
} else {
System.out.println("파일이 존재하지 않습니다.");
}
7. 미니 실습: 파일과 디렉터리 생성 및 확인
import java.nio.file.*;
public class MiniPractice {
public static void main(String[] args) throws Exception {
Path dir = Paths.get("demo_dir");
Path file = dir.resolve("hello.txt");
// 디렉터리가 없으면 생성
if (!Files.exists(dir)) {
Files.createDirectory(dir);
System.out.println("디렉터리 생성됨: " + dir.toAbsolutePath());
}
// 파일이 없으면 생성
if (!Files.exists(file)) {
Files.createFile(file);
System.out.println("파일 생성됨: " + file.toAbsolutePath());
}
// 속성 출력
System.out.println("파일이 존재합니까? " + Files.exists(file));
System.out.println("파일인가요? " + Files.isRegularFile(file));
System.out.println("크기: " + Files.size(file) + " 바이트");
// 파일과 디렉터리 삭제(선택)
Files.delete(file);
Files.delete(dir);
System.out.println("파일과 디렉터리가 삭제되었습니다.");
}
}
8. File/Path/Files 작업 시 자주 발생하는 실수
실수 1: File과 Path를 혼동함. 많은 초보자가 Files 메서드를 File 객체에 쓰려고 합니다. 기억하세요: Files.*는 Path와 함께 동작합니다. File이 있다면 먼저 file.toPath()를 호출하세요.
실수 2: File 또는 Path 객체를 만들면 파일이 생성된다고 생각함. 경로 객체는 바로가기에 불과하며 실제 파일이 아닙니다. 파일 생성에는 Files.createFile(path) 또는 데이터 기록을 사용하세요.
실수 3: 문자열로 경로를 수동 조합. "folder/file.txt" 또는 "folder\\file.txt"처럼 작성하지 마세요. Paths.get(...)과 resolve(...)를 사용하세요. 더 안전하고 크로스 플랫폼에 적합합니다.
실수 4: 예외를 무시함. 파일 작업은 IOException 등 예외를 던질 수 있습니다. try-catch로 처리하거나 상위로 던지기(throws)를 사용하세요.
실수 5: File.exists()로 확인하고는 Path로 작업함. 한 가지 스타일을 유지하세요. Path로 시작했다면 Files.exists(path) 및 다른 Files 메서드를 사용하세요.
GO TO FULL VERSION