[2026] Java 입출력 | File, BufferedReader, NIO
이 글의 핵심
Java IO·NIO, 스트림·직렬화, Path·Files 실전, 대용량 처리와 CSV·JSON 예제까지 정리합니다.
들어가며
java.io·java.nio 계열은 바이트·문자 스트림으로 파일과 네트워크를 다루는 출발점입니다. 읽기/쓰기 경로를 열고 닫는 책임을 try-with-resources로 묶는 패턴이 흔합니다.
실무에서 마주한 현실
개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.
1. 파일 읽기 (IO)
BufferedReader
텍스트 파일을 효율적으로 읽는 가장 일반적인 방법입니다: 다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import java.io.*;
public class FileReadExample {
public static void main(String[] args) {
// try-with-resources 구문: 자동으로 리소스를 닫아줌
// () 안에 선언된 리소스는 try 블록이 끝나면 자동으로 close() 호출
try (BufferedReader br = new BufferedReader(
new FileReader("file.txt"))) {
// BufferedReader: 내부 버퍼(보통 8KB)를 사용해 I/O 횟수 감소
// FileReader를 감싸서 성능 향상
String line;
// readLine(): 한 줄씩 읽어옴 (줄바꿈 문자 제외)
// 파일 끝에 도달하면 null 반환
// (line = br.readLine()): 읽으면서 동시에 line 변수에 할당
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
// 파일이 존재하지 않을 때 발생
System.out.println("파일을 찾을 수 없습니다");
} catch (IOException e) {
// 파일 읽기 중 발생하는 기타 입출력 오류
System.out.println("파일 읽기 오류: " + e.getMessage());
}
// try 블록이 끝나면 br.close()가 자동 호출됨
}
}
성능 비교:
FileReader단독: 한 문자씩 읽음 → 느림BufferedReader+FileReader: 버퍼 단위로 읽음 → 빠름 (10~100배)
FileReader vs BufferedReader
두 방식의 성능 차이를 명확히 이해해봅시다: 다음은 java를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// FileReader: 한 문자씩 읽기 (느림)
try (FileReader fr = new FileReader("file.txt")) {
int ch;
// read(): 한 문자를 읽어서 int로 반환 (0~65535)
// 파일 끝이면 -1 반환
// 문제점: 파일에서 한 문자 읽을 때마다 디스크 I/O 발생
while ((ch = fr.read()) != -1) {
// int를 char로 캐스팅해서 출력
System.out.print((char) ch);
}
}
// 1000자 파일 → 1000번의 디스크 I/O 발생
// BufferedReader: 버퍼링 사용 (빠름)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
// readLine(): 한 줄 전체를 읽어서 String으로 반환
// 내부적으로 버퍼(보통 8KB)에 미리 읽어놓고 사용
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
// 1000자 파일 → 약 1~2번의 디스크 I/O만 발생 (버퍼 크기에 따라)
성능 차이 예시:
- 1MB 텍스트 파일 읽기
FileReader: 약 1,000,000번의 I/O → 수 초 소요BufferedReader: 약 128번의 I/O (8KB 버퍼) → 0.1초 미만 결론: 파일 I/O는 항상BufferedReader/BufferedWriter사용 권장
2. 파일 쓰기 (IO)
BufferedWriter
다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import java.io.*;
public class FileWriteExample {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("output.txt"))) {
bw.write("첫 번째 줄");
bw.newLine();
bw.write("두 번째 줄");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
추가 모드
아래 코드는 java를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 덮어쓰기 (기본)
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("output.txt"))) {
bw.write("새 내용");
}
// 추가 모드
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("output.txt", true))) {
bw.write("추가 내용");
}
3. NIO (Java 7+)
Files 클래스
다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class NIOExample {
public static void main(String[] args) throws IOException {
Path path = Path.of("file.txt");
// 파일 읽기 (전체)
String content = Files.readString(path);
System.out.println(content);
// 파일 읽기 (줄 단위)
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
// 파일 쓰기
Files.writeString(Path.of("output.txt"), "Hello, NIO!");
// 여러 줄 쓰기
List<String> linesToWrite = List.of("라인 1", "라인 2", "라인 3");
Files.write(Path.of("output.txt"), linesToWrite);
}
}
파일 조작
다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import java.nio.file.*;
// 파일 존재 확인
boolean exists = Files.exists(Path.of("file.txt"));
// 파일 삭제
Files.deleteIfExists(Path.of("temp.txt"));
// 파일 복사
Files.copy(
Path.of("source.txt"),
Path.of("dest.txt"),
StandardCopyOption.REPLACE_EXISTING
);
// 파일 이동
Files.move(
Path.of("old.txt"),
Path.of("new.txt"),
StandardCopyOption.REPLACE_EXISTING
);
// 디렉토리 생성
Files.createDirectories(Path.of("dir/subdir"));
4. 실전 예제
예제: 로그 파일 처리
다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import java.io.*;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
public class LogProcessor {
public static void processLog(String inputPath, String outputPath) {
try {
List<String> lines = Files.readAllLines(Path.of(inputPath));
List<String> errors = lines.stream()
.filter(line -> line.contains("ERROR"))
.collect(Collectors.toList());
Files.write(Path.of(outputPath), errors);
System.out.println("에러 로그 " + errors.size() + "개 추출");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
processLog("app.log", "errors.log");
}
}
정리
핵심 요약
- BufferedReader/Writer: 버퍼링으로 성능 향상
- try-with-resources: 자동 리소스 닫기
- NIO Files: 간결한 현대적 API
- Path: 파일 경로 표현
- 파일 조작: 복사, 이동, 삭제