3주차 Unit 8.6 — FileWriter (한글 쓰기)

Psj·2026년 5월 20일

F-lab

목록 보기
111/230

Unit 8.6 — FileWriter (한글 쓰기)

F-LAB JAVA · 3주차 · Phase 8 · Stream 실전
🏆 Phase 8 완주 — Stream 실전 마스터


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • Writer 의 정의와 OutputStream 과의 차이 는?
  • FileWriter 의 정의와 한계는?
  • OutputStreamWriter 의 정의와 활용은?
  • 다양한 write 메서드 (int, char[], String, char[]+off+len) 의 차이는?
  • BufferedWriter 의 효과 (8KB 버퍼 + newLine) 는?
  • PrintWriter vs BufferedWriter 의 비교는?
  • 한글 쓰기 패턴 (인코딩 명시) 은?
  • Java 11+ FileWriter(File, Charset) 의 의미는?
  • Files.newBufferedWriter / writeString 의 활용은?
  • Phase 8 의 모든 학습 종합은?

🎯 핵심 한 문장

Writer 는 문자 (char) 단위로 쓰는 추상 클래스로, 인코딩을 적용 하여 문자를 바이트 시퀀스로 변환한다.
FileWriter 는 파일 전용 Writer 지만 Java 10 까지 인코딩 명시 불가 — Cross-platform 위험.
OutputStreamWriter 는 인코딩 명시 가능한 안전한 변환 도구 — 실무 권장.
BufferedWriter 를 결합하면 8KB 버퍼링 + newLine() 메서드로 효율적 줄 단위 쓰기.
Java 11+ 부터 FileWriter(File, Charset) 추가로 안전 사용 가능, 가장 권장은 Files.newBufferedWriter(path, UTF_8) 또는 Files.writeString(path, content, UTF_8).

비유 — 외국어 책 출판

OutputStream:
  글자 사진을 한 장씩 인쇄
  - 사진 1장 = 1바이트
  - 한글 의미 모르고 그냥 인쇄

Writer:
  사전 (인코딩) 으로 글자 → 인쇄
  - "안" 입력
  - 사전 보고 EC 95 88 (3바이트) 인쇄
  - 의미 보존

FileWriter (옛):
  사전을 시스템에서 가져옴
  - 환경 따라 다름
  - Cross-platform 위험

OutputStreamWriter (안전):
  사전 명시 ("UTF-8" 사용)
  - 어디서나 같은 결과
  - 안전

→ Writer = 인코딩 적용 + 문자 단위.


🧭 9개 섹션 로드맵

1. Writer 의 정의와 OutputStream 과의 차이
2. FileWriter 의 정의와 한계
3. OutputStreamWriter 의 정의와 활용
4. 다양한 write 메서드
5. BufferedWriter 와의 결합
6. PrintWriter vs BufferedWriter
7. 한글 쓰기 권장 패턴
8. Phase 8 완주 정리
9. 면접 + 자기 점검 + Phase 8 졸업 시험

1️⃣ Writer 의 정의와 OutputStream 과의 차이

1.1 Writer 의 정의

public abstract class Writer implements Appendable, Closeable, Flushable {
    
    // 핵심 메서드
    public void write(int c) throws IOException;
    public void write(char[] cbuf) throws IOException;
    public abstract void write(char[] cbuf, int off, int len) throws IOException;
    public void write(String str) throws IOException;
    public void write(String str, int off, int len) throws IOException;
    
    // Appendable (Java 5+)
    public Writer append(char c) throws IOException;
    public Writer append(CharSequence csq) throws IOException;
    public Writer append(CharSequence csq, int start, int end) throws IOException;
    
    // 추가
    public abstract void flush() throws IOException;
    public abstract void close() throws IOException;
}

핵심:

  • 문자 (char) 단위 처리
  • 인코딩 적용 (char → byte)
  • java.io 패키지
  • Java 1.1 부터

1.2 Writer 의 계층

Writer (추상)
  ├── OutputStreamWriter      ← OutputStream 을 Writer 로
  │   └── FileWriter           ← 파일 전용
  ├── BufferedWriter           ← 버퍼링 + newLine
  ├── PrintWriter              ← println, printf 편의
  ├── StringWriter             ← String 으로
  ├── CharArrayWriter         ← char[] 로
  ├── PipedWriter              ← 파이프
  └── FilterWriter

1.3 OutputStream vs Writer 결정적 차이

항목OutputStreamWriter
단위byte (8비트)char (16비트)
인코딩X (raw)✓ (자동 변환)
write(int)하위 8비트만char 의 16비트
한글 쓰기불가 (직접)OK (인코딩 매핑)
메서드write(int/byte[])write(int/char[]/String)
String 쓰기getBytes 필요직접

1.4 write(int) 의 비교

// OutputStream.write(int) — 하위 8비트
OutputStream os = new FileOutputStream("file.txt");
os.write('A');     // 0x41 (정상)
os.write('안');    // 하위 8비트만! ('안' = 0xC548, 하위 = 0x48 = 'H')
// ❌ 한글 깨짐

// Writer.write(int) — char 의 16비트
Writer w = new OutputStreamWriter(new FileOutputStream("file.txt"), UTF_8);
w.write('A');     // 0x41 → UTF-8 0x41 (1바이트)
w.write('안');    // 0xC548 → UTF-8 EC 95 88 (3바이트)
// ✓ 정상

1.5 String 쓰기

// OutputStream — getBytes 필요
OutputStream os = new FileOutputStream("file.txt");
os.write("안녕".getBytes(StandardCharsets.UTF_8));
// 매번 getBytes 호출

// Writer — 직접
Writer w = new OutputStreamWriter(
    new FileOutputStream("file.txt"), UTF_8);
w.write("안녕");
// 인코딩 자동 처리

1.6 시각화

"안녕" 쓰기:

OutputStream:
  매번 getBytes → 바이트 시퀀스 → write
  복잡, 인코딩 매번 처리

Writer:
  write("안녕")
  내부에서 인코딩 적용
  → 0xEC 0x95 0x88 0xEB 0x85 0x95 (UTF-8 6바이트)
  → OutputStream 으로 전달

추상화 = Writer 가 인코딩 처리

1.7 ILIC 활용 비교

// ❌ OutputStream 으로 한글 (귀찮음)
public void writeKoreanBad(Path path, String text) throws IOException {
    try (FileOutputStream fos = new FileOutputStream(path.toFile())) {
        fos.write(text.getBytes(StandardCharsets.UTF_8));
        // 매번 getBytes
    }
}

// ✓ Writer 로 한글
public void writeKoreanGood(Path path, String text) throws IOException {
    try (Writer w = new OutputStreamWriter(
            new FileOutputStream(path.toFile()),
            StandardCharsets.UTF_8)) {
        w.write(text);
        // 직접 String
    }
}

// ✓✓ BufferedWriter (더 효율)
public void writeKoreanBest(Path path, String text) throws IOException {
    try (BufferedWriter w = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
        w.write(text);
    }
}

1.8 자기 점검 답변

Writer 와 OutputStream 의 결정적 차이는?

