← Java 강의 목록으로
📂
예외 · 입출력
예외 · 입출력 · 선수: 이전 단원

16. 파일 IO

파일을 읽고 쓰는 것은 거의 모든 프로그램이 필요로 하는 기능입니다. Java 의 신형 IO API 인 `java.nio.file` 의 `Files`, `Path` 를 중심으로, 자원 누수를 막는 **try-with-resources** 패턴까지 배웁니다.

Java예외I/O파일 IO
소요 시간
약 1~1.5시간
난이도
📊 중급
선수 조건
🎯 이전 단원 또는 동등 지식
결과물
파일을 읽고 쓰는 것은 거의 모든 프로그램이 필요로 하는 기능입니다. Java 의 신형 IO API 인 `java.nio.file` 의 `Files`, `Path` 를 중심으로, 자원 누수를 막는 **try-with-resources** 패턴까지 배웁니다.

이 강의에서 배우는 것

  • 1`Path` 와 `Files` 의 역할을 안다
  • 2`Files.readAllLines` / `Files.writeString` 으로 텍스트 파일을 읽고 쓴다
  • 3`BufferedReader` 와 `try-with-resources` 의 사용법을 안다
  • 4`IOException` 이 checked 예외임을 이해한다

소개

파일을 읽고 쓰는 것은 거의 모든 프로그램이 필요로 하는 기능입니다. Java 의 신형 IO API 인 `java.nio.file` 의 `Files`, `Path` 를 중심으로, 자원 누수를 막는 **try-with-resources** 패턴까지 배웁니다.

핵심 개념

1) `Path` 와 `Files`

java
import java.nio.file.Path;
import java.nio.file.Files;

Path p = Path.of("data.txt");
Files.writeString(p, "Hello\nWorld\n");
String text = Files.readString(p);

`Path` 는 경로를 표현하는 불변 객체, `Files` 는 정적 유틸 모음입니다.

2) 줄 단위 입출력

java
List<String> lines = Files.readAllLines(Path.of("data.txt"));
Files.write(Path.of("out.txt"), lines);   // 줄들 그대로 쓰기

3) try-with-resources

java
try (BufferedReader br = Files.newBufferedReader(p)) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}
// br.close() 가 자동 호출

`AutoCloseable` 을 구현한 자원이면 무엇이든 사용 가능합니다.

4) checked 예외 `IOException`

파일 IO 메서드는 대부분 `IOException` 을 던지는 **checked** 예외입니다. `throws IOException` 선언이나 `try/catch` 가 필요합니다.

핵심 예제

예제 1 — `WriteFile.java` : 파일 쓰기

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class WriteFile {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");
        System.out.println(p.toAbsolutePath() + " 쓰기 완료");
    }
}

**실행 결과**

text
/.../greeting.txt 쓰기 완료

**메모:** 파일이 없으면 자동 생성, 있으면 덮어씁니다.

예제 2 — `ReadFile.java` : 파일 읽기

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class ReadFile {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");

        List<String> lines = Files.readAllLines(p);
        for (int i = 0; i < lines.size(); i++) {
            System.out.println((i + 1) + ": " + lines.get(i));
        }
    }
}

**실행 결과**

text
1: Hello
2: 안녕

**메모:** 줄 단위로 읽으면 번호 매기기 같은 처리가 쉽습니다.

예제 3 — `BufferedReadDemo.java` : try-with-resources

java
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class BufferedReadDemo {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");

        try (BufferedReader br = Files.newBufferedReader(p)) {
            String line;
            int n = 0;
            while ((line = br.readLine()) != null) {
                n++;
                System.out.printf("L%d: %s%n", n, line);
            }
        }
    }
}

**실행 결과**

text
L1: Hello
L2: 안녕

**메모:** try 블록 종료 시 `br.close()` 가 자동 호출됩니다.

예제 4 — `AppendAndDelete.java` : 추가 + 삭제

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class AppendAndDelete {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("notes.txt");
        Files.writeString(p, "첫 줄\n");
        Files.writeString(p, "두 번째 줄\n", StandardOpenOption.APPEND);
        System.out.println(Files.readString(p));
        Files.deleteIfExists(p);
        System.out.println("삭제 완료");
    }
}

**실행 결과**

text
첫 줄
두 번째 줄

삭제 완료

**메모:** `StandardOpenOption.APPEND` 로 기존 내용 뒤에 덧붙일 수 있습니다.

전체 예제 코드 (src/)

