IT·테크/개발

Java 입출력 — 바이트·문자 스트림과 보조 스트림으로 기능 얹기

레루루 2026. 7. 28. 21:45
반응형

이 글은 2022년 5월 기준 기록입니다.

 

Java의 데이터 입출력은 java.io 패키지가 담당합니다. 클래스 이름이 많아 보이지만, 두 개의 축만 잡으면 대부분 제자리를 찾습니다. 바이트 단위인가 문자 단위인가, 그리고 주 스트림인가 보조 스트림인가입니다.

java.io는 바이트 계열과 문자 계열로 나뉩니다

패키지의 주요 클래스를 성격별로 묶으면 이렇습니다.

java.io 패키지의 주요 클래스 설명
File 파일 시스템의 파일의 정보를 얻기 위한 클래스
Console 콘솔로부터 문자를 입출력하기 위한 클래스
InputStream / OutputStream 바이트 단위 입출력을 위한 최상위 입출력 스트림 클래스
FileInputStream/FileOutputStream, DataInputStream/DataOutputStream, ObjectInputStream/ObjectOutputStream, PrintStream, BufferedInputStream/BufferedOutputStream 바이트 단위 입출력을 위한 하위 스트림 클래스
Reader / Writer 문자 단위 입출력을 위한 최상위 입출력 스트림 클래스
FileReader/FileWriter, InputStreamReader/OutputStreamWriter, PrintWriter, BufferedReader/BufferedWriter 문자 단위 입출력을 위한 하위 스트림 클래스

 

이름 규칙이 그대로 힌트입니다. Stream으로 끝나면 바이트 단위이고, Reader·Writer로 끝나면 문자 단위입니다.

바이트 기반 입출력 스트림

바이트 단위 입출력의 최상위에는 InputStreamOutputStream이 있고, 그 아래로 용도별 하위 클래스가 붙습니다. InputStream에는 FileInputStream·BufferedInputStream·DataInputStream이, OutputStream에는 FileOutputStream·PrintStream·BufferedOutputStream·DataOutputStream이 상속받습니다.

문자열 기반 입출력 스트림

문자 단위 입출력의 최상위는 ReaderWriter입니다. Reader에는 FileReader·BufferedReader·InputStreamReader가, Writer에는 FileWriter·BufferedWriter·PrintWriter·OutputStreamWriter가 상속받습니다.

 

양쪽 계층을 나란히 놓고 보면 이름이 대칭을 이룹니다. File-은 파일을 직접 다루는 주 스트림이고, Buffered-·Data-·Print- 같은 접두어가 붙은 것들은 다음에 볼 보조 스트림입니다.

보조 스트림은 주 스트림에 기능을 덧대는 장치입니다

보조 스트림은 다른 스트림과 연결되어 성능 향상, 기본 데이터 타입 입출력, 객체 입출력 등의 기능을 제공합니다. 혼자서는 아무것도 읽고 쓰지 못하고, 반드시 다른 스트림에 얹혀서 동작합니다.

 

구조를 보면 이해가 빠릅니다. 입력 쪽에서는 주 입력 스트림 뒤에 보조 스트림 1, 보조 스트림 2를 차례로 연결한 뒤 프로그램이 데이터를 받고, 출력 쪽에서는 프로그램이 내보낸 데이터가 보조 스트림 2, 보조 스트림 1을 거쳐 주 출력 스트림으로 빠져나갑니다. 보조 스트림은 여러 개를 겹쳐 연결할 수 있으며, 입력에서 얹은 순서와 출력에서 벗기는 순서가 서로 대칭입니다.

 

이제 각 보조 스트림이 이 흐름의 어디에서 무엇을 하는지 하나씩 봅니다.

문자 변환 보조 스트림은 바이트를 문자로 바꿔 줍니다

소스 스트림이 바이트 기반 스트림이지만 데이터가 문자일 경우에 사용합니다. ReaderWriter는 문자 단위로 입출력하므로 바이트 기반 스트림보다 편리하고, 문자셋의 종류를 지정할 수 있기 때문에 다양한 문자 입출력이 가능합니다.

 

입력 쪽에서는 바이트가 InputStream으로 들어오고, InputStreamReader가 그 앞을 감싸 Reader로 바꿔 놓습니다. 프로그램은 문자를 받습니다. 출력 쪽은 정반대입니다. 프로그램이 문자를 Writer로 내보내면 OutputStreamWriter가 이를 OutputStream으로 연결해 바이트로 흘려보냅니다.

성능 향상 보조 스트림은 버퍼에 모았다 한 번에 옮깁니다

 

BufferedInputStreamBufferedReader는 입력 소스에서 미리 버퍼에 데이터를 채워 둡니다. 프로그램은 그 버퍼에서 데이터를 가져가므로 고속으로 읽을 수 있습니다.

BufferedOutputStreamBufferedWriter는 반대 방향입니다. 프로그램이 데이터를 버퍼로 고속 전송해 쌓아 두었다가, 한 번에 버퍼 내용 모두를 목적지로 전송합니다. 매번 목적지까지 다녀오는 왕복을 줄이는 것이 성능 향상의 정체입니다.

기본 타입 입출력 보조 스트림은 순서를 맞춰 써야 합니다

 

DataInputStreamDataOutputStreamint·double 같은 기본 데이터 타입을 그대로 주고받게 해 줍니다. 바이트가 InputStream으로 들어와 DataInputStream을 거치면 프로그램에서 기본 데이터 타입으로 받고, 내보낼 때는 DataOutputStream을 거쳐 OutputStream으로 나갑니다.

여기에는 지켜야 할 규칙이 하나 있습니다. 입출력 순서를 맞추어 사용해야 합니다. 쓴 순서대로 읽지 않으면 값이 어긋납니다.

프린터 보조 스트림은 print 계열 메소드를 제공합니다

 

PrintStreamPrintWriter는 프로그램의 기본 타입 또는 문자열을 받아 print()·println()·printf() 메소드로 내보냅니다. 이 데이터는 OutputStream 또는 Writer를 거쳐 문자 코드로 출력됩니다.

객체 입출력 보조 스트림은 객체를 통째로 흘려보냅니다

 

ObjectOutputStream은 프로그램 안의 객체를 직렬화OutputStream으로 내보내고, ObjectInputStreamInputStream으로 들어온 바이트를 역직렬화해 객체로 되돌립니다.

 

직렬화에는 조건과 예외가 붙습니다.

  • Serializable 인터페이스를 구현한 클래스만 직렬화가 가능합니다.
  • transient로 지정된 필드는 제외됩니다.
  • serialVersionUID 필드를 통해 직렬화·역직렬화 과정에서 같은 클래스라는 것을 표시합니다.

마지막 항목은 그냥 넘기기 쉬운데 실제로 사고가 나는 지점입니다. serialVersionUID를 표시하지 않으면 직렬화·역직렬화 과정에서 원본 클래스에 수정이 있었을 때 다른 클래스로 인식해서 오류가 발생합니다. 객체를 파일이나 네트워크로 주고받는 코드라면 이 필드를 명시해 두는 편이 안전합니다.

반응형