1. 소개
솔직히 말해, 러시아어 텍스트 대신 Привет 같은 이상한 문자를 본 적이 있다면 이미 잘못된 인코딩의 피해자입니다. 이는 파일을 한 인코딩으로 저장해 놓고 다른 인코딩으로 읽을 때 발생합니다. 예를 들어 파일은 UTF-8로 저장되었는데 Windows-1251로 읽거나 그 반대인 경우입니다.
Java는 기본적으로 시스템 인코딩을 사용하며, 다음과 같이 확인할 수 있습니다:
System.out.println(System.getProperty("file.encoding"));
어떤 컴퓨터에서는 UTF-8, 다른 곳에서는 Windows-1251, 또 어떤 곳에서는 ISO-8859-1일 수 있습니다. 따라서 인코딩은 항상 명시적으로 지정하는 것이 가장 좋습니다. 이는 다국어 데이터를 다루거나, 파일이 여러 컴퓨터에서 사용되거나 다른 프로그램에서 열릴 수 있거나, 코드가 당신의 컴퓨터뿐 아니라 어떤 환경에서도 동일하게 동작하길 원할 때 특히 중요합니다.
Charset 클래스: 인코딩 세계의 친구
Java에서 인코딩 작업에는 java.nio.charset.Charset 클래스를 사용합니다. 이 클래스는 인코딩을 이름(예: "UTF-8")으로 지정할 수도 있고 표준 상수(StandardCharsets.UTF_8)를 사용할 수도 있게 해 줍니다.
표준 인코딩 예:
| 인코딩 | Java 상수 |
|---|---|
| UTF-8 | |
| UTF-16 | |
| ISO-8859-1 | |
| Windows-1251 | |
상수를 사용하는 편이 더 좋습니다. 이름을 잘못 쓸 가능성이 줄어들고 UnsupportedCharsetException이 발생하지 않습니다.
2. 인코딩을 지정하여 파일 읽기
예전 방식:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("example.txt"),
StandardCharsets.UTF_8 // <-- 인코딩을 명시적으로 지정
)
);
현대적인 방식:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
BufferedReader reader = Files.newBufferedReader(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- 인코딩을 명시적으로 지정
);
둘째 방법 사용을 권장합니다 — 더 짧고 더 안전하며 try-with-resources와 잘 어울립니다.
예: 파일에서 한 줄 읽기
try (BufferedReader reader = Files.newBufferedReader(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
String line = reader.readLine();
System.out.println("읽었습니다: " + line);
}
왜 중요한가: 파일이 UTF-8로 저장되어 있는데 Windows-1251로 읽으면 키릴 문자 등이 깨집니다. 올바른 인코딩을 지정하면 어떤 OS에서도 텍스트를 제대로 읽을 수 있습니다.
3. 인코딩을 지정하여 파일 쓰기
예전 방식:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("example.txt"),
StandardCharsets.UTF_8 // <-- 인코딩을 명시적으로 지정
)
);
현대적인 방식:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;
BufferedWriter writer = Files.newBufferedWriter(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- 인코딩을 명시적으로 지정
);
예: 파일에 한 줄 쓰기
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
writer.write("안녕, 세상!");
}
결과: 파일은 UTF-8로 저장되며, UTF-8을 지원하는 어떤 편집기에서도 올바르게 열 수 있습니다.
4. 유용한 팁과 확인 사항
지원되는 인코딩 확인 방법
import java.nio.charset.Charset;
public class ListCharsets {
public static void main(String[] args) {
System.out.println("사용 가능한 인코딩:");
Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
}
}
팁: 아주 특수한 인코딩(예: 고대 한자나 이모지에 특화된 인코딩 등)을 사용한다면, 해당 인코딩이 JVM에서 지원되는지 확인하세요.
try-with-resources 사용: 스트림 닫는 것 잊지 않기
파일을 다룰 때는 리소스 누수를 막기 위해 스트림을 반드시 닫아야 합니다. 최신 Java 코드는 try-with-resources 구문을 사용합니다:
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
// 파일을 처리합니다
}
예외가 발생하더라도 스트림은 자동으로 닫힙니다.
권장 사항
- 항상 인코딩을 명시적으로 지정하세요. 기본값으로도 동작한다는 확신이 있더라도 읽기/쓰기 시 인코딩을 지정하는 편이 안전합니다.
- 새로운 파일에는 UTF-8을 사용하세요 — 사실상의 표준이며, 웹, JSON, XML 작업이나 파일이 어디서나 읽히길 원할 때 특히 좋습니다.
- 기존 파일(예: 1C 내보내기, 오래된 데이터베이스, Windows에서 만든 CSV)에는 해당 파일이 저장된 인코딩을 사용하세요(예: Windows-1251, ISO-8859-1).
- 인코딩을 명시할 수 없는 구식 클래스는 사용하지 마세요: FileReader/FileWriter. 대신 InputStreamReader/OutputStreamWriter에 인코딩을 명시하거나 Files의 메서드를 사용하세요.
- 큰 파일에는 버퍼링(BufferedReader/BufferedWriter)을 사용하여 메모리 사용을 줄이세요.
5. 인코딩 작업 시 흔한 오류
오류 №1: 파일 읽기/쓰기 시 인코딩을 지정하지 않음.
인코딩을 지정하지 않으면 Java는 시스템 기본값("file.encoding")을 사용합니다. 내 컴퓨터에서는 잘 보이지만 동료의 컴퓨터에서는 “깨진 문자”가 나타날 수 있습니다.
오류 №2: 읽기와 쓰기에 사용하는 인코딩이 일치하지 않음.
파일을 한 인코딩으로 저장해 놓고 다른 인코딩으로 읽는 경우입니다. 예를 들어 파일은 UTF-8로 저장했는데 Windows-1251로 읽으면 키릴 문자가 깨집니다.
오류 №3: 구식 클래스 FileReader/FileWriter 사용.
이 클래스들은 인코딩을 명시적으로 지정할 수 없으므로 사용을 권장하지 않습니다. 대신 InputStreamReader/OutputStreamWriter에 인코딩을 지정하거나 Files의 메서드를 사용하세요.
오류 №4: 인코딩 이름 오기.
예를 들어 "utf8" 대신 "UTF-8", "win1251" 대신 "Windows-1251"처럼 잘못 쓰는 경우입니다. Java는 UnsupportedCharsetException을 던집니다.
오류 №5: 스트림을 닫지 않음 — 파일이 제대로 기록되지 않음.
try-with-resources를 사용하지 않거나 스트림을 명시적으로 닫지 않으면 일부 데이터가 디스크에 쓰이지 않을 수 있습니다.
GO TO FULL VERSION