핑구

11. 입출력(IO) 본문

JAVA 웹 개발/1. JAVA

11. 입출력(IO)

코딩 펭귄 2022. 5. 13. 14:22
📅 2021.08.30 ~ 2021.08.31

입출력(IO)

  • Input과 Output의 약자로 컴퓨터 내부 또는 외부 장치와 프로그램 간에 데이터를 주고받는 것을 말한다.
  • 장치와 입출력을 위해서는 장치에 직접 접근하여야 하는데, 다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림(Stream)을 이용한다.

 

스트림(Stream)

    • 입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스이다.
    • 모든 스트림은 단방향이며, 하나의 스트림으로 입력과 출력을 동시에 할 수 없다.
    • 스트림의 분류
      1. 바이트 기반 스트림: 바이트 단위로 처리하며, 이름 뒤에 Stream이 붙는다.
        바이트 기반 스트림의 최상위 클래스는 다음과 같으며, 하위 클래스는 최상위 클래스의 이름 앞에 기능이 추가된다
        • 입력 스트림: InputStream
        • 출력 스트림: OutputStream
      2. 문자 기반 스트림: 문자 단위로 처리하며, 이름 뒤에 Stream이 붙지 않는다.
        문자 기반 스트림의 최상위 클래스는 다음과 같으며, 하위 클래스는 최상위 클래스의 이름 앞에 기능이 추가된다.
        • 입력 스트림: Reader
        • 출력 스트림: Writer
      바이트 기반 스트림, 문자 기반 스트림 모두 최상위 클래스는 추상 클래스이다.
    • 스트림의 종류
      색이 있는 것은 기반 스트림이며, 색이 없는 것은 보조 스트림이다.
      • 기반 스트림: 데이터가 실제로 흐르는 스트림
      • 보조 스트림: 데이터가 흐르지 않으면서 기반 스트림을 보조해주는 역할
      • 기반 스트림과 보조 스트림의 차이는 생성자에 있는데, 기반 스트림은 생성자의 매개 변수로 데이터 자체가 들어가고, 보조 스트림은 생성자의 매개 변수로 스트림이 들어간다.

 

File 클래스

  • 파일 시스템의 파일을 표현하는 클래스로 파일 크기, 파일 속성, 파일 이름 등의 정도와 파일 생성 및 삭제 기능을 제공한다.
  • File 클래스에 대한 객체를 생성하였다고 해서 해당 파일이 만들어지는 것은 아니며, createNewFile() 메소드를 이용하여야 파일을 생성할 수 있다.
  • createNewFile() 메소드는 IOException을 위임하고 있기 때문에 해당 메소드를 사용하기 위해서는 IOException에 대한 예외 처리를 진행해 주어야 한다.
  • createNewFile() 메소드는 단순히 파일만 만들어 주는 메소드이기 때문에 지정한 경로가 존재하지 않는 경우 파일이 만들어지지 않으며, 에러가 발생한다. 따라서 경로까지 만들어 주고 싶은 경우에는 mkdirs()/mkdir() 메소드를 사용하여야 한다.
  • mkdirs() 메소드는 여러 개의 경로를 생성하며, mkdir() 메소드는 하나의 경로를 생성한다.

 

FileInputStream

  • 파일을 바이트 단위로 읽을 때 사용되며, 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 읽을 수 있다.
  • InuptStream의 하위 클래스로 InputStream과 사용 방법이 동일하다.
  • 객체 생성: FileInputStream 객체가 생성될 때 파일과 직접 연결되며, 만약 파일이 존재하지 않는다면 FileNotFoundException이 발생하기 때문에 예외처리를 필수적으로 진행해 주어야 한다.
  • 객체를 생성만 하는 경우에는 연결만 되는 것이며, 값을 읽어오기 위해서는 read() 메소드를 사용하여야 한다.
    read() 메소드는 다음에 있는 값을 하나씩 차례로 반환하며, 파일의 끝에 도달하는 경우 -1을 반환한다.
    해당 메소드는 호출될 때마다 문자 하나를 반환하고, 한 칸 뒤로 넘어가기 때문에 조건문에 사용하는 경우 올바른 출력이 어렵다. 따라서 변수를 사용해 값을 저장한 후 출력하여야 한다.
    int value;
    while((value = fis.read()) != -1) {
    	System.out.print((char)value + " "); 
    }
  • 해당 스트림은 바이트 단위로 값을 잘라 읽어오기 때문에 한글을 입력하는 경우 바이트의 범위를 벗어나서 글자가 깨지는 현상이 발생한다. 따라서 한글을 사용하기 위해서는 문자 기반 스트림을 사용하여야 한다.

 

