입출력이 왜 3개나 있는가?
Java는 기술이 발전하면서 성능 개선, 사용 편의성에 따라 새로운 IO가 등장했다.
그렇다면, 각각의 IO는 어떻게 발전했는지, 그리고 어떻게 사용하면 좋을지에 대해 알아보자.
JAVA IO
초기의 Java IO의 경우 Java가 처음 만들어진 시기인 1996년 스트림 기반으로 데이터를 읽고 쓰는 방식으로 등장했다.
때문에 단순한 파일 읽기/쓰기, 소규모 입출력에는 충분하지만 1byte 또는 1문자씩 읽고 쓰기 때문에 느리다는 단점을 가졌습니다.
위 공식 사이트에가면 IO가 지원하는 인터페이스와 클래스를 볼 수 있다.
보면 InputStreamReader를 비롯한 Stream을 기반으로한 출력이라는것을 확인할 수 있다.
read()
, write()
를 수행했을 때 읽고 쓸 데이터가 있을 때까지 블로킹이 되는데 이를 Blocking mode라 한다.
IO 스레드가 블로킹되면 다른 일을 수행할 수 없고, blocking을 빠져나오기 위해 인터럽트 할 수 없기 때문에 .close()
로 스트림을 당아줘야 한다.
JAVA NIO
IO는 성능이 느리고, 비동기 처리를 지원하지 않는 단점을 가졌다.
그래서 이를 개선하기 위해 Java 1.4부터 등장한게 바로 NIO(New IO)다.
NIO는 크게 3가지로 구성되어 있다.
Buffer
Channel
Selector
NIO는 채널과 버퍼를 활용해 블로킹을 최소화한 비동기 입출력을 지원한다.
즉, Buffer
를 사용해 데이터를 한번에 모아 읽고 쓰기가 가능하고, Channel
을 사용해 버퍼를 통해 입출력하는것을 객체로 처리할 수 있게 되었다.
또한, Selector
를 사용해 여러 채널을 동시에 관리가 가능해 비동기 입출력도 사용이 가능해졌다.
Buffer란?
버퍼(Buffer)는 데이터를 임시로 저장하는 메모리 공간이다.
Java IO에서는 데이터를 1바이트(혹은 1문자)씩 읽고 썼지만, NIO에서는 데이터를 모아서 한꺼번에 처리하기 위해 버퍼를 사용한다.
위 그림을 보면 이해가 갈 것이다.
버퍼는 고정값이 아닌 가변적인 값을 받게 된다.
즉, 4개의 값을 받으면 4개의 공간이 사용되며, 10개의 값을 받으면 10개의 공간을 사용해 효율적이게 된다.
또한, 버퍼는 입력받은 값은 버퍼에 저장해뒀다가 버퍼가 가득차거나 개행 문자가 나타나면, 버퍼에 저장해뒀던 내용을 한 번에 전송하게 된다.
이렇게 한 번에 전송하는 방법을 통해 IO의 처리방식보다 속도가 월등하게 빨라지게 된다.
주의할점은 자바에서 버퍼는 띄어쓰기를 경계로 인식하지 않기 때문에 데이터를 가공해주어야 한다는것과 기본 입력 타입은 String이기 때문에 형변환도 해줘야 할 때가 종종 있다는것이다.
Channel이란?
데이터가 움직일 수 있는 양방향의 통로가 바로 Channel이다.
Java NIO에서는 데이터를 직접 다루는 대신, Channel과 Buffer를 함께 사용해 데이터를 주고받는다.
기존 IO에서는 InputStream과 OutputStream처럼 방향이 구분되지만, Channel은 하나로 입력과 출력을 모두 처리할 수 있는 양방향 입출력이 가능하다..
Channel은 데이터를 직접 다루지 않고, 항상 버퍼를 통해 데이터를 읽고 쓰기 때문에 고성능 입출력이 가능하다.
Selector
와 함께 사용하면, Channel을 비동기(Non-Blocking) 방식으로 다룰 수 있어, 수천 개의 연결도 적은 리소스로 처리할 수 있다.
주의할점은 Channel은 항상 Buffer와 함께 사용해야 하기 때문에, flip()
, clear()
, rewidn()
같은 버퍼 상태 제어 메서드를 정확하게 이해하고 사용해야 한다.
Selector란?
앞서 JAVA IO에서는 블로킹으로 IO에서 클라이언트마다 스레드를 하나씩 할당해줬다.
하지만, 이는 클라이언트가 많아질수록 서버에 리소스 낭비를 초래하게 된다.
때문에, 스레드의 개수를 줄이개 되는데 이는 하나의 스레드가 여러개의 채널에 논블로킹 상태로 작업을 해야하며, 스레드는 주기적으로 채널에 데이터가 있는지 확인해야 한다.
여기서, 데이터가 들어올 때까지 주기적으로 채널 확인을 해야하는데 이것을 폴링이라 한다.
폴링과정에서 스레드가 Read, Write 가능 여부를 계속 확인하기 때문에 CPU 낭비가 심하다는 단점이 발생한다.
이를 해결하기 위해 셀렉터가 등장했다.
그림을 보면 알 수 있듯이, 스레드는 셀렉터에 등록된 채널중 가용한 채널이 있는지 알 수 있다.
이는 셀렉터에 등록된 채널 중 하나라도 사용 가능한 상태가 되면 바로 알 수 있게 해주는데 이를 멀티플렉싱이라고 한다.
JAVA NIO 2
Java NIO.2는 Java 7에서 도입된 API로, java.nio.file 패키지를 중심으로 구성돼 있다.
기존의 File 클래스의 한계를 보완하고, 파일 및 디렉토리 조작을 더 편리하고 강력하게 만들어줬다.
NIO2는 NIO와 비교했을때 새롭개 추가된 컴포넌트와 개념들이 있다.
Path
,Paths
,FileSystem
– 새로운 파일 경로 관리 방식- 예전에는 File file = new File("path")처럼 문자열 기반 경로만 있었지만, NIO.2에서는 파일 경로를 추상화한 객체인 Path를 통해 파일 시스템을 더욱 유연하게 제어가 가능해졌다.
Files
– 파일 조작을 위한 고수준 유틸리티 클래스- Java NIO에서는 파일 복사, 삭제, 이동 등을 하기 위해 복잡한 코드와, 직접 버퍼 처리를 해야하는 문제점이 있었다.
- NIO.2에서는 Files 클래스를 통해 간결하게 수행할 수 있다.
- WatchService – 디렉토리/파일 변경 감지 기능 (OS 이벤트 기반)
- NIO나 IO에서는 없던 완전히 새로운 기능이다.
- 디렉토리의 변경을 실시간으로 감지할 수 있다.
- 파일 생성, 삭제, 수정 등의 이벤트를 감시할 수 있다.
- 심볼릭 링크 / 하드 링크 지원
- 기존 IO/NIO에서는 파일 자체에 대한 링크 기능이 없었다.
- NIO.2에서는 OS 수준의 링크 파일 생성/삭제/판별 기능을 제공한다.
- 파일 속성/메타데이터 접근 (Attribute View API)
- NIO.2는 파일의 속성 정보에 접근할 수 있는 구조적인 방법을 제공한다.
- OS마다 다른 속성까지 다룰 수 있다. (Windows, POSIX 등)
'백엔드 > 자바' 카테고리의 다른 글
[JAVA] Java NIO를 활용한 파일 목록 가져오기 (0) | 2025.03.21 |
---|---|
[JAVA] Java Files API를 활용한 텍스트 및 객체 입출력 (0) | 2025.03.21 |
[JAVA] 스트림 파이프라인(Stream pipeline) (0) | 2025.03.20 |
[JAVA] Stream API (0) | 2025.03.19 |
[JAVA] java.util 패키지 (0) | 2025.03.19 |