brunch

You can make anything
by writing

C.S.Lewis

JAVA I.O. - 보조스트림

* 이 내용은 철저히 초심자를 위해 알기 쉽게 설명하는 것을 목적으로 하고 있습니다.

* 더 정확하고 자세한 개념은 다른 고수님들의 글들을 참고하시길 바랍니다.

* 그리고 이 글에서는 코드 최소한으로 다루고 있습니다.


우리는 앞서 자바 시스템 내외부로 데이터를 받아들이거나 내보낼 때 데이터의 파이프인 스트림을 사용한다고 하였습니다. 그리고 그 용도에 맞는 스트림을 사용하는 것이 좋다고도 했었죠?

이번엔 그 스트림에 추가 기능을 부여해 주는 보조스트림에 대해서 알아보겠습니다.


보조스트림 개념



보조 스트림은 주 스트림(우리가 앞에서 배운 input, output, reader, writer)과 연결되어 추가 기능을 제공해 주는 스트림입니다.

그래서 사용하고 싶은 추가 기능과 관련된 보조 스트림을 추가하면 됩니다.

예를 들어 사람은 빠르게 달릴 수 없습니다. 하지만 차량에 탑승하면 빠르게 이동이 가능합니다. 좋은 자동차의 경우는 왠지 모를 존중도 받게 되고요.

이처럼 주스트림은 보조스트림에 탑승하게 되면 본인이 겐 원래 없던 기능들을 얻게 됩니다.


보조스트림 사용법

‘주스트림은 보조스트림에 탑승’ 한다는 개념입니다.

우리가 자동차를 타거나 비행기를 타거나, 어찌 되었던 없는 기능을 활용하기 위해서는 탑승을 하지요?

그래서 위 코드와 같이 우리가 준비했던 스트림, 주 스트림을 보조 스트림에 탑승을 시키는 것입니다.


우리가 애니메이션에서 합체하는 로봇들을 본 적이 있을 것입니다.

그때 보면 사람이 적들과 맞서기 위해 전투기 등을 타고 싸우다가, 로봇이 필요해질 때 비행기가 로봇에 태워지게 되면서 로봇을 움직이는 상황이 나오죠? 이처럼 내가 추가로 필요한 기능이 있다면, 주스트림을 태운 보조스트림이 또 다른 보조스트림에 탑승할 수 있습니다.


코드에서도 이 부분이 잘 나타나 있죠?

주 스트림이 보조 스트림에 탑승하고, 이 보조 스트림이 또 다른 보조스트림에 탑승하고 있습니다.

그런데 이 부분에서 의아한 점이 하나 있습니다. 다 쓰고 난 스트림은 어디까지 닫아줘야 하는가?


우리가 사용한 스트림은 주 스트림 한 개와, 보조 스트림 두 개입니다.

원래는 이 것들을 모두 반납해 줘야겠죠? 하지만 잘 생각해 보면 주 스트림과 보조 스트림은 최종 보조 스트림에 탑승된 상태 이므로 결국 마지막에 탑승된 보조 스트림만 반납하면 됩니다.


이렇게 보조 스트림의 사용방법에 대해서 알아보았는데요, 이런 보조 스트림을 잘 사용하려면 어떤 것들이 있는지 알아봐야겠지요?

보조 스트림의 종류는 굉장히 다양하기 때문에 자주 사용하는 몇 가지만 알아보도록 하겠습니다.

우선 주 스트림과 마찬가지로 작명법에는 들오느냐 나가느냐, 문자 냐 바이너리 냐 에 따른 규칙이 있습니다.


Buffer

버퍼링이라는 단어를 아십니까?

요즘은 그나마 덜 하지만 예전에는 영상을 보려고 하면 버퍼링이 꽤나 심하게 걸렸었죠.

그래서 버퍼링 이란 ‘버벅 거린다’의 뜻으로 쓰이곤 하는데요. 그건 오해입니다.

사실 버퍼링은 엄청 유용한 친구입니다.


예를 들어 우리가 버킷에 담긴 많은 양의 물을 다른 곳으로 이동시킨다고 해 보겠습니다.

이때 티스푼으로 한 번씩 떠서 옮기는 방식과, 컵으로 물을 담아서 옮기는 방식이 있다면 어떤 것을 선택하시겠습니까? 생각할 필요도 없겠죠?


버퍼는 데이터를 버퍼 버퍼메모리라는 곳에 일괄적으로 담아서 목적지에 이동을 시켜줍니다.

그렇기 때문에 데이터를 하나하나 옮기는 것보다 훨씬 속도가 우월합니다.


그럼 우리에게 부정적인 인식을 심어주는 버퍼링이란 현상은 무엇일까요?

바로 버퍼 메모리에 아직 데이터가 다 들어오기 않아서 들어올 때까지 기다리는 현상을 버퍼링이라고 부릅니다. 즉 버퍼에 담는 중이라는 뜻이죠.