FileOutputStream

  • 파일에 바이트 단위로 저장(쓸)할 때 사용되며, 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 데이터를 파일로 저장할 수 있다.
  • OutputStream의 하위 클래스로 OutputStream과 사용 방법이 동일하다.
  • 객체 생성: FileOutputStream 객체가 생성될 때 파일과 직접 연결되며, 만약 파일이 존재하지 않는 경우 자동으로 생성된다. 하지만 이미 파일이 존재하는 경우 해당 파일을 덮어쓰게 되며, 이를 방지하기 위해서는 생성자의 마지막 매개 변수에 true를 넣어 주면 된다.
    FileOutputStream("파일명", true);
    기본 값은 false이기 때문에 이어 쓰지 않고 덮어 쓸 경우에는 해당 부분은 비우고, 파일명만 작성하면 된다.
    파일이 없는 경우 자동으로 생성되기 때문에 createNewFile() 메소드를 사용하지 않아도 된다.
  • 존재하지 않는 파일을 자동으로 만들어 주기는 하지만, 혹시 모를 에러를 대비해 FileNotFoundException을 위임하고 있으므로 try~catch를 사용하여 처리해 주어야 한다.
  • write(byte[] b, int off, int len) 메소드에서 len은 불러올 문자의 개수이며, off는 불러오기 시작할 문자의 인덱스를 나타낸다.
    따라서 만약 write(bArr, 1, 3);이라면 인덱스 1번째 문자부터 3개의 문자를 작성한다는 의미이다.

 

FileReader

  • 텍스트 파일을 문자 단위로 읽을 때 사용되며, 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 읽을 수 없다.
  • Reader의 하위 클래스로 Reader와 사용 방법이 동일하다.
  • 객체 생성: FileReader 객체가 생성될 때 파일과 직접 연결되며, 만약 파일이 존재하지 않는다면 FileNotFoundException이 발생하기 때문에 예외처리를 필수적으로 진행해 주어야 한다.

 

FileWriter

  • 텍스트 파일에 문자 단위로 저장할 경우에 사용되며, 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 저장이 불가능하다.
  • Writer의 하위 클래스로 Writer와 사용 방법이 동일하다.
  • 객체 생성: FileWriter 객체가 생성될 때 파일과 직접 연결되며, 만약 파일이 존재하지 않는 경우 자동으로 생성된다. 하지만 이미 파일이 존재하는 경우 해당 파일을 덮어쓰게 되며, 이를 방지하기 위해서는 생성자의 마지막 매개 변수에 true를 넣어 주면 된다.
    FileWriter("파일명", true);
    기본 값은 false이기 때문에 이어 쓰지 않고 덮어 쓸 경우에는 해당 부분은 비우고, 파일명만 작성하면 된다.
    파일이 없는 경우 자동으로 생성되기 때문에 createNewFile() 메소드를 사용하지 않아도 된다.

 

close()

  • 파일에서 데이터를 읽고 쓸 때 Stream을 생성하고, 읽고 쓰는 행위가 끝난 이후 Stream을 닫아 주지 않으면 원하지 않는 데이터가 해당 Stream을 통해 작성/출력될 수 있다. 따라서 입출력이 끝난 이후에는 꼭 close() 메소드를 이용하여 닫아 주어야 한다.
  • 닫는 행위는 예외 처리가 일어나든 일어나지 않든 수행되어야 하기 때문에 try~catch문의 finally에 작성한다.
    만약 finally 사용이 번거로운 경우에는 try~with~resource를 사용할 수 있다.
  • finally에서 객체를 닫는 경우 해당 객체의 영향이 finally까지 미쳐야 하기 때문에 try 구문 외부에 객체 변수를 선언해 주어야 하며, close()는 IOException을 위임하고 있으므로, 해당 예외에 대한 예외 처리 또한 필요하다.
    IOException에 대한 예외 처리가 try 구문에서 진행되었다고 해도 해당 예외 처리는 finally까지 영향을 미치지 않기 때문에 별개의 예외 처리를 진행해 주어야 한다.

 

보조 스트림

  • 스트림의 기능을 향상 시키거나 새로운 기능을 추가하기 위해 사용한다.
  • 보조 스트림은 실제 데이터를 주고 받는 스트림이 아니기 때문에 입출력 처리가 불가능하며, 기반 스트림을 먼저 생성한 후 보조 스트림을 생성하여야 한다. 따라서 보조 스트림의 생성자의 매개 변수 타입은 스트림이다.
  • 보조 스트림의 종류
    1. 문자 변환 보조 스트림 (InputStreamReader/OutputStreamWriter) : 문자를 바이트 기반으로 바꾸거나 바이트 기반을 문자 기반으로 바꾸어 사용하는 경우 사용된다.
    2. 입출력 성능 향상 보조 스트림 (BufferedInputStream/BufferedOutputStream) : 다른 보조 스트림의 경우 기반 스트림에 기능을 추가하는 보조 스트림이지만, 입출력 성능 향상 보조 스트림의 경우 유일하게 성능을 향상시키는 보조 스트림이다.
    3. 기본 데이터 타입 출력 보조 스트림 (DataInputStream, DataOutputStream) : 바이트 기반 스트림의 경우 무조건 바이트 크기만큼 잘라서 입/출력이 진행되기 때문에 해당 과정에서 데이터의 손상이 생길 수 있는데, 이것을 방지하기 위해 넣고자 하는 기본 자료형의 크기에 맞춰서 값을 잘라 주는 역할을 한다. 단, 데이터를 넣을 때와 가지고 올 때의 순서가 동일하여야 입력된 자료형의 크기에 맞춰 값의 손상 없이 출력이 가능하다.
    4. 객체 입출력 보조 스트림 (ObjectInputStream/ObjectOutputStream) : 객체 자체를 파일 또는 네트워크로 입/출력할 수 있는 기능을 제공한다. 이때 객체는 문자가 아니기 때문에 바이트 기반 스트림으로 데이터를 변경해주는 직렬화/역직렬화 과정이 필요하다.
      • 직렬화(Serialization): 객체를 문자로 바꿔주는 것으로 Serialization 인터페이스를 해당 객체에 implements 하여 구현한다. 문자로 변환 가능한 객체라는 것을 알려 주는 것이며, 객체 직렬화 시 private 필드를 포함한 모든 필드를 바이트로 변환한다. 이때 필드를 직렬화에서 제외하고 싶은 경우 필드 앞에 transient 키워드를 사용한다.
      • 역직렬화(Deserialization): 데이터(문자)를 객체로 바꿔 주는 것으로 직렬화한 클래스와 같은 클래스를 사용하여야 한다. 이때 단순히 클래스의 이름만 동일한 것이 아니라, 클래스의 내용 또한 동일해야 역직렬화가 가능하다. 직렬화한 클래스와 같은 클래스임을 알려주기 위하여 serialVersionUID 필드를 사용하며, 컴파일 시 JVM이 자동으로 추가해 주기 때문에 작성하지 않아도 오류가 발생하지는 않으나, 자동 생성 시 예상치 못한 예외가 발생될 수 있어 명시하는 것을 권장한다.

 

입출력 성능 향상 보조 스트림

  • 버퍼라는 임시 공간에 내용을 한 번에 담고, 한 번에 보내는 방식을 사용하여 입출력의 횟수를 줄이고, 성능을 향상시킨다.
  • 다른 보조 스트림의 경우 바이트 기반만 존재하지만, 성능 향상 보조 스트림의 경우 문자 기반도 존재한다.
  • BufferedReader에만 존재하는 readLine() 메소드는 문자열을 한 줄씩 읽어오며, 파일의 끝에 도달하는 경우 null을 반환한다.
  • Scanner의 nextLine()과 같은 기능을 하므로 대신 사용 가능하다.
  • 기본 입출력은 아래와 같이 System.in, System.out이 있으며, 기반 스트림의 역할을 한다.
    System.in : InputStream
    System.out : PrintStream
  • 만약 BufferedReader/BufferedWriter를 이용하여 입출력을 받을 경우 System.in/System.out이 바이트 기반 스트림이기 때문에 먼저 문자 기반 스트림으로 변경해 준 후 사용한다.
    InputStreamReader isr = new InputStreamReader(System.in); 
    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    


.

 

'JAVA 웹 개발 > 1. JAVA' 카테고리의 다른 글

13. 네트워크  (0) 2022.05.13
12. 컬렉션  (0) 2022.05.13
10. 예외 처리  (0) 2022.05.13
09. 기본 API  (0) 2022.05.10
08. 다형성  (0) 2022.05.10