공공부하자개발 · 영어 학습 노트
자바
실무 배치대용량 데이터 처리0/6 완료
  • 01배치 프로세스 개념과 아키텍처
  • 02대용량 파일 I/O (NIO, Buffered)
  • 03Chunk 단위 처리와 OOM 방지
  • 04트랜잭션: 커밋과 롤백 시뮬레이션
  • 05Skip과 Retry 로직 구현
  • 06메서드 활용 패턴 (배치 유틸)
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 실무 배치 › 02 / 6

대용량 파일 I/O (NIO, Buffered)

섹션 7진행 0 / 6
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

6. 연습 문제

문제 1

data/transactions.csv 를 읽어 type 이 WITHDRAW 인 행만 data/withdraws.csv 로 뽑아내는 프로그램을 작성하세요. 헤더는 유지하고, 메모리에 파일 전체를 올리지 않아야 하며, 출력은 임시 파일을 거쳐 원자적으로 이동해야 합니다. 마지막에 "입력 N건 중 M건 추출" 을 출력하세요.

정답 보기
java
Path src = Path.of("data/transactions.csv");
Path dest = Path.of("data/withdraws.csv");
Path tmp = dest.resolveSibling("withdraws.csv.tmp");
long total = 0, picked = 0;

try (BufferedReader r = Files.newBufferedReader(src, StandardCharsets.UTF_8);
     BufferedWriter w = Files.newBufferedWriter(tmp, StandardCharsets.UTF_8)) {
    String header = r.readLine();
    w.write(header); w.newLine();
    String line;
    while ((line = r.readLine()) != null) {
        total++;
        List<String> f = CsvParser.parseLine(line);
        if ("WITHDRAW".equals(f.get(3))) {
            w.write(line); w.newLine();
            picked++;
        }
    }
}
Files.move(tmp, dest, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
System.out.printf("입력 %,d건 중 %,d건 추출 -> %s%n", total, picked, dest);
// 출력: 입력 100,000건 중 60,1xx건 추출 -> data\withdraws.csv

문제 2

Files.walk 를 사용해 data/ 아래(하위 폴더 포함) 모든 .csv 파일의 이름과 크기(KB)를 크기 내림차순으로 출력하고, 합계를 출력하세요. 스트림은 반드시 닫아야 합니다.

정답 보기
java
record FileInfo(Path path, long bytes) {}

List<FileInfo> csvs;
try (Stream<Path> walk = Files.walk(Path.of("data"))) {
    csvs = walk.filter(Files::isRegularFile)
               .filter(p -> p.toString().endsWith(".csv"))
               .map(p -> {
                   try { return new FileInfo(p, Files.size(p)); }
                   catch (IOException e) { throw new UncheckedIOException(e); }
               })
               .sorted(Comparator.comparingLong(FileInfo::bytes).reversed())
               .toList();
}
long sum = 0;
for (FileInfo f : csvs) {
    System.out.printf("%-40s %,8d KB%n", f.path(), f.bytes() / 1024);
    sum += f.bytes();
}
System.out.printf("합계 %,d KB (%d개 파일)%n", sum / 1024, csvs.size());
// 출력:
// data\merged.csv                             4,309 KB
// data\transactions.csv                       4,309 KB
// data\parts\part-000.csv                     1,077 KB
// ...
// 합계 13,xxx KB (8개 파일)

문제 3

고정길이 전문 data/transactions.txt 를 읽어 날짜별 입금 합계를 계산하고, 결과를 날짜(8),합계(15, 우측정렬 0패딩) 형식의 고정길이 파일 data/daily_deposit.txt 로 쓰세요. String.format 을 사용하고, 결과 파일의 모든 줄이 정확히 23자인지 검증하는 코드도 포함하세요.

정답 보기
java
Map<String, Long> byDate = new TreeMap<>();
try (Stream<String> lines = Files.lines(Path.of("data/transactions.txt"), StandardCharsets.US_ASCII)) {
    lines.forEach(line -> {
        if (line.charAt(24) != 'D') return;
        String date = line.substring(10, 18);
        long amount = Long.parseLong(line.substring(25, 37).trim());
        byDate.merge(date, amount, Long::sum);
    });
}

Path out = Path.of("data/daily_deposit.txt");
try (BufferedWriter w = Files.newBufferedWriter(out, StandardCharsets.US_ASCII)) {
    for (Map.Entry<String, Long> e : byDate.entrySet()) {
        w.write(String.format("%8s%015d", e.getKey(), e.getValue()));   // 8 + 15 = 23
        w.newLine();
    }
}

// 검증
try (Stream<String> lines = Files.lines(out, StandardCharsets.US_ASCII)) {
    long bad = lines.filter(l -> l.length() != 23).count();
    System.out.println("길이 불일치 줄 수: " + bad);
}
System.out.println(byDate.size() + "일치 기록 -> " + out);
// 출력:
// 길이 불일치 줄 수: 0
// 28일치 기록 -> data\daily_deposit.txt
// 파일 내용 예: 20260901000000002345000
연습 문제
  • 문제 1
  • 문제 2
  • 문제 3
이전 섹션5 자주 하는 실수 (Tip)6 / 7다음 섹션7 정리