src/AppendAndDelete.java

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class AppendAndDelete {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("notes.txt");
        Files.writeString(p, "첫 줄\n");
        Files.writeString(p, "두 번째 줄\n", StandardOpenOption.APPEND);
        System.out.println(Files.readString(p));
        Files.deleteIfExists(p);
        System.out.println("삭제 완료");
    }
}

src/BufferedReadDemo.java

java
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class BufferedReadDemo {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");

        try (BufferedReader br = Files.newBufferedReader(p)) {
            String line;
            int n = 0;
            while ((line = br.readLine()) != null) {
                n++;
                System.out.printf("L%d: %s%n", n, line);
            }
        }
    }
}

src/ReadFile.java

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class ReadFile {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");

        List<String> lines = Files.readAllLines(p);
        for (int i = 0; i < lines.size(); i++) {
            System.out.println((i + 1) + ": " + lines.get(i));
        }
    }
}

src/WriteFile.java

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class WriteFile {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("greeting.txt");
        Files.writeString(p, "Hello\n안녕\n");
        System.out.println(p.toAbsolutePath() + " 쓰기 완료");
    }
}

자주 하는 실수

  1. 파일 자원을 close 하지 않아 OS 핸들 누수 → try-with-resources 사용
  2. 경로 구분자를 OS 별로 하드코딩 → `Path.of` / `Path.resolve` 사용
  3. 인코딩을 지정하지 않아 한글이 깨짐 → `Files.readString(path, StandardCharsets.UTF_8)` 권장
  4. `IOException` 을 매번 잡지 않고 전파 → 입문 단계에선 main 의 `throws IOException` 도 충분
  5. 같은 파일을 동시에 여러 스레드가 쓰는 상황을 고려하지 않음 (입문 범위 밖)

정리

  • `Path` + `Files` 가 모던 Java IO 의 중심
  • `try-with-resources` 는 자원 해제의 표준 패턴
  • 대용량 파일은 줄 단위 처리 + 버퍼링이 안전
  • 인코딩은 UTF-8 명시 권장

과제

# 과제 - 16. 파일 IO

## 문제 1 — TODO 파일 작성·읽기

  • 파일명: `Homework01.java`
  • 핵심 개념: `Files.write`, `Files.readAllLines`

요구사항

  • `todo.txt` 파일에 다음 3줄을 씁니다: `우유 사기`, `숙제 끝내기`, `운동 30분`
  • 다시 읽어 `1) ...` 형식으로 출력.
  • 끝나면 파일을 삭제합니다.

예상 출력

text
1) 우유 사기
2) 숙제 끝내기
3) 운동 30분
삭제 완료

## 문제 2 — 단어 수 세기

  • 파일명: `Homework02.java`
  • 핵심 개념: `Files.readString`, `split`

요구사항

  • `essay.txt` 에 `Java is fun. Java is powerful.` 를 씁니다.
  • 공백/구두점 기준으로 단어를 분리해 총 단어 수를 출력합니다.
  • 끝나면 파일 삭제.

예상 출력

text
단어 수=6

힌트

  • `text.split("[\\s.]+")`

## 정답 확인 직접 풀어 본 후 [`answer/`](./answer/) 폴더의 정답과 비교해 보세요.

정답 코드 (homework/answer/)

answer/Homework01.java

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/** TODO 파일 작성·읽기·삭제. */
public class Homework01 {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("todo.txt");
        Files.write(p, List.of("우유 사기", "숙제 끝내기", "운동 30분"));
        List<String> lines = Files.readAllLines(p);
        for (int i = 0; i < lines.size(); i++) {
            System.out.println((i + 1) + ") " + lines.get(i));
        }
        Files.deleteIfExists(p);
        System.out.println("삭제 완료");
    }
}

answer/Homework02.java

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/** 파일을 읽어 단어 수 세기. */
public class Homework02 {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("essay.txt");
        Files.writeString(p, "Java is fun. Java is powerful.");
        String text = Files.readString(p).trim();
        String[] words = text.split("[\\s.]+");
        int count = 0;
        for (String w : words) if (!w.isEmpty()) count++;
        System.out.println("단어 수=" + count);
        Files.deleteIfExists(p);
    }
}

직접 해 보기

bash
cd 04_예외_입출력/16_파일_IO/src
javac WriteFile.java
java WriteFile

다음 단원

[17_문자열_처리](../17_문자열_처리/) — `String`/`StringBuilder`/정규식을 배웁니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