:
1. 단위:

  • OutputStream: byte
  • Writer: char (인코딩 적용)
  1. 한글 처리:

    • OutputStream: 매번 getBytes
    • Writer: 직접 String
  2. write(int):

    • OutputStream: 하위 8비트
    • Writer: char 전체 (인코딩으로 변환)
  3. 메서드 종류:

    • OutputStream: write(int/byte[])
    • Writer: + write(String)
  4. 권장:

    • 텍스트: Writer
    • 바이너리: OutputStream

2️⃣ FileWriter 의 정의와 한계

2.1 FileWriter 의 정의

package java.io;

public class FileWriter extends OutputStreamWriter {
    
    // Java 10 이하
    public FileWriter(String fileName) throws IOException;
    public FileWriter(String fileName, boolean append) throws IOException;
    public FileWriter(File file) throws IOException;
    public FileWriter(File file, boolean append) throws IOException;
    public FileWriter(FileDescriptor fd);
    
    // Java 11+ 추가
    public FileWriter(String fileName, Charset charset) throws IOException;
    public FileWriter(File file, Charset charset) throws IOException;
    public FileWriter(String fileName, Charset charset, boolean append) throws IOException;
    public FileWriter(File file, Charset charset, boolean append) throws IOException;
}

핵심:

  • OutputStreamWriter 의 자식
  • 파일 전용
  • Java 10 까지 인코딩 명시 불가
  • Java 11+ 부터 Charset 생성자

2.2 기본 사용 (Java 10 이하)

// Java 10 이하 — 인코딩 명시 불가
try (FileWriter writer = new FileWriter("file.txt")) {
    writer.write("Hello\n");
    writer.write("안녕\n");   // ★ 시스템 인코딩 적용
}

// 문제:
// - 시스템 기본 인코딩
// - Windows MS949
// - Linux/Mac UTF-8
// - Cross-platform 위험

2.3 Java 11+ 의 개선

// Java 11+ 부터 안전
try (FileWriter writer = new FileWriter("file.txt", StandardCharsets.UTF_8)) {
    writer.write("Hello\n");
    writer.write("안녕\n");
    // 항상 UTF-8
}

// 모든 생성자 조합
new FileWriter("file.txt");                          // 기본 인코딩
new FileWriter("file.txt", true);                     // append + 기본
new FileWriter("file.txt", UTF_8);                    // UTF-8
new FileWriter("file.txt", UTF_8, true);              // UTF-8 + append

2.4 append 모드

// 덮어쓰기 (기본)
try (FileWriter writer = new FileWriter("log.txt", UTF_8)) {
    writer.write("New log\n");
    // 기존 내용 삭제
}

// 이어쓰기
try (FileWriter writer = new FileWriter("log.txt", UTF_8, true)) {
    writer.write("Additional log\n");
    // 기존 끝에 추가
}

// FileOutputStream 의 append 와 동일한 동작

2.5 FileWriter 의 한계

한계:

1. 인코딩 명시 (Java 10 이하) 불가
   - Java 11+ 에서 해결

2. NIO.2 API 어려움
   - Path 대신 String/File

3. BufferedWriter 결합 권장
   - 단독은 1문자씩 비효율

4. Files.newBufferedWriter 가 더 권장
   - 더 간결

2.6 ILIC 의 활용

public class ShipmentLogger {
    
    private final Path logFile;
    
    public ShipmentLogger(Path logFile) {
        this.logFile = logFile;
    }
    
    // Java 11+ FileWriter
    public void log(String message) throws IOException {
        try (FileWriter writer = new FileWriter(
                logFile.toFile(), StandardCharsets.UTF_8, true)) {
            writer.write(LocalDateTime.now() + " " + message + "\n");
        }
    }
    
    // BufferedWriter 결합 (더 효율)
    public void logBuffered(String message) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(
                new FileWriter(logFile.toFile(), StandardCharsets.UTF_8, true))) {
            writer.write(LocalDateTime.now() + " " + message);
            writer.newLine();
        }
    }
    
    // NIO.2 (가장 권장)
    public void logModern(String message) throws IOException {
        String line = LocalDateTime.now() + " " + message + "\n";
        Files.writeString(logFile, line, StandardCharsets.UTF_8,
            StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
}

2.7 자기 점검 답변

FileWriter 의 정의와 한계는?

:
1. 정의:

  • OutputStreamWriter 의 자식
  • 파일 전용 Writer
  1. 한계 (Java 10 이하):

    • 인코딩 명시 불가
    • 시스템 기본 사용
    • Cross-platform 위험
  2. Java 11+ 개선:

    • FileWriter(File, Charset) 추가
    • FileWriter(File, Charset, boolean append) 추가
    • 안전한 사용 가능
  3. append 모드:

    • 4번째 boolean 매개변수
    • true = 이어쓰기
  4. 실무 권장:

    • Files.newBufferedWriter 더 좋음
    • BufferedWriter 결합

3️⃣ OutputStreamWriter 의 정의와 활용

3.1 OutputStreamWriter 의 정의

package java.io;

public class OutputStreamWriter extends Writer {
    
    public OutputStreamWriter(OutputStream out);
    public OutputStreamWriter(OutputStream out, String charsetName) 
        throws UnsupportedEncodingException;
    public OutputStreamWriter(OutputStream out, Charset cs);
    public OutputStreamWriter(OutputStream out, CharsetEncoder enc);
}

핵심:

  • Writer 의 직접 자식
  • OutputStream 을 Writer 로 변환 (Bridge)
  • 인코딩 명시 가능
  • Java 1.1 부터

3.2 기본 사용

// 파일 — OutputStreamWriter
try (OutputStreamWriter osw = new OutputStreamWriter(
        new FileOutputStream("file.txt"),
        StandardCharsets.UTF_8)) {
    osw.write("Hello\n");
    osw.write("안녕\n");
}

// 표준 출력
try (OutputStreamWriter osw = new OutputStreamWriter(
        System.out, StandardCharsets.UTF_8)) {
    osw.write("출력\n");
    osw.flush();
}

// 메모리
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
    osw.write("안녕");
}
byte[] bytes = baos.toByteArray();
// UTF-8 인코딩된 바이트

3.3 다양한 OutputStream 활용

// 1. 파일
new OutputStreamWriter(new FileOutputStream("file.txt"), UTF_8);

// 2. 표준 출력
new OutputStreamWriter(System.out, UTF_8);

// 3. 표준 에러
new OutputStreamWriter(System.err, UTF_8);

// 4. 메모리
new OutputStreamWriter(new ByteArrayOutputStream(), UTF_8);

// 5. 소켓 (네트워크)
new OutputStreamWriter(socket.getOutputStream(), UTF_8);

// 6. 압축
new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream("file.gz")), UTF_8);

// 모든 OutputStream 을 Writer 로

3.4 인코딩 명시의 4가지 방법

// 1. 인코딩 없음 (위험, 시스템 기본)
new OutputStreamWriter(out);

// 2. 문자열 이름
new OutputStreamWriter(out, "UTF-8");
// UnsupportedEncodingException 가능

// 3. Charset 객체 (권장)
new OutputStreamWriter(out, StandardCharsets.UTF_8);

// 4. CharsetEncoder (고급)
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
encoder.onMalformedInput(CodingErrorAction.REPLACE);
new OutputStreamWriter(out, encoder);

3.5 OutputStreamWriter 의 내부

사용자 호출:
  osw.write("안녕");

내부 동작:
  1. CharsetEncoder 가 인코딩
     - "안" (U+C548) → 0xEC 0x95 0x88 (UTF-8 3바이트)
     - "녕" (U+B155) → 0xEB 0x85 0x95
  
  2. OutputStream 으로 바이트 전달
     - 6바이트 write 호출

3. flush 가 강제 출력

복잡한 처리를 추상화
사용자는 String 그대로

3.6 ILIC 활용

public class ShipmentMultiSource {
    
    private static final Charset CHARSET = StandardCharsets.UTF_8;
    
    // 1. 파일 쓰기
    public void writeFile(Path path, String content) throws IOException {
        try (OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream(path.toFile()), CHARSET)) {
            osw.write(content);
        }
    }
    
    // 2. 표준 출력 한글
    public void printKorean(String message) throws IOException {
        try (OutputStreamWriter osw = new OutputStreamWriter(System.out, CHARSET)) {
            osw.write(message);
            osw.write('\n');
            osw.flush();
        }
    }
    
    // 3. 네트워크 응답
    public void sendResponse(Socket socket, String content) throws IOException {
        try (OutputStreamWriter osw = new OutputStreamWriter(
                socket.getOutputStream(), CHARSET);
             BufferedWriter bw = new BufferedWriter(osw)) {
            bw.write(content);
            bw.flush();
        }
    }
    
    // 4. 압축 + 인코딩
    public void writeGzip(Path path, String content) throws IOException {
        try (OutputStreamWriter osw = new OutputStreamWriter(
                new GZIPOutputStream(new FileOutputStream(path.toFile())),
                CHARSET);
             BufferedWriter bw = new BufferedWriter(osw)) {
            bw.write(content);
        }
    }
    
    // 5. 메모리에 인코딩
    public byte[] encodeToBytes(String text) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (OutputStreamWriter osw = new OutputStreamWriter(baos, CHARSET)) {
            osw.write(text);
        }
        return baos.toByteArray();
    }
}

3.7 자기 점검 답변

OutputStreamWriter 의 정의와 활용은?

:
1. 정의:

  • Writer 의 직접 자식
  • OutputStream → Writer (Bridge)
  1. 인코딩 명시:

    • String (UnsupportedEncodingException)
    • Charset (권장)
    • CharsetEncoder (고급)
  2. 활용:

    • 다양한 OutputStream
    • 파일, 표준 출력, 네트워크, 압축, 메모리
  3. 내부:

    • CharsetEncoder 가 인코딩
    • char → byte 변환
    • OutputStream 으로 전달
  4. 권장:

    • BufferedWriter 결합
    • StandardCharsets.UTF_8

4️⃣ 다양한 write 메서드

4.1 5가지 write 메서드

public abstract class Writer {
    
    // 1. 1문자 쓰기
    public void write(int c) throws IOException;
    
    // 2. char[] 전체
    public void write(char[] cbuf) throws IOException;
    
    // 3. char[] 일부
    public abstract void write(char[] cbuf, int off, int len) throws IOException;
    
    // 4. String 전체
    public void write(String str) throws IOException;
    
    // 5. String 일부
    public void write(String str, int off, int len) throws IOException;
}

4.2 write(int c) — 1문자 쓰기

// 1문자 쓰기
Writer w = ...;

w.write('A');         // 'A' → 0x41 (UTF-8 1바이트)
w.write(65);          // 위와 동일
w.write('안');         // '안' → 0xEC 0x95 0x88 (UTF-8 3바이트)
w.write(0xC548);      // 위와 동일

// 16비트 (char) 의 범위
// 0 ~ 65535
// 이모지 등 Supplementary 문자는 Surrogate Pair 두 번

4.3 write(char[]) — char 배열

// 전체
char[] chars = "Hello".toCharArray();
w.write(chars);   // "Hello" 5문자

// 한글
char[] korean = "안녕".toCharArray();
w.write(korean);   // 2 char (UTF-8 6바이트)

// 부분
w.write(chars, 0, 3);   // "Hel"

4.4 write(String) — String 직접

// 가장 편리
w.write("Hello, World!");

// 부분
w.write("Hello, World!", 7, 5);   // "World"

// 한글
w.write("안녕하세요");

// 인코딩 적용:
// "안" → 0xEC 0x95 0x88
// "녕" → 0xEB 0x85 0x95
// ...

4.5 write(char[], off, len) — 마지막 함정

// read(char[]) 와 짝
try (Reader r = Files.newBufferedReader(src);
     Writer w = Files.newBufferedWriter(dest)) {
    char[] buf = new char[8192];
    int n;
    while ((n = r.read(buf)) != -1) {
        w.write(buf, 0, n);   // ★ n 만큼만!
    }
}

// ❌ 잘못
w.write(buf);   // buf.length 전체
// 마지막 read 후 이전 데이터 누적

// Unit 8.3 의 함정과 동일

4.6 append 메서드 (Java 5+)

// Appendable 인터페이스
Writer w = ...;

w.append('A');                // == write('A')
w.append("Hello");             // == write("Hello")
w.append("Hello, World!", 7, 12);   // "World"

// 체이닝
w.append("a").append("b").append("c");
// "abc" 쓰기

// CharSequence 호환
StringBuilder sb = new StringBuilder("Hello");
w.append(sb);

4.7 write 의 비교

메서드매개변수특징
write(int)1문자단순, 비효율
write(char[])배열 전체효율적
write(char[], off, len)배열 일부정확한 제어
write(String)문자열가장 편리
write(String, off, len)문자열 일부substring 대안
appendchar/String체이닝, NPE 회피

4.8 ILIC 활용

public class ShipmentTextWriter {
    
    private static final Charset CHARSET = StandardCharsets.UTF_8;
    
    // 1. 1문자씩 (드물게)
    public void writeChars(Path path, String text) throws IOException {
        try (Writer w = Files.newBufferedWriter(path, CHARSET)) {
            for (int i = 0; i < text.length(); i++) {
                w.write(text.charAt(i));
            }
        }
    }
    
    // 2. char[]
    public void writeCharArray(Path path, char[] data, int len) throws IOException {
        try (Writer w = Files.newBufferedWriter(path, CHARSET)) {
            w.write(data, 0, len);
        }
    }
    
    // 3. String (가장 편리)
    public void writeString(Path path, String content) throws IOException {
        try (Writer w = Files.newBufferedWriter(path, CHARSET)) {
            w.write(content);
        }
    }
    