그래서 버퍼링 증상 이후에는 동영상이 재생이 되곤 합니다.

하지만 버퍼 안으로 들어오는 데이터의 속도가 지속적으로 느리다면 버퍼링은 계속 일어나는 것이고, 이후 네트워크 속도가 개선되면 버퍼에 데이터를 빠르게 채워주면서 버퍼링이 생기지 않습니다.


그래서 유튜브 등의 스트리밍 서비스를 보면 진행 중인 빨간 바와 회색바가 보일 것입니다. 이때 회색바가 버퍼를 통해 데이터를 쌓아놓는 정도를 표기한 것입니다.


이 버퍼를 들여올 때 쓰느냐, 그리고 문자를 주고받을 때 쓰느냐의 여부에 따라 다른 사용하는 스트림이 달라집니다.


Data Input / Data Output

이렇게 스트림을 이용해 데이터를 보낼 때는 바이너리와 문자 밖에 사용할 수 없습니다.

그렇기 때문에 순수 정수나, 실수 등 자바에서 사용하는 데이터타입을 온전하게 보낼 수가 없습니다.

그래서 순수 자바의 데이터 타입을 보내기 위한 DataInputStream, DataOutputStream이라는 보조 스트림을 제공합니다.



그리고 각각 특정한 데이터 타입을 쓰거나 읽기 위한 메서드를 제공합니다. 그리고 메서드의 이름은 누가 봐도 이해할 수 있게 직관적으로 만들어졌습니다.


만약 자바의 특정한 데이터 타입을 빠른 속도로 파일로 저장하고 싶다면…

위와 같이 스트림을 사용하면 됩니다. 위에서 보조스트림 사용 때 보았던 내용과 비슷하죠?


Object Input / Object Output

Data 스트림을 통해서 자바의 순수 데이터 타입을 보내고 받을 수 있게 되었지만 아쉬움은 여전히 남습니다.

그 이유는 우리가 앞에서 배운 컬렉션 프레임워크를 포함하여 자바의 데이터타입에 대부분은 바로 클래스 이기 때문입니다. 그리하여 클래스도 보내고 받을 수 있는 Object 스트림이 제공되게 됩니다.


사용하는 메서드도 Data 스트림과 동일합니다.

다만 클래스등을 다룰 수 있는 readObject와 writeObject 가 추가 되었습니다.

여기서 Object는 java.lnag 패키지에서 제공하는 클래스들의 최고 조상입니다.

그렇기 때문에 다형성에 의해 Object 형태에는 자바의 모든 클래스, 모든 데이터 타입을 수용할 수 있습니다.


직렬화(serialize)와 역직렬화(un serialize)

그런데 잘 생각해 보면 뭐든지 담을 수 있고, 어떤 형태가 될지도 모르는 이 거대한 오브젝트를 스트림이라는 얇은 관을 통해 이동하기는 쉽지 않습니다.


그렇기에 거대한 오브젝트를 스트림이라는 관 안에 흘려보낼 수 있도록 잘게 잘라주는 직렬화(serialize), 그리고 이렇게 잘린 조각들을 다시 오브젝트 형태로 맞추는 역직렬화(un serialize)를 사용해 오브젝트를 이동시켜야 합니다.


하지만 불규칙적으로 쪼개진 조각들을 하나로 다시 맞추는 것은 쉽지 않습니다. 누군가 잘게 찢어버린 종이를 맞추는 게 어려운 것처럼요.

그럼 어떻게 하는 게 맞추기 쉬울까요? 당연히 어떤 모양이 정해져 있는 퍼즐이 훨씬 맞추기가 쉽겠죠?

퍼즐을 맞출 때 퍼즐의 전체적은 그림과 가이드가 있으면 더욱 맞추기가 편리해집니다.


즉 쪼개고 합칠 때 규격이라는 것이 있으면 훨씬 수월하다는 것이죠!

그리고 자바에서 규격은 추상 클래스나 인터페이스를 통해 지정할 수 있다고 했습니다.

그래서 Object 스트림을 통해 전송하고 싶은 클래스는 반드시 Serializable이라는 인터페이스를 구현받아야만 합니다.

그래야 해당 클래스를 인터페이스 대로 쪼개고 합칠 수 있을 테니까요.


이상으로 스트림을 이용해 데이터를 전송하는 JAVA I.O. 에 대해서 알아보았습니다.

시대가 발전하면서 스트림을 사용하지 않는 Channel이라는 방식을 사용하는 새로운 I.O. 방식인 JAVA N.I.O. 도 생겼습니다. 하지만 아직도 JAVA I.O. 는 많이 사용되고 있고 네트워크 쪽에서도 사용되고 있기에 중요도 있는 기술입니다.

매거진의 이전글 JAVA I.O. - 파일 쓰기
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari