2020.03.30-개발일지
System stream 객체를 close() 했을 때 발생하는 문제점
리팩토링하면서 가장 먼저 눈에 띄는것은 Scanner
와 System.out.println
이었습니다. 주로 Web MVC로 개발하다 보니 콘솔 환경에서 입.출력을 구현할 일도 없거니와, 만약 구현한다 해도 Logger
를 이용했기 때문입니다. 더욱이 알고리즘등을 공부하면서 일반적인 입출력보다 속도나 성능면에서 문제가 있는 것을 알고 있는데, 개선하지 않는 것은 안 좋은 방향이라 생각했습니다.
문제를 해결하기 위해 유틸 클래스를 작성 후 입력과 출력을 담당하는 메서드를 작성했습니다. 이때 BufferdReader
와BufferdWriter
를 사용하였습니다. I/O Stream은 항상 close를 해야 한다고 알고 있기에 자연스럽게 메서드 마지막 부분에 close
를 했는데 여기서 문제가 발생했습니다. 첫번째 호출에서는 문제가 없으나 두번째부터 다시 객체를 생성하려 하면 java.io.IOException: Stream closed
에러가 발생 했습니다.
알게된 내용
1 .System.out
System 클래스의 out
인스턴스 설명은 아래와 같이 주석으로 잘 되어 있습니다.
/**
* The "standard" output stream. This stream is already
* open and ready to accept output data. Typically this stream
* corresponds to display output or another output destination
* specified by the host environment or user.
* <p>
* For simple stand-alone Java applications, a typical way to write
* a line of output data is:
* <blockquote><pre>
* System.out.println(data)
* </pre></blockquote>
* <p>
* See the <code>println</code> methods in class <code>PrintStream</code>.
*/
public final static PrintStream out = null;
주석 내용은 ‘스트림은 프로그램 실행 시에 이미 열려있으며 데이터를 출력할 준비가 되어 있음’ 이라 되어 있습니다. 당연하게도? close 를 하면 Console
관련 스트림은 연결이 해제되고 재사용할 수 없게 됩니다. (out 역시 마찬가지 입니다.)
2. Java 에서 사용자 입력을 받는 3가지 방법
- BufferdReader
- Java 1.1 부터 사용
- 명시적인 에러처리 필요
- 속도가 매우빠름
- 입력에 대한 validation을 직접 구현해야 함
- Scanner
- Java 1.5 부터 생김
- 명시적인 예외처릴 할 필요 없음
- line, token 별로 read 하는 등의 다양한 기능을 제공
- 속도가 느림
- Console
- Java 1.6 부터 사용
- 명시적인 예외처리가 필요 없음
- Scanner와 BufferdReader의 장점을 모두 가져옴 (암호 읽기 *** 기능도 제공함)
- 에디터에서 안되는 경우가 있음. NullPointerException 발생. Console 객체와 에디터의 콘솔이 매칭이 안되서 발생한 문제
상황에 맞게 쓰자
다양한 상황속에서 용도에 따라 분리해서 사용하는 것이 좋습니다.
- 빠른 속도가 필요한 경우 -> BufferdReader
- 암호, 개인정보등 추가적인 처리가 필요할 경우 -> Console
- 형타입, 정규식 표현등을 그대로 사용하고 싶을 때 -> Scanner
- Thread-safe 하게 사용해야 하는 경우 -> Console 또는 BufferdReader
이번에는 아래와 같이 작성 후, 프로그램 종료직전 close()
호출 하기로 결정했습니다.
public class ConsoleUtil {
private static final BufferedReader BUFFERED_READER = new BufferedReader(new InputStreamReader(System.in));
private static final BufferedWriter BUFFERED_WRITER = new BufferedWriter(new OutputStreamWriter(System.out));
public static final String HORIZONTAL_RULE = "=================================";
public static String readString(String msg) throws IOException {
printLn(msg);
String input = BUFFERED_READER.readLine();
return input;
}
public static int readInt(String msg) throws IOException {
printLn(msg);
int parseInt = 0;
boolean success = false;
while (success != true) {
String input = BUFFERED_READER.readLine();
try {
parseInt = Integer.parseInt(input);
success = true;
} catch (NumberFormatException e) {
printLn("숫자만 입력해주세요!");
}
}
return parseInt;
}
public static void printLn(String msg) throws IOException {
BUFFERED_WRITER.write(msg);
BUFFERED_WRITER.newLine();
BUFFERED_WRITER.flush();
}
public static void close() throws IOException {
BUFFERED_READER.close();
BUFFERED_WRITER.close();
}
}
Stream에 대한 이해가 많이 부족한 것 같다고 느꼈습니다. 해당 내용을 정리하는 시간을 갖는다면 좋을 것 같습니다.
댓글남기기