    // 4. 복사 (read + write)
    public void copyText(Path src, Path dest) throws IOException {
        try (BufferedReader r = Files.newBufferedReader(src, CHARSET);
             BufferedWriter w = Files.newBufferedWriter(dest, CHARSET)) {
            char[] buf = new char[8192];
            int n;
            while ((n = r.read(buf)) != -1) {
                w.write(buf, 0, n);   // ★ n 만큼만
            }
        }
    }
    
    // 5. append 체이닝
    public void appendChained(Path path) throws IOException {
        try (Writer w = Files.newBufferedWriter(path, CHARSET)) {
            w.append("First line\n")
                .append("Second line\n")
                .append("Third line\n");
        }
    }
}

4.9 자기 점검 답변

write 메서드 5가지의 차이는?

:
1. write(int):

  • 1문자
  • char 의 16비트
  1. write(char[]):

    • 배열 전체
    • 효율적
  2. write(char[], off, len):

    • 배열 일부
    • read 와 짝: write(buf, 0, n)
  3. write(String):

    • 가장 편리
    • 자주 사용
  4. write(String, off, len):

    • 문자열 일부
    • substring 대안
  5. append:

    • 체이닝
    • Appendable 인터페이스

5️⃣ BufferedWriter 와의 결합

5.1 BufferedWriter 의 정의

public class BufferedWriter extends Writer {
    
    public BufferedWriter(Writer out);
    public BufferedWriter(Writer out, int sz);
    
    // 핵심 메서드
    public void newLine() throws IOException;
    
    // 상속
    public void write(int c);
    public void write(char[] cbuf, int off, int len);
    public void write(String s, int off, int len);
    public void flush();
    public void close();
}

특징:

  • Writer + 버퍼링 (Decorator)
  • 기본 8KB 버퍼
  • newLine() 메서드 (시스템 줄바꿈)

5.2 결합 패턴

// 1. 기본 결합
try (BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(
            new FileOutputStream("file.txt"),
            StandardCharsets.UTF_8))) {
    bw.write("Hello");
    bw.newLine();
    bw.write("안녕");
}

// 2. Java 11+ FileWriter
try (BufferedWriter bw = new BufferedWriter(
        new FileWriter("file.txt", StandardCharsets.UTF_8))) {
    bw.write("...");
}

// 3. NIO.2 (가장 권장)
try (BufferedWriter bw = Files.newBufferedWriter(
        Path.of("file.txt"), StandardCharsets.UTF_8)) {
    bw.write("...");
}

5.3 newLine() 의 동작

public void newLine() throws IOException;

// 시스템의 line.separator 사용
// - Unix/Mac: \n
// - Windows: \r\n

// 사용
bw.write("Line 1");
bw.newLine();         // 시스템 줄바꿈
bw.write("Line 2");
bw.newLine();

// 또는 직접
bw.write("Line 1\n");   // Unix
bw.write("Line 1\r\n"); // Windows

// 어느 게 좋나?
// - 텍스트 파일: \n (대부분 OS 인식)
// - Windows 네이티브: \r\n
// - newLine(): 시스템 의존 (Cross-platform 텍스트는 \n 권장)

5.4 BufferedWriter 의 버퍼링

일반 Writer:
  write() → OutputStreamWriter → CharsetEncoder → OutputStream → OS
  매 write 가 system call

BufferedWriter (8KB):
  write() → BufferedWriter 의 char[8192]
  버퍼 가득 시 flush
  대부분 메모리 접근
  
효과:
  - write 호출 빠름
  - 매 write 마다 system call X
  - flush() 또는 close() 시 강제 출력

5.5 flush 의 중요성

// ❌ flush 누락
BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"));
bw.write("important data");
// JVM 종료 시 8KB 버퍼 내용 손실 가능!

// ✓ try-with-resources (자동 flush + close)
try (BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))) {
    bw.write("important data");
}
// try 종료 시 close → 자동 flush

// ✓ 명시적 flush (진행 중)
try (BufferedWriter bw = ...) {
    for (int i = 0; i < 100; i++) {
        bw.write(data[i]);
        bw.newLine();
        
        if (i % 10 == 0) {
            bw.flush();   // 10개마다 강제
        }
    }
}

5.6 BufferedWriter vs PrintWriter

항목BufferedWriterPrintWriter
줄바꿈newLine()println()
printfX
예외IOException 던짐안 던짐 (checkError)
autoFlushX옵션
권장일반 텍스트콘솔, 디버깅

5.7 ILIC 활용

public class ShipmentReportWriter {
    
    private static final Charset CHARSET = StandardCharsets.UTF_8;
    
    // 1. 기본 패턴
    public void writeReport(Path path, List<Shipment> shipments) throws IOException {
        try (BufferedWriter w = Files.newBufferedWriter(path, CHARSET)) {
            w.write("ID,선적번호,수하인,중량");
            w.newLine();
            
            for (Shipment s : shipments) {
                w.write(s.getId() + ",");
                w.write(s.getBlNo() + ",");
                w.write(s.getConsignee() + ",");
                w.write(s.getWeight().toString());
                w.newLine();
            }
        }
    }
    
    // 2. 큰 데이터 (주기적 flush)
    public void writeLargeReport(Path path, Iterator<Shipment> shipments) throws IOException {
        try (BufferedWriter w = Files.newBufferedWriter(path, CHARSET)) {
            int count = 0;
            while (shipments.hasNext()) {
                Shipment s = shipments.next();
                w.write(s.toCsvLine());
                w.newLine();
                
                count++;
                if (count % 1000 == 0) {
                    w.flush();
                    log.info("Written {} records", count);
                }
            }
        }
    }
    
    // 3. append 모드
    public void appendLog(Path logFile, String message) throws IOException {
        try (BufferedWriter w = Files.newBufferedWriter(
                logFile, CHARSET,
                StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
            w.write(LocalDateTime.now() + " " + message);
            w.newLine();
        }
    }
}

5.8 자기 점검 답변

BufferedWriter 의 효과는?

:
1. 정의:

  • Writer + 버퍼링 (Decorator)
  • 8KB 기본 버퍼
  1. 핵심 메서드:

    • newLine(): 시스템 줄바꿈
    • write: 효율적 (버퍼)
    • flush: 강제 출력
  2. 버퍼링 효과:

    • write 마다 system call X
    • 메모리 접근
    • 빠른 처리
  3. 결합 패턴:

    • BufferedWriter + OutputStreamWriter + FileOutputStream
    • 또는 BufferedWriter + FileWriter
    • 또는 Files.newBufferedWriter (가장 권장)
  4. flush:

    • close 가 자동 flush
    • 명시적 flush (진행 중)

6️⃣ PrintWriter vs BufferedWriter

6.1 PrintWriter 의 정의

public class PrintWriter extends Writer {
    
    public PrintWriter(Writer out);
    public PrintWriter(Writer out, boolean autoFlush);
    public PrintWriter(OutputStream out);
    public PrintWriter(OutputStream out, boolean autoFlush);
    public PrintWriter(OutputStream out, boolean autoFlush, Charset charset);
    public PrintWriter(File file);
    public PrintWriter(File file, Charset charset);
    // ... 기타
    
    // 편의 메서드
    public void print(...);
    public void println(...);
    public PrintWriter printf(String format, Object... args);
    public PrintWriter format(String format, Object... args);
    
