스트림(STREAM)이란?
데이터를 일정한 순서대로 흘려보내는 연속적인 데이터 흐름을 의미한다.
데이터를 한 번에 모두 처리하는 대신, 한 조각씩처리하도록 설계되어 있어 큰 파일이나 네트워크 데이터 처리 시 메모리 효율적이다.
파일, 네트워크 연결, 메모리 등 다양한 데이터 소스로 부터 데이터를 읽거나 쓸 때 사용한다.
스트림은 Java에만 국한된 것이 아니라 컴퓨터 과학 전반에서 데이터 처리 방식을 설명할 때 사용하는 일반적인 개념이다. (파이썬에서는 open()함수로, C/C++에서는 FILE* 포인터를 이용하여 스트림 개념을 사용한다.)
JAVA에서 사용되는 스트림
자바의 스트림은 InputStream/OutputStream으로 나누어져 입출력을 명확히 구분한다.
데이터를 읽는 스트림(입력 스트림): InputStream 계열 - 외부데이터(파일, 네트워크 등)를 프로그램으로 가져오는 흐름
데이터를 쓰는 스트림(출력 스트림): OutputStream 계열 - 프로그램 데이터를 외부로 보내는 흐름
InputStream / OutputStream
-> 스트림을 통해 데이터를 읽고/쓰는 기본 동작을 정의한 추상 클래스로, 자체적으로 사용되지 않고 이를 구현한 하위 클래스(FileInputStream, ByteArrayInputStream 등)를 통해 실제 작업을 수행한다.
스프링에서 사용하는 스트림 종류
1. FileInputStream / FileOutputStream
- 파일 내용을 읽거나 쓰는 데 사용
- 데이터를 한 바이트씩 처리 (대량 데이터 처리 시 성능 문제)
- 데이터를 바로 파일로 저장
- ex) 간단한 테스트 로그 기록
2. BufferedInputStream / BufferedOutputStream
- 데이터를 버퍼링하여 성능을 향상 (기본값 8KB)
- 파일이나 네트워크 데이터를 더 빠르게 처리할 때 사용
- ex) 대규모 엑셀 데이터 저장, HTTP 파일 업로드
3. ByteArrayInputStream / ByteArrayOutputStream
- 메모리에 있는 데이터(바이트 배열)을 스트림으로 처리
- 데이터를 메모리에 임시로 저장해 변환하거나 조작한 뒤 나중에 한 번에 처리할 때 사용
- ex) JSON 생성, 이미지 데이터 처리
4. InputStreamResource
- 스프링에서 파일이나 데이터 소스를 스트림으로 노출할 때 사용 (ex) REST API 응답으로 파일을 전송)
5. ServletInputStream
- HTTP 요청으로 본문 데이터를 스트림으로 읽을 때 사용
6. ObjectInputStream / ObjectOutputStream
- JAVA 객체를 직렬화/역직렬화하여 저장하거나 읽는 데 사용
7. ZipInputStream / ZipOutputStream
- ZIP 압축된 데이터를 읽고 쓸 때 사용
FileInputStream을 이용한 파일 읽기 예시
FileInputStream은 디스크에 이진수로 저장된 데이터(파일)을 읽어 메모리로 가져온다.
// 1. 파일과 스트림 연결
FileInputStream fis = new FileInputStream("example.txt");
// 2. 데이터 읽기
// read() 메서드를 호출해 파일의 내용을 한 바이트씩 읽는다. (파일 끝에 도달 시 -1 반환)
int data;
while ((data = fis.read()) != -1) {
Systsem.out.print((chart) data); // 바이트 데이터를 문자로 변환 후 출력
}
// 3. 스트림 닫기
fis.close();
InputStreamResource를 이용한 파일 다운로드 예시 (REST API)
@GetMapping("/download")
public ResponseEntity<InputStreamResource> download() throws FileNotFoundException {
File file = new File("example.txt");
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEnetity.ok()
.contentType(MediaType.APPLICATION_OCTET_STERAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"example.txt\"")
.body(resource);
}
스트림 사용 시 주의사항
.close()를 이용해 명시적으로 닫아주거나, 아래와 같은 try-with-resource 구문을 사용해 스트림이 자동으로 닫히도록 해야한다.
try (FileInputStream fis = new FileInputStream("example.txt")) {
int data;
while ((data = fis.read()) != -1) {
Systsem.out.print((chart) data);
}
} // try-with-resources 구문을 이용해 블록 끝에서 자동으로 스트림이 닫힘
스트림을 닫아주지 않으면 메모리 누수나 파일 잠금문제 (접근 오류), 시스템 성능이 저하되는 문제가 발생할 수 있다.
( 내 경우에 InputStream inputStream = file.getInputStream();을 사용해 파일을 SFTP 서버에 업로드 해주는 로직을 구현했는데, try-with-resource 구문을 사용하지 않고 스트림을 닫아주지도 않아 아래와 같은 오류가 발생했었다. )
java.io.UncheckedIOException: Cannot delete C:\Users\AppData\Local\Temp\tomcat.8086.11889956667311011581\work\Tomcat\localhost\ROOT\upload_fa1e9ec9_b16a_494b_98a3_7c3ddcad23e1_00000001.tmp
at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.delete(DiskFileItem.java:427) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationPart.delete(ApplicationPart.java:53) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
.....
진행 중인 프로젝트에서 DB에서 데이터를 읽어온 후 화면으로 파일 데이터를 리턴해준 후 저장하게 하는 내용을 구현해야 했다. 이 때 엑셀 파일 생성을 위해 사용한 라이브러리는 Apach POI 였다.
위에 나열한 여러 종류의 스트림 중 이와 같은 상황에 가장 적합한 유형은 무엇일까?
=> BufferedOutputStream
그 이유는 한꺼번에 많은 데이터를 메모리에 적재하지 않고 스트림으로 조금씩 처리하여 메모리 사용량을 줄일 수 있기 때문이다. 또한 Apache POI와 같은 엑셀 라이브러리와 호환성도 좋다.
여기서 다시 궁금한게 생겼다.
BufferedOutputStream이 그렇게 좋으면, 모든 경우에 그걸 사용하면 되는 게 아닐까?
GPT에게 물어본 답은 모든 경우에 적합한 것은 아니라고 한다.
그 이유는
- 작은 데이터 처리에는 과도함 : 데이터 양이 작거나 단순히 메모리에서 처리 하는 경우 버퍼링 오버헤드가 발생한다.
- 메모리 데이터 조작 불가: BufferedOutputStream은 메모리 데이터를 직접 조작할 수 없는 반면 ByteArrayOutputStream은 메모리 내 데이터를 조작하고 읽기/쓰기 작업을 동시에 처리할 수 있다.
그러면 다시..
내가 작성해야 하는 엑셀 파일은 저장되는 데이터가 클 수도 있고 작을 수도 있고 상황에 따라 다른데 그래도 BufferedOutputStream이 가장 좋은 것일까?
-> 소량의 데이터에 약간의 오버헤드가 생길 수도 있지만 대부분의 경우 무시할 수 있는 경우이며, 대량의 데이터에는 필수적이므로 이런 경우에도 BufferedOutputStream을 사용하는 것이 안전하고 효율적이라고 한다.
'열심히 직장인 > Hello World' 카테고리의 다른 글
방통대 컴퓨터 과학과 3학년 2학기 후기 (선형대수/자료구조/컴퓨터구조/UNIX시스템/멀티미디어시스템/대학영어) (1) | 2024.12.30 |
---|---|
Apache POI 라이브러리를 이용해 Java에서 엑셀 파일 만들기 (DB to File) (1) | 2024.12.30 |
서버와 컨테이너, 그리고 POD (CI/CD 파이프라인 흐름) (4) | 2024.11.13 |
브라우저 디버깅을 위한 크롬 개발자 도구 사용하기 - console.log에서 벗어나자! (2) | 2024.11.06 |
간단한 회원가입 서비스 만들기 (2) | 2024.10.23 |
[글또10기] 글또를 시작하며 작성해보는 다짐글 - 내 페이스대로! (10) | 2024.10.09 |
[방통대 컴퓨터과학과] 2024년 1학기- C프로그래밍 다 들은 기념으로 남기는 수업 후기 (+ 출석 수업 & 과제) (1) | 2024.03.17 |
방통대 편입 / 대학생 혜택 활용하기 1. IntelliJ IDEA Ultimate (JetBrains 교육용 무료 라이센스) (0) | 2024.02.20 |
신용장 매입과 추심 거래 흐름 정리 (0) | 2024.02.02 |
신용장(L/C) - 통지 / 매입 관련 용어 정리 (0) | 2024.02.01 |