    // 예외 안 던짐 — checkError 로 확인
    public boolean checkError();
}

6.2 BufferedWriter vs PrintWriter

// BufferedWriter — 표준
try (BufferedWriter bw = Files.newBufferedWriter(path)) {
    bw.write("Hello");
    bw.newLine();
    // printf 없음
}

// PrintWriter — 편의
try (PrintWriter pw = new PrintWriter(
        new FileWriter("file.txt", UTF_8))) {
    pw.println("Hello");
    pw.printf("Value: %d%n", 42);
    pw.print("No newline");
    // 예외 안 던짐
}

6.3 println 의 동작

public void println();
public void println(boolean b);
public void println(char c);
public void println(int i);
public void println(long l);
public void println(float f);
public void println(double d);
public void println(char[] x);
public void println(String x);
public void println(Object x);

// 동작:
// 1. print 호출
// 2. line.separator 추가 (\n 또는 \r\n)

pw.println("Hello");
// "Hello\n" (Unix) 또는 "Hello\r\n" (Windows)

6.4 printf — 형식 지정

PrintWriter pw = new PrintWriter("file.txt");

// 다양한 형식
pw.printf("Name: %s%n", "Alice");
pw.printf("Age: %d%n", 30);
pw.printf("Price: %.2f%n", 19.99);
pw.printf("%-10s%5d%n", "Apple", 100);

// %n = 시스템 줄바꿈
// %s = String
// %d = int
// %f = float/double
// %.2f = 소수점 2자리
// %-10s = 왼쪽 정렬 10칸
// %5d = 오른쪽 정렬 5칸

6.5 PrintWriter 의 함정

// 함정 1: 예외 안 던짐
PrintWriter pw = new PrintWriter("file.txt");
pw.println("Hello");
// 디스크 풀이어도 예외 X
// 데이터 손실 가능

// 명시적 검사
pw.println("Hello");
if (pw.checkError()) {
    System.err.println("Write failed!");
}

// 함정 2: 인코딩 명시 안 함 (옛 생성자)
new PrintWriter("file.txt");   // 시스템 인코딩
// Java 11+ 권장:
new PrintWriter("file.txt", StandardCharsets.UTF_8);

// 함정 3: autoFlush
PrintWriter pw1 = new PrintWriter(out);          // autoFlush = false
PrintWriter pw2 = new PrintWriter(out, true);    // autoFlush = true
// pw2 는 println 후 자동 flush

6.6 System.out 의 정체

System.out 의 정체:

  public static final PrintStream out;

// PrintStream 은 PrintWriter 와 유사
// 차이:
// - PrintStream: byte 기반 (OutputStream 의 자식)
// - PrintWriter: char 기반 (Writer 의 자식)

// 둘 다 println, printf 지원

System.out.println("Hello");
// 내부적으로 인코딩 적용
// 시스템 콘솔로

6.7 어느 것을 언제?

BufferedWriter 권장:
  ✓ 표준
  ✓ 명시적 newLine
  ✓ 예외 처리 명확
  ✓ 일반 텍스트 파일

PrintWriter 권장:
  ✓ printf 필요
  ✓ println 의 편의
  ✓ 콘솔 출력 스타일

PrintStream 권장:
  ✓ System.out 처럼
  ✓ byte 기반 출력
  ✓ println 의 편의

선택:
  - 일반: BufferedWriter
  - 형식: PrintWriter
  - 콘솔: System.out (PrintStream)

6.8 ILIC 활용

public class ShipmentReportFormatter {
    
    // BufferedWriter — 표준 텍스트
    public void writeCsv(Path path, List<Shipment> shipments) throws IOException {
        try (BufferedWriter w = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
            w.write("ID,BL,Weight");
            w.newLine();
            for (Shipment s : shipments) {
                w.write(s.toCsvLine());
                w.newLine();
            }
        }
    }
    
    // PrintWriter — 형식 있는 보고서
    public void writeFormatted(Path path, List<Shipment> shipments) throws IOException {
        try (PrintWriter pw = new PrintWriter(
                new FileWriter(path.toFile(), StandardCharsets.UTF_8))) {
            
            pw.println("=== Shipment Report ===");
            pw.printf("Generated: %s%n", LocalDateTime.now());
            pw.printf("Total: %d%n", shipments.size());
            pw.println();
            
            pw.printf("%-10s %-20s %10s%n", "ID", "BL", "Weight");
            pw.println("-".repeat(40));
            
            for (Shipment s : shipments) {
                pw.printf("%-10d %-20s %10.2f%n",
                    s.getId(), s.getBlNo(), s.getWeight());
            }
            
            // 검사
            if (pw.checkError()) {
                throw new IOException("Write failed");
            }
        }
    }
}

6.9 자기 점검 답변

PrintWriter vs BufferedWriter?

:
1. BufferedWriter:

  • 표준
  • newLine()
  • 예외 던짐
  1. PrintWriter:

    • println, printf
    • 편의
    • 예외 안 던짐 (checkError)
  2. 선택:

    • 일반: BufferedWriter
    • 형식: PrintWriter
    • 콘솔: System.out (PrintStream)
  3. 함정:

    • PrintWriter 의 예외 마스킹
    • checkError 명시적 호출

7️⃣ 한글 쓰기 권장 패턴

7.1 권장 패턴 종합

// 1. 가장 권장 — NIO.2 + BufferedWriter
try (BufferedWriter w = Files.newBufferedWriter(
        Path.of("file.txt"), StandardCharsets.UTF_8)) {
    w.write("안녕하세요");
    w.newLine();
}

// 2. 짧은 콘텐츠 — Files.writeString
Files.writeString(Path.of("file.txt"), "안녕하세요\n",
    StandardCharsets.UTF_8);

// 3. 여러 줄 — Files.write
List<String> lines = List.of("첫 줄", "둘째 줄", "셋째 줄");
Files.write(Path.of("file.txt"), lines, StandardCharsets.UTF_8);

// 4. append — Files.writeString + APPEND
Files.writeString(Path.of("log.txt"), "추가 로그\n",
    StandardCharsets.UTF_8,
    StandardOpenOption.CREATE, StandardOpenOption.APPEND);

// 5. 다양한 OutputStream — OutputStreamWriter
try (OutputStreamWriter osw = new OutputStreamWriter(
        socket.getOutputStream(), StandardCharsets.UTF_8);
     BufferedWriter bw = new BufferedWriter(osw)) {
    bw.write("응답");
}

7.2 인코딩 상수 활용

public class IoConstants {
    public static final Charset UTF8 = StandardCharsets.UTF_8;
    public static final int BUFFER_SIZE = 8192;
}

// 일관된 사용
Files.newBufferedWriter(path, IoConstants.UTF8);
Files.writeString(path, content, IoConstants.UTF8);

7.3 자주 발생하는 인코딩 버그 5가지

1. CSV Excel 에서 한글 깨짐
   원인: Excel 의 BOM 요구
   해결: UTF-8 BOM 추가

2. HTTP 응답 한글 깨짐
   원인: Content-Type charset 누락
   해결: charset=UTF-8 명시

3. DB 저장 후 깨짐
   원인: DB 인코딩 불일치
   해결: 모두 UTF-8

4. 파일명 한글 깨짐
   원인: 파일 시스템 인코딩
   해결: -Dfile.encoding=UTF-8

5. 컴파일러 인코딩
   원인: 소스 파일 인코딩
   해결: -encoding UTF-8 (javac)

7.4 Excel 호환 CSV (BOM)

// Excel 이 UTF-8 BOM 을 인식해서 한글 정상 표시

public void exportCsvForExcel(Path path, List<Shipment> shipments) throws IOException {
    try (OutputStream os = Files.newOutputStream(path);
         BufferedWriter bw = new BufferedWriter(
             new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
        
        // BOM 쓰기 (Excel 한글 인식)
        os.write(0xEF);
        os.write(0xBB);
        os.write(0xBF);
        // ★ Writer 가 아닌 OutputStream 으로 직접 BOM
        
        // 데이터
        bw.write("ID,선적번호,수하인");
        bw.newLine();
        for (Shipment s : shipments) {
            bw.write(s.toCsvLine());
            bw.newLine();
        }
    }
}

// 또는 더 간단
public void exportCsvForExcel2(Path path, String csvContent) throws IOException {
    byte[] bom = new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF};
    byte[] data = csvContent.getBytes(StandardCharsets.UTF_8);
    
    try (OutputStream os = Files.newOutputStream(path)) {
        os.write(bom);
        os.write(data);
    }
}

7.5 한글 검증

public boolean isValidKorean(Path path) throws IOException {
    try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
        String line;
        while ((line = br.readLine()) != null) {
            // U+FFFD (replacement character) 검사
            if (line.contains("\uFFFD")) {
                return false;
            }
        }
        return true;
    }
}

public void writeAndVerify(Path path, String content) throws IOException {
    // 쓰기
    Files.writeString(path, content, StandardCharsets.UTF_8);
    
    // 검증 (라운드 트립)
    String readBack = Files.readString(path, StandardCharsets.UTF_8);
    if (!content.equals(readBack)) {
        throw new IOException("Round-trip failed");
    }
}

7.6 ILIC 의 한글 처리 종합

@Service
public class ShipmentExportService {
    
    private static final Charset CHARSET = StandardCharsets.UTF_8;
    
    // 1. 일반 한글 CSV
    public Path exportCsv(List<Shipment> shipments) throws IOException {
        Path path = Path.of("/var/shipment/exports/shipments.csv");
        Files.createDirectories(path.getParent());
        
        try (BufferedWriter w = Files.newBufferedWriter(path, CHARSET)) {
            w.write("ID,선적번호,수하인,중량(kg)");
            w.newLine();
            
            for (Shipment s : shipments) {
                w.write(s.getId() + ",");
                w.write(s.getBlNo() + ",");
                w.write(s.getConsignee() + ",");
                w.write(s.getWeight().toString());
                w.newLine();
            }
        }
        
        return path;
    }
    
    // 2. Excel 호환 CSV (BOM)
    public Path exportCsvForExcel(List<Shipment> shipments) throws IOException {
        Path path = Path.of("/var/shipment/exports/shipments_excel.csv");
        Files.createDirectories(path.getParent());
        
        try (OutputStream os = Files.newOutputStream(path);
             OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET);
             BufferedWriter w = new BufferedWriter(osw)) {
            
            // BOM (Writer 거치지 않고)
            os.write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});
            
            // 데이터
            w.write("ID,선적번호,수하인,중량(kg)");
            w.newLine();
            for (Shipment s : shipments) {
                w.write(s.toCsvLine());
                w.newLine();
            }
        }
        
        return path;
    }
    
    // 3. JSON 한글
    public Path exportJson(Shipment shipment) throws IOException {
        Path path = Path.of("/var/shipment/exports/shipment.json");
        Files.createDirectories(path.getParent());
        
        String json = String.format(
            "{\"id\":%d,\"수하인\":\"%s\",\"비고\":\"%s\"}",
            shipment.getId(),
            escape(shipment.getConsignee()),
            escape(shipment.getNotes()));
        
        Files.writeString(path, json, CHARSET);
        return path;
    }
    
    // 4. 로그 (append + 한글)
    public void log(String level, String message) throws IOException {
        Path logFile = Path.of("/var/log/shipment/app.log");
        Files.createDirectories(logFile.getParent());
        
        String line = String.format("[%s] [%s] %s%n",
            LocalDateTime.now(), level, message);
        Files.writeString(logFile, line, CHARSET,
            StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
    
    // 5. HTTP 응답 (네트워크)
    public void writeResponse(OutputStream out, Shipment s) throws IOException {
        try (OutputStreamWriter osw = new OutputStreamWriter(out, CHARSET);
             BufferedWriter w = new BufferedWriter(osw)) {
            w.write("HTTP/1.1 200 OK\r\n");
            w.write("Content-Type: application/json; charset=UTF-8\r\n");
            w.write("\r\n");
            w.write(String.format("{\"id\":%d}", s.getId()));
        }
    }
    
    private String escape(String s) {
        return s == null ? "" : s.replace("\"", "\\\"");
    }
}

7.7 자기 점검 답변

한글 쓰기의 권장 패턴은?

:
1. 가장 권장:

  • Files.newBufferedWriter(path, UTF_8)
  • 또는 Files.writeString(path, content, UTF_8)
  1. 인코딩 명시:

    • StandardCharsets.UTF_8
    • 일관성
    • Cross-platform 안전
  2. Excel 호환:

    • UTF-8 BOM 추가
    • OutputStream 으로 직접 BOM
  3. append:

    • StandardOpenOption.APPEND
  4. 흔한 버그 5가지:

    • CSV Excel, HTTP, DB, 파일명, 컴파일러

8️⃣ Phase 8 완주 정리

8.1 Phase 8 학습 종합

Phase 8 — Stream 실전

Unit 8.1 — System.in (한글 안 되는 이유)
  - System.in 의 정체 (InputStream)
  - 1바이트 read 의 한계
  - 인코딩 기초
  - Scanner, BufferedReader, Console 비교

Unit 8.2 — FileInputStream
  - InputStream 의 파일 전용
  - read() 의 정확한 동작
  - EOF (-1) 의 이유
  - try-with-resources 필수

Unit 8.3 — byte[] 배열로 효율적 읽기
  - read(byte[]) 의 효율
  - 마지막 읽기 함정 (n 만큼만)
  - for-each 위험성
  - 버퍼 크기 (8KB sweet spot)

Unit 8.4 — FileOutputStream
  - OutputStream 의 파일 전용
  - 덮어쓰기 vs append
  - write 메서드 종류
  - 바이트가 문자로 보이는 이유

Unit 8.5 — 한글 처리 (FileReader, InputStreamReader)
  - Reader vs InputStream
  - FileReader 의 한계
  - InputStreamReader 의 Bridge 패턴
  - BufferedReader 결합

Unit 8.6 — FileWriter (한글 쓰기) ← 여기
  - Writer vs OutputStream
  - FileWriter 의 한계
  - OutputStreamWriter
  - BufferedWriter, PrintWriter
  - Phase 8 완주

8.2 Phase 8 마스터 후 가능한 일

1. 파일 입출력 자유자재
   - 텍스트 / 바이너리
   - 한글 안전
   - 인코딩 명시

2. 효율적 처리
   - byte[] 활용
   - 버퍼링
   - Stream API

3. 다양한 소스
   - 파일, 네트워크, 메모리
   - InputStream/Reader 추상화
   - OutputStream/Writer 추상화

4. 실무 함정 회피
   - 1바이트 read 한계
   - 마지막 읽기 n
   - for-each 위험
   - 인코딩 명시
   - 디렉토리 자동 생성 X

5. NIO.2 활용
   - Files.newBufferedReader/Writer
   - Files.readString/writeString
   - Stream 통합

8.3 Phase 8 의 큰 그림

Stream 의 두 가지 차원:

1. 바이트 vs 문자
   - byte: InputStream/OutputStream
   - char: Reader/Writer
   - 한글: 반드시 Reader/Writer

2. 입력 vs 출력
   - 입력: InputStream/Reader
   - 출력: OutputStream/Writer
   - 짝: write(buf, 0, n)

핵심 패턴:
  - try-with-resources
  - 인코딩 명시 (UTF-8)
  - BufferedXxx 결합
  - NIO.2 Files 활용

8.4 Phase 8 → Phase 9 의 연결

Phase 8: 기본 Stream
   ↓
Phase 9: Stream 의 강화
   - try-with-resources 정밀
   - BufferedInput/OutputStream
   - DataInput/OutputStream
   - Serialization (직렬화)
   - serialVersionUID

8.5 자주 발생하는 함정 종합

Phase 8 의 흔한 함정 10가지:

1. System.in.close()
   → 표준 입력 영구 종료, 절대 X

2. 1바이트 read 로 한글 처리
   → 깨짐. Reader 사용

3. 인코딩 명시 안 함
   → 시스템 의존, Cross-platform 위험

4. 마지막 read 후 buf.length 사용
   → 이전 데이터 처리

5. for-each (byte b : buf)
   → buf.length 전체 순회

6. write(buf) 후 write(buf, 0, n) 누락
   → 이전 데이터 누적

7. close() 누락
   → 파일 핸들 누수

8. flush() 누락 (Buffered)
   → 데이터 손실 가능

9. 부모 디렉토리 자동 생성 가정
   → FileNotFoundException

10. PrintWriter 의 예외 마스킹
   → checkError() 누락 시 손실

8.6 3주차 누적 진행

✅ Phase 1 — Pass by Value (3 Unit)
✅ Phase 2 — 컬렉션 프레임워크 (6 Unit)
✅ Phase 3 — 해시의 원리 (4 Unit)
✅ Phase 4 — 추상화의 두 도구 (4 Unit)
✅ Phase 5 — 제네릭과 와일드카드 (5 Unit)
✅ Phase 6 — 객체 비교 (4 Unit)
✅ Phase 7 — I/O 시스템 큰 그림 (5 Unit)
✅ Phase 8 — Stream 실전 (6 Unit) ← 여기, 완주
⏭ Phase 9 — I/O 강화 (5 Unit)
⏭ Phase 10 — 함수형 프로그래밍 (4 Unit)

총: 37/43 Unit (Phase 8 완주, 약 86%)

8.7 자기 점검 답변

Phase 8 학습의 종합은?

:
1. 6개 Unit 학습:

  • System.in / FileInputStream / byte[] 효율
  • FileOutputStream / Reader / Writer
  1. 두 가지 차원:

    • 바이트 vs 문자 (Stream vs Reader/Writer)
    • 입력 vs 출력
  2. 핵심 패턴:

    • try-with-resources
    • 인코딩 명시 (UTF-8)
    • BufferedXxx 결합
    • NIO.2 Files
  3. 흔한 함정 10가지:

    • 1바이트 read 한글
    • 마지막 n
    • for-each
    • 인코딩
    • 디렉토리
  4. 마스터 후:

    • 파일 I/O 자유자재
    • 다양한 소스 처리
    • 함정 회피

9️⃣ 면접 + 자기 점검 + Phase 8 졸업 시험

9.1 면접 단골 질문 매핑

Q핵심 답변
Writer 정의?char 단위, 인코딩 적용
Writer vs OutputStream?char vs byte, 인코딩
FileWriter 한계?Java 10 이하 인코딩 X
OutputStreamWriter 역할?Bridge (OutputStream → Writer)
BufferedWriter newLine?시스템 줄바꿈 (\n / \r\n)
BufferedWriter vs PrintWriter?표준 vs printf/예외 마스킹
한글 쓰기 권장?Files.newBufferedWriter + UTF_8
flush() 의 역할?버퍼 → OS 강제
Excel 한글 BOM?UTF-8 BOM 0xEF 0xBB 0xBF

9.2 Phase 8 졸업 시험 50문항

System.in (10 문항)

Q1. System.in 의 타입? → InputStream
Q2. System.in 의 특성? → Blocking, line-buffered
Q3. read() 반환 int 이유? → byte (-1) 와 충돌
Q4. System.in.close()? → 표준 입력 영구 종료, X
Q5. Scanner 의 nextInt + nextLine 함정? → \n 버퍼 남음
Q6. Console 의 특별 기능? → readPassword
Q7. ASCII 의 범위? → 0~127
Q8. UTF-8 의 한글 크기? → 3바이트
Q9. EUC-KR 한글? → 2바이트
Q10. Java 18+ 기본 인코딩? → UTF-8

FileInputStream (10 문항)

Q11. FileInputStream 정의? → InputStream 의 파일 전용
Q12. read() 반환? → 0~255, -1 EOF
Q13. -1 의 이유? → byte (0xFF) 와 충돌 회피
Q14. FileNotFoundException? → IOException 자식
Q15. try-with-resources? → AutoCloseable 자동 close
Q16. available() 의 의미? → 즉시 읽기 가능 (대략)
Q17. skip() 보장? → 요청한 만큼 X
Q18. mark/reset 지원? → FileInputStream X
Q19. getChannel()? → FileChannel 변환
Q20. NIO.2 대안? → Files.newInputStream

byte[] 효율 (10 문항)

Q21. read(byte[]) 반환? → 실제 읽은 수, -1 EOF
Q22. 1바이트 vs byte[8KB] 성능? → 약 2000배
Q23. 마지막 읽기 함정? → n < buf.length, 이전 데이터
Q24. for-each 위험? → buf.length 전체
Q25. 올바른 처리? → for (i=0; i<n; i++)
Q26. write(buf, 0, n)? → n 만큼만
Q27. 권장 버퍼? → 8KB
Q28. OS 페이지 크기? → 4KB
Q29. readNBytes (Java 9+)? → 정확한 크기
Q30. transferTo (Java 9+)? → 다른 OutputStream

FileOutputStream (10 문항)

Q31. FileOutputStream 정의? → OutputStream 의 파일 전용
Q32. append 모드? → new FOS(path, true)
Q33. 덮어쓰기 vs append? → truncate vs 끝에 추가
Q34. write(int) 의 한계? → 하위 8비트만
Q35. 한글 write(int)? → 깨짐
Q36. flush()의 역할? → 버퍼 → OS
Q37. FOS 의 flush? → 자체 버퍼링 X
Q38. BufferedOutputStream flush? → 8KB → OS
Q39. 바이트가 문자로? → ASCII/UTF-8 매핑
Q40. 부모 디렉토리 자동? → X, mkdirs 필요

Reader/Writer (10 문항)

Q41. Reader vs InputStream? → char vs byte
Q42. FileReader 한계? → Java 10 이하 인코딩 X
Q43. InputStreamReader 역할? → Bridge
Q44. BufferedReader readLine? → 한 줄, \n/\r/\r\n 제외
Q45. char 16비트? → UTF-16 한 단위
Q46. Supplementary 문자? → 이모지, Surrogate Pair
Q47. Writer vs OutputStream? → 인코딩 적용
Q48. FileWriter 한계? → 동일
Q49. BufferedWriter newLine? → 시스템 줄바꿈
Q50. 권장 패턴? → Files.newBufferedReader/Writer + UTF_8

9.3 채점

50 / 50 → Phase 8 마스터
45-49   → 거의 마스터
40-44   → 복습
< 40    → Unit 8.1 ~ 8.6 재학습

9.4 자기 점검 체크리스트

Writer

  • Writer 의 정의
  • OutputStream 과 차이
  • 5가지 write 메서드

FileWriter

  • 정의 (OutputStreamWriter 자식)
  • Java 10 이하 한계
  • Java 11+ 개선
  • append 모드

OutputStreamWriter

  • Bridge 패턴
  • 인코딩 명시 4가지
  • 다양한 OutputStream
  • BufferedWriter 결합

BufferedWriter

  • 8KB 버퍼
  • newLine()
  • flush
  • write(buf, 0, n)

PrintWriter

  • println, printf
  • 예외 마스킹
  • checkError
  • BufferedWriter 와 선택

한글 쓰기

  • Files.newBufferedWriter
  • Files.writeString
  • UTF-8 BOM (Excel)
  • 흔한 버그 5가지

9.5 추가 심화 질문

Q1: FileWriter 가 자식인 이유?

답:

  • FileWriter extends OutputStreamWriter
  • OutputStreamWriter 는 Bridge
  • FileWriter = OutputStreamWriter + FileOutputStream 의 편의 래퍼
  • 새 기능 추가 X, 단순화

Q2: BufferedWriter.newLine vs \n?

답:

bw.newLine();    // 시스템 의존 (\n 또는 \r\n)
bw.write("\n");  // 항상 \n

// Cross-platform 텍스트:
// - 대부분 \n 권장 (대부분 OS 인식)
// - newLine 은 플랫폼 의존

// Windows 네이티브:
// - newLine() 사용 (\r\n)

Q3: PrintWriter 의 autoFlush?

답:

PrintWriter pw1 = new PrintWriter(out);          // autoFlush=false
PrintWriter pw2 = new PrintWriter(out, true);    // autoFlush=true

// autoFlush=true:
// - println 후 flush
// - 문자 배열 쓴 후 flush
// - print 후엔 flush 안 함

// 디버깅에 유용 (println 후 즉시 출력)

Q4: Writer.write(int) 의 char 표현?

답:

  • char 는 16비트 (0~65535)
  • write(int) 는 하위 16비트 사용
  • 0x10000 이상은 사용 X
  • 이모지 등 Supplementary 문자는 2번 호출
// 이모지 😀 (U+1F600)
char[] chars = Character.toChars(0x1F600);
// chars = [0xD83D, 0xDE00] (Surrogate Pair)
w.write(chars);
// 또는
w.write(0xD83D);
w.write(0xDE00);

Q5: Files.writeString vs BufferedWriter?

답:

// Files.writeString — 작은 콘텐츠
Files.writeString(path, "Hello\n안녕\n", UTF_8);
// 내부적으로 BufferedWriter 사용
// 더 간단

// BufferedWriter — 큰 콘텐츠, 점진적
try (BufferedWriter w = Files.newBufferedWriter(path, UTF_8)) {
    for (int i = 0; i < 1000000; i++) {
        w.write(data[i]);
        w.newLine();
    }
}
// 점진적 쓰기
// flush 제어

🎯 핵심 요약 — 3줄 정리

1. Writer

  • char 단위, 인코딩 적용
  • write(int/char[]/String) 다양
  • OutputStream 보다 텍스트에 안전

2. FileWriter vs OutputStreamWriter

  • FileWriter: 파일 전용 (Java 11+ Charset)
  • OutputStreamWriter: Bridge (모든 OutputStream)
  • 가장 권장: Files.newBufferedWriter

3. 한글 쓰기

  • 항상 인코딩 명시 (UTF-8)
  • BufferedWriter 결합
  • Excel 호환: UTF-8 BOM

🏆 Phase 8 완주 — Stream 실전 마스터 달성

🚀 Phase 8 — Stream 실전
  ✅ Unit 8.1 System.in (한글 안 되는 이유)
  ✅ Unit 8.2 FileInputStream
  ✅ Unit 8.3 byte[] 배열로 효율적 읽기
  ✅ Unit 8.4 FileOutputStream
  ✅ Unit 8.5 한글 처리 (FileReader, InputStreamReader)
  ✅ Unit 8.6 FileWriter (한글 쓰기) ← 여기, Phase 8 완주

→ 파일 입출력 자유자재
→ 한글 안전 처리
→ 인코딩 명시 패턴
→ 실무 함정 회피
→ NIO.2 활용

📚 다음으로...

Phase 9 — I/O 강화

Phase 9 — I/O 강화 (5 Unit)

Unit 9.1 — try-with-resources
  - Java 7+ 자원 관리
  - AutoCloseable 인터페이스
  - Suppressed Exception

Unit 9.2 — BufferedInputStream / BufferedOutputStream
  - 버퍼링의 정밀
  - Decorator 패턴
  - 성능 효과

Unit 9.3 — DataInputStream / DataOutputStream
  - 기본 타입 입출력
  - 바이너리 형식
  - readInt/writeInt 등

Unit 9.4 — Serialization (직렬화)
  - Serializable 인터페이스
  - ObjectInputStream/OutputStream
  - transient 키워드

Unit 9.5 — serialVersionUID
  - 버전 관리
  - 직렬화 호환성
  - 보안 고려

3주차 누적 진행

✅ Phase 1 ~ 8 완주 (37 Unit)
⏭ Phase 9 — I/O 강화 (5 Unit)
⏭ Phase 10 — 함수형 프로그래밍 (4 Unit)

총: 37/43 Unit (Phase 8 완주, 약 86%)

🏆 Phase 8 완주 — Stream 실전 마스터 달성

profile
Software Developer

0개의 댓글