MemberController에서 MemberService를 통해 회원가입을 하고 MemberService를 통해서 데이터를 조회할 수 있어야 한다.
MemberController와 MemberService는 의존관계이다.
MemberController가 MemberService를 의존한다.
스프링이 시작할 때 스프링 컨테이너에 @Controller, @Service, @Repository 등의 어노테이션을 가진 클래스 객체들을 생성하고 스프링 빈으로 등록해서 관리한다.
스프링 컨테이너 : 스프링에서 객체를 관리하는 것. 객체의 생명주기를 관리하고 컨테이너에 담겨있는 객체들을 스프링 빈(Bean)이라고 부른다.
@Controller, @Service, @Repository 어노테이션이 존재하는 클래스들의 경우 객체를 스프링 빈(Bean)으로 등록해 스프링 컨테이너에서 관리한다.
위처럼 MemberService() 객체를 new로 할당해서 사용할 수도 있지만, Spring 컨테이너에 저장된 빈을 불러서 사용하면된다.
new를 통해 객체를 생성해서 할당할 경우, 모든 Controller가 각각의 Service 객체를 사용하게 된다.
-> 자원이 낭비되며 비효율 적이다.
스프링 컨테이너에는 각 객체들이 하나만 빈으로 등록되어 해당 빈을 공유하는 형태로 사용하기 때문에 new를통한 객체 할당을 통해서 사용하는것보다 훨씬 효율적으로 사용이 가능하다.
생성자에 @Autowired 어노테이션을 사용하면 스프링 컨테이너에 빈으로 존재하는 MemberService 객체를 연결해 준다.
-> MemberController가 memberService를 의존하게 된다. (Dependency Injection : 의존성 주입)
@Controller 어노테이션이 존재하는 MemberController 클래스는 스프링이 시작할 때 자동으로 스프링 컨테이너에 스프링 빈으로 등록된다.
@Autowired 어노테이션을 통해 스프링 빈을 연결하는 경우 해당 객체또한 스프링 컨테이너의 스프링빈으로 등록되어 있어야한다. 즉 위 코드에서는 memberService가 스프링 빈으로 등록되어 있어야 한다는 것이다.
@Service 어노테이션의 경우 @Controller와 마찬가지로 스프링이 시작할때 컴포넌트 스캔에 의해 스프링 빈으로 등록된다. MemberService에서도 생성자에서 @Autowired를 사용하는데 이때 사용되는 MemberRepository 또한 스프링 빈으로 등록되어 있어야한다.
@Repository 어노테이션도 스프링 시작시 스프링 빈으로 등록시켜준다.
코드들은 위 그림과 같이 의존관계가 연결된다.
컴포넌트 스캔과 자동 의존관계 설정
@Controller, @Service, @Repository와 같이 어노테이션으로 스프링 빈을 등록하는 경우는 컴포넌트 스캔 방식에 해당한다. @Component 어노테이션이 스프링 빈으로 등록하는 어노테이션이지만, @Controller, @Service, @Repository 어노테이션의 내부에 @Component가 존재해 세가지 어노테이션 모두 스프링 빈으로 등록된다.
스프링이 시작할 때 @Component 어노테이션들을 스캔해서 스프링 빈으로 등록하는 것을 컴포넌트 스캔이라고 한다.
@Autowired는 스프링빈을 연결하는 역할을 한다.(의존 관계 설정)
@Component스캔의 범위는 @SpringBootApplication 어노테이션이 존재하는 클래스의 패키지와 해당 패키지의 하위패키지에서만 작동한다.
@SpringBootApplication 어노테이션 내부에 @ComponentScan 어노테이션이 존재한다.
**스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본적으로 싱글 톤 등록을 한다.**
싱글 톤 : 하나만 등록하여 해당 객체를 공유한다.
예를 들어 MemberController의 경우도 하나의 객체만 컨테이너에 스프링 빈으로 등록해서 공유한다. MemberController 객체가 여러개 스프링빈으로 등록되지않는다.
자바 코드로 직접 스프링 빈 등록하기
@Service, @Repository, @Autowired 어노테이션을 제거한 후 코드를 작성한다.
SpringConfig 파일을 새로 하나 생성한다.
@Configuration 어노테이션은 설정 파일을 만들기 위한 어노테이션이며, Bean을 등록하기 위한 어노테이션이다.
@Configuration 클래스 내부에 @Bean 어노테이션 작성시 해당 객체를 Bean에 등록한다는 의미를 가진다.
위 코드는 MemberService와 MemoryMemberRepository 객체를 Bean으로 등록하는 코드이다.
위코드를 통해 코드로 직접 스프링 빈을 등록한 경우에도 아래처럼 구조가 가능해진다.
스프링이 시작될 때, memberRepository와 memberService를 스프링 컨테이너에 올려 빈으로 등록한다.
MemberService 생성자에 파라미터로 memberRepository를 넣어주는데 이때 들어가는 memberRepository는 빈으로 등록된 memberRepository이다.
자바코드로 직접 등록할 때에도 Controller는 컴포넌트 스캔으로 올라가야 하기 때문에 @Controller 어노테이션을 꼭 써줘야한다. 또한 Controller 생성자에서 스프링 빈 객체를 가져올 때도 컴포넌트 스캔 방법인 @Autowired 어노테이션을 작성해주어야한다.
Dependency Injection의 3가지 방법
1.생성자 주입(생성자에 @Autowired 어노테이션 사용)
2.필드주입(필드에 @Autowired 어노테이션 사용)
3.Setter 주입(set 메소드가 public으로 사용되며 어디서든 호출이 가능해져 개발중 문제가 생길 수 있다.)
가장 좋은 방법은 생성자 주입이다.(생성자에 @Autowired 어노테이션을 통해 빈 객체를 연결하는것)
실무에서는 주로 Controller, Service, Repository와 같은 코드들은 컴포넌트 스캔을 사용한다고 한다. 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하는 경우 설정을 통해 코드로 직접 스프링 빈으로 등록한다고 한다.
MemberRepository의 경우 Memory를 사용하는 구현체에서 DB를 사용하는 구현체로 바꿀 예정이기 때문에 컴포넌트 스캔이아닌 코드로 직접 빈에 등록하는 코드를 남겨둔다.
@Autowired를 통해 Dependency Injection을 하는 경우 MemberController와 MemberService 와 같이 스프링이 관리하는 객체 즉 스프링 빈 객체들에 한해서만 가능하다. 스프링 빈으로 등록되지 않은 객체들은 @Autowired 사용이 불가능하다.
해당 인터페이스의 save, findById, findByName, findAll 메소드들을 해당 클래스에서 구현한다.
1씩 sequence를 더하면서 id값을 부여, store(Map)라는 임시저장소(Memory DB)에 member 저장
findById : id값과 일치하는 id를 store에서 탐색
Null이 반환될 수 있기 때문에 Optional.ofNullable로 탐색
findByName : name값을 기반으로 store에 저장된 값 탐색
stream()의 경우 for문으로 모든원소에 접근하듯이 모든 원소들에대해 접근한다고 생각하면 된다.
filter를 통해 name과 일치하는 멤버들을 걸러내고, findAny()를 통해 걸러진 값을 반환한다.
즉 store에 저장된 값중 이름이 name과 일치하는 경우를 반환한다.
모든 값들을 Member List로 반환한다.
회원 리포지토리 Test코드 작성
개발한 기능들을 main메서드를 실행시켜 테스트하는 경우 시간이 오래걸리고, 한번에 모두 테스트하기 어렵다는 단점이 있기 때문에 테스트코드를 생성하여 메소드들을 테스트하는것이 좋다.
test내에 리포지토리 패키지를 생성하여 test클래스를 작성해준다.
@Test 어노테이션을 사용하여 해당 메소드가 테스트 메소드임을 명시해준다.
위처럼 해당 테스트 메소드를 구현한뒤 해당 메소드만 실행시켜 테스트가 가능하다.
save를 실행시킬경우 해당 테스트코드 내에서 오류가 발생하거나 실패를 하게되면 해당 실행에서 경고를 반환한다.
테스트코드가 성공할경우 아래처럼 정상 종료가 된다.
테스트코드는 항상 해당 코드를 실행시키고 검증까지 완료해야한다. save 메소드의 경우 Member 데이터 생성과 save함수 실행, 이후 findById를 통해 해당 id를 검색하고 assertThat(member).isEqualTo(result)를 통해 검증을 완료했다.
findByName 테스트 코드
findByName을 검증하기 위해 데이터를 만들고 findByName을 통해 result를 반환받고 assertThat(result).isEqualTo(member1)을 통해 검증했다.
findAll() 테스트 코드
2개의 member를 생성 및 save하고 findAll()을 실행한다.
이후 findAll()의 반환의 결과가 2개 인지 확인하여 검증한다.
테스트코드의 경우 모든 메소드들을 한꺼번에 실행하여 테스트하는것도 가능하다.
여러 메소드를 한꺼번에 실행할 경우 각 메소드들이 순서와 상관없이 실행된다.
각 메소드들이 실행되면서 같은 데이터를 save하는 경우 에러가 발생한다.(중복 저장 예외)
이러한 문제를 해결하기 위해 아래의 코드를 추가해준다.
@AfterEach는 해당 클래스내의 각 메소드들이 실행을 끝낼때 마다 실행되게하는 어노테이션이다.
MemoryMemberRepository의 clearStore메소드
위 메소드를 통해 각 메소드들이 끝날 때마다 저장소를 비워준다.
-> 중복 저장 예외를 방지할 수 있다.
위의 개발과정은 MemoryMemberRepository를 모두 구현하고, 테스트 코드를 통해 해당 클래스의 메소드들을 검증 했다. 해당 개발 과정을 뒤집어 테스트 코드를 먼저 작성하고 검증이 완료된 후 메소드를 구현할 경우 테스트 주도개발(TDD)이라고 한다.
MemberService 메소드 작성
Join() 메소드
join 내부에서 member중복을 검사하는 코드를 작성한 뒤 외부의 새로운 메소드로 생성했다.
ifPresent를 통해 findByName의 반환값이 존재한다면 throw new IllegalStateExceptioon("이미 존재하는 회원입니다.")를 실행시킨다.
ifPresent는 값이 존재할 경우 내부 로직을 실행시키는 기능을 제공한다.
위와 같이 MemberService 코드를 구현했다.
회원가입, 전체 회원조회, 회원ID를 통한 회원 조회 등 비즈니스적인 메소드들이 구현된다.
MemberService 테스트 코드 구현
위처럼 테스트코드의 메소드명을 한글로 지정해도 상관없다.
테스트 코드를 구현할 때 given-when-then 구성으로 코드를 작성하는것이 좋다.
given : 주어지는 것
when : 실행했을 때
then : 결과
어떤 것이 주어진 상황에서 코드를 실행했을 때 결과가 이렇게 나와야 한다는 과정을 정리하여 작성하는 방법이다.
given-when-then 의 회원가입 테스트코드
멤버가 given으로 주어지고 join메소드를 실행했을 때 검증을 위해 findOne을 통해 나오는 결과가 무엇인지 확인하는 과정이다.
중복 회원가입 예외 테스트코드
assertThrows는 두번째 인자로 주어진 로직 실행시 첫번째 인자에 해당하는 예외가 발생하는지 검사하는 함수입니다.
첫번째 인자가 IllegalStateException이 아닌 NullPointerException이라면 assertThrows에서 에러를 반환한다.
해당 로직에서 정상적으로 IllegalStateException 을 발생시키면 해당 예외를 예외객체 e에 저장하고,
assertThat을 통해 해당 예외 메세지를 "이미 존재하는 회원입니다." 와 비교하여 검증을 완료한다.
Service테스트 코드에서도 테스트별로 멤버들을 생성하고 저장하기 때문에 아래 코드를 넣어준다.
MemberService의 생성자 코드
MemberService의 생성자 인자로 memberRepository가 주어지며 각 MemberService마다 memberRepository를 별도로 갖게 된다.
테스트코드에서의 MemberService
@BeforEach를 통해 모든 메소드들이 실행되기전 해당 코드가 실행되도록 했다.
테스트코드 메소드들이 실행될 때 마다 MemoryMemberRepository()를 생성하고 서비스를 따로 생성해주며 서비스 객체 생성때마다 memberRepository를 생성해준다.
즉 각 테스트 메소드들이 각각의 memberService와 memberRepository를 갖게된다.
2.MVC와 템플릿 엔진 : 서버에서 일부 동작을 통해 HTML을 가공하여 파일을 내려주는것 (JSP, PHP)
3.API : HTML로 파일을 내리는 것이아니라 JSON 형태의 데이터를 반환하는 방법
정적컨텐츠
스프링에서도 정적컨텐츠(Static Content) 기능을 제공한다.
서버 실행후 해당 파일 url 연결
해당 html 파일의 내용이 그대로 출력된다.
정적컨텐츠의 경우 요청한 html파일을 가공없이 그대로 출력해준다. 또한 동적 프로그래밍이 불가능하다.
MVC와 템플릿 엔진
MVC : Model, View, Controller
MVC 모델이 도입되기 이전에는 View에서 데이터가공까지 모두 처리하였다. ex) JSP, PHP
View는 화면과 관련된 일, 비즈니스 로직이나 서버 뒷단에 관련된 동작들은 Controller나 Back-end 비즈니스 로직에서 , View와 Controller, Back-end 에서 주고받는 데이터를 Model이라고 하는 데이터에 담아서 주고받는 구조로 동작한다.
Hello-mvc로 Parameter name을 가지고 GET 요청을 통해 서버에 요청하면 model객체에 addAttribute를 통해 파라미터로 받은 name값을 담아서 hello-template으로 전송한다. return의 목적지로 model 객체가 전송된다.
데이터를 전송할 때 사용하는 Model 객체의 경우 메소드의 파라미터로 선언만 해주면 Spring에서 만들어준다. Model 객체를 사용하기 위해 따로 메모리를 할당할 필요가 없으며 Spring에서 만들어준것을 사용하기만 하면된다.
addAttribute를 통해 저장된 값은 JSON형태이며 "name"이 key이고 파라미터로 전달받은 name이 value가 된다.
spring코드에서 반환되는 데이터가 전달되는 hello-template.html 이다.
Model 객체로 전달된 데이터의 key가 name인 value를 출력한다.
붉은색 선으로 밑줄 쳐져있는 name이 넘겨주는 파라미터 이름이고 푸른색 선이 가리키는 spring은 해당 파라미터의 값이된다.
전달받은 값을 addAttribute("name", name) 으로 Model 객체에 넣어주는데, key가 "name"이 되고 파라미터로 전달받은 값이 name에 들어가게된다. 즉 key : name, value : spring이 된다.
@RequestParam 어노테이션에 require=false를 넣어주는것은 해당 파라미터에 값이 없어도 실행되게 하는것이다.
파라미터 값을 채우지 않을 경우 default text인 hello! empty가 출력된다.
API
@ResponseBody 어노테이션을 사용할 경우 return에 의한 반환값이 그대로 HTTP response의 body에 삽입되어 HTML에 출력된다.
return "hello "+name 에 의해 name파라미터로 전송된 pw4ngc0가 삽입되어 출력됨을 알 수 있다.
API를 통한 객체 반환
Hello 객체에 name값을 넣어 반환한다.
객체를 반환할 경우 해당 객체의 데이터를 JSON형태로 출력해준다.
프론트에서 해당 객체를 가공하여 사용하는 것 같다.
@ResponseBody 어노테이션의 메소드가 객체를 반환할 경우 해당 객체를 JSON형태로 변환하여 HTTP 응답으로 넘겨준다.
FilterInputStream과 FilterOutputStream은 InputStream/OutputStream의 자손이면서 모든 보조스트림의 조상이다.
보조스트림은 자체적으로 입출력을 수행할 수 없기 때문에 기반 스트림을 필요로한다.
protected(FilterInputStream(InputStream in)
public FilterOutputStream(OutputStream out)
FilterI/OStream의 모든 메서드는 기반스트림의 메서드를 그대로 호출하고 Filter보조스트림은 아무런 일을 하지않는다. FilterInputStream, FilterOutputStream은 상속을 통해 원하는 작업을 수행하도록 읽고쓰는 메서드를 오버라이딩 해야한다.
FilterInputStream의 자손 BufferedInputStream, DataInputStream, PushbackInputStream 등
FilterOutputStream의 자손 BufferedOutputStream, DataOutputStream, PrintStream 등
BufferedInputStream, BufferedOutputStream
버퍼 보조스트림은 입출력의 효율을 높이기 위해 사용된다. 버퍼를 이용해서 한번에 여러 바이트를 입출력하는것이 빨라지기 떄문이다.
BufferedInputStream(InputStream in, int size) : InputStream인스턴스와 버퍼 사이즈를 지정하여 버퍼 InputStream을 생성한다.
BufferedInputStream은 입력소스로부터 데이터를 buffer크기만큼 데이터를 읽어 내부 버퍼에 저장해놓는다. 프로그램에서는 외부가 아닌 내부 버퍼에 저장된 데이터를 읽어들이기 때문에 효율적인 데이터 처리가 가능하다.
내부 버퍼에 저장된 모든 데이터를 읽어들인 이후 read메서드가 호출되면 BufferedInputStream은 입력 소스로부터 다시 버퍼크기만큼의 데이터를 읽어다 버퍼에 저장해 놓는다.
BufferedOutputStream(OutputStream out, int size) : OutputStream인스턴스와 버퍼 사이즈를 인자로 던져 내부버퍼를 갖는 BufferedOutputStream 인스턴스를 생성한다.
BufferedOutputStream(OutputStream out) : OutputStream인스턴스를 인자로 주며 사이즈를 지정해주지 않을 경우 기본 버퍼사이즈는 8192byte크기가 된다.
flush() : 버퍼의 모든 내용을 출력하여 버퍼를 비운다.
close() : flush()를 호출해서 버퍼의 모든 내용을 출력하고 BufferedOutputStream인스턴스가 사용하던 모든 자원을 반환한다.
BufferedOutputStream 도 버퍼를 이용하여 출력작업을 하게 된다. write()메소드를 이용하여 BufferedOutputStream의 데이터를 내부 버퍼에 복사하고 버퍼로부터 출력한다. 버퍼의 모든내용을 출력한 후 write()호출 시 버퍼를 비우고 추가적인 데이터들을 내부 버퍼에 복사한다.
출력소스가 버퍼에 남아있는 채로 프로그램이 종료될 수 있기 때문에, BufferedOutputStream을 사용완료한 후에는 반드시 BufferedOutputStream의 close()메소드나 flush() 메소드를 호출해 버퍼의 모든내용이 출력되도록 해야한다.
버퍼의 사이즈가 5기때문에 처음 write호출시 1부터 5까지만 write되고 6,7,8,9는 버퍼에 존재한다.
버퍼를 비우기위해 bos.close()를 호출하여 버퍼에 존재하는 6,7,8,9가 출력되고 버퍼를 비운다.
abstract int read() : 1byte를 읽어온다. 더이상 읽어올 데이터가 없으면 -1을 반환한다.
int read(byte[] b) : 배열 b의 크기만큼 읽어서 배열을 채우고 읽어온 데이터 수를 반환한다.
int read(byte[] b, int off, int len) : len개의 byte를 읽어서, 배열 b의 지정된 위치(off)부터 저장한다. 실제로 읽어오는 데이터의 수는 len보다 작을 수 있다.
void reset() : 스트림에서의 위치(offset)를 마지막으로 mark()이 호출되었던 위치로 되돌린다.
long skip(long n) : 스트림에서 주어진 길이(n)만큼을 건너뛴다.
OutputStream 메서드
void close() : 입력소스를 닫음으로써 사용하고 있던 자원을 반환한다.
void flush() : 스트림의 버퍼에있는 모든 내용을 출력소스에 쓴다.
abstract void write(int b) : 주어진 값을 출력소스에 쓴다.
void write(byte[ ] b) : 주어진 배열 b에 저장된 모든 내용을 출력소스에 쓴다.
void write(byte[ ] b, int off, int len) : 주어진 배열 b에 저장된 내용 중에서 off번째부터 len개 만큼만을 읽어서 출력소스에 쓴다.
flush()는 버퍼가 있는 출력스트림에서만 의미가 있다.
프로그램이 종료될 때, 사용하고 close하지 않은 스트림을 JVM이 자동적으로 닫아주기는 하지만 스트림을 사용 완료한 이후에는 close()를 호출하여 반드시 닫아주는 것이 좋다.
ByteArrayInputStream처럼 메모리를 사용하는 스트림, System.in, System.out과 같은 표준 입출력 스트림은 닫아주지 않아도 된다.
ByteArrayInputStream과 ByteArrayOutput
바이트배열에 데이터를 입출력하는데 사용되는 스트림이다. 입출력하기 전에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업을 하는데 사용된다.
스트림의 종류가 달라도 읽고 쓰는 방법은 동일하다.
ByteArrayInputStream()에 읽을 데이터를 매개변수로 주어 읽기 스트림을 주고 ByteArrayOutputStream을 생성해주어 각InputStream에 저장된 데이터를 하나씩 입력받아 출력 스트림에 쓴다. 이후 스트림에 적은내용을 배열에 옮긴다.
배열을 스트림을 통해 복사하는 방법이다.
배열 크기 복사
InputStream의 offset 0부터 temp.length만큼 데이터를 읽어 temp에 저장한다.
이후 temp에 저장된 내용을 temp의 offset 5부터 길이 5만큼 outputStream에 쓴다.
available()을 통해 스트림에 읽을 데이터가 남아있는경우 반복문이 계속 실행되게하고 내부에서는 데이터를 temp의 크기(4byte)만큼씩 temp에 저장하고 output.write를 통해 temp 에 저장된 데이터를 outputStream에 temp크기만큼씩 쓴다.
tmep의 마지막 저장결과를 보면 4바이트씩 읽다가 마지막에 입력스트림에 2바이트밖에 남지않아 2바이트를 읽은 경우이다. 기존의 데이터를 가지고있는상태에서 2바이만 새로읽어 뒷내용이 유지된다. 이후 OutputStream의 결과처럼 temp의 모든내용이 저장된다.
위의 예제에서의 temp에서 저장된 데이터가 변하지 않는 문제점을 해결한 예제이다. 읽은 크기만큼만 write하기 때문에 마지막에 2byte를 읽은경우 2byte만 write하여 배열이 그대로 복사된다.
입출력 : I/O Input과 Output의 약자이다. 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 의미한다. 키보드로부터 입력을 받고 모니터 화면에 출력을 하는 행위도 I/O행위이다.
스트림(Stream)
자바에서의 입출력 즉 데이터를 한쪽에서 다른쪽으로 전달하려면 두 개의 대상을 연결하고 데이터를 전송할 수 있는 것이 필요하다. 이것을 스트림(Stream)이라한다.
이전에 작성한 스트림과는 다른 개념이다.
입출력에서 사용되는 스트림은 데이터를 운반하는 연결통로를 의미한다.
스트림은 한쪽방향 즉 단방향 통신만 가능하다. 그렇기 때문에 입력과 출력을 수행하기 위해서는 입력스트림(input Stream), 출력스트림(output stream) 2개의 스트림이 각각 필요하다.
스트림은 먼저보낸 데이터를 먼저 받는 FIFO 구조이다. 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다.
바이트기반 스트림 InputStream, OutputStream
스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다른 스트림을 사용한다.
파일
입력스트림 - FileInputStream
출력스트림 - FileOutputStream
메모리(byte 배열)
입력스트림 - ByteArrayInputStream
출력스트림 - ByteArrayOutputStream
프로세스(프로세스간 통신)
입력스트림 - PipedInputStream
출력스트림 - PipedOutputStream
오디오 장치
입력스트림 - AudioInputStream
출력스트림 - AudioOutputStream
입출력 대상에따라 입출력스트림 클래스가 정의되어있으며 각각 읽고 쓰는데 필요한 추상메서드들을 입출력대상에 맞게 구현해 놓았다.
읽기 쓰기 메서드
입력을 받는 InputStream에는 데이터를 읽는 read()메서드가 정의되어있다.
abstract int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
int read(byte[] b) 메서드는 내부적으로 int read(byte[] b, int off, int len) 를 호출하고 int read(byte[] b, int off, int len) 는 내부적으로 read()를 호출한다. 추상메서드 read() 는 입력스트림으로 부터 1byte를 읽어서 반환하는 메서드로 구현되어있다.
출력 스트림 OutputStream에는 데이터를 출력하는 write()메서드가 정의되어있다.
abstract void write(int b)
void write(byte [] b)
void write(byte[] b, int off, int len)
write()메서드도 마찬가지로 아래 두개의 메서드가 내부적으로 write(int b)를 사용한다.
write(int b)는 b를 출력하는 함수이다. 각각 데이터를 출력스트림으로 출력한다.
보조스트림
스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조스트림은 데이터를 입출력하는것이아닌 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있는 역할을 한다. 보조스트림을 사용하기위해서는 입출력 스트림을 먼저 생성한 후 이것을 이용하여 보조스트림을 생성해야한다.
FileInputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
test파일을 읽는 코드이다.
파일 입력 스트림인 FileInputStream에 성능 향상을 위한 보조스트림 BufferedInputStream을 생성해준 경우이다. 보조스트림역시 InputStream과 OutputStream의 자손이므로 입출력방법이 동일하다.
보조스트림의 종류
FilterInputStream, FilterOutputStream : 필터를 이용한 입출력 처리
BufferedInputStream. BufferedOutputStream : 버퍼를 이용한 입출력 처리
DataInputStream, DataOutputStream : int, float와 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능
SequenceInputStream : 두개의 데이터를 하나로 연결
LineNumberInputStream : 읽어 온 데이터의 라인 번호를 카운트
ObjectInputStream, ObjectOutputStream : 데이터를 객체단위로 읽고 쓰는데 사용. 주로 파일을 이용하며 객체 직렬화
PrintStream : 버퍼를 이용하며, 추가적인 print관련 기능
PushbackInputStream : 버퍼를 이ㅛㅇ해서 읽어온 데이터를 다시 되돌리는 기능(unread, push back to buffer)
문자기반 스트림 Reader, Writer
자바에서는 문자형인 char형이 2byte이기 때문에 바이트 기반의 스트림으로 2byte인 문자를 처리하는데 어려움이 있다.
2byte문자를 처리하기 위한 스트림이 문자기반 스트림 Reader와 Writer이다.
문자데이터를 입출력 할때는 바이트기반 스트림 대신 문자기반 스트림을 사용하는것이 좋다.
문자열 파일
입력스트림 - FileReader
출력스트림 - FileWriter
문자열 메모리(char 배열)
입력스트림 - CharArrayReader
출력스트림 - CharArrayWriter
프로세스(프로세스간 문자열 통신)
입력스트림 - PipedReader
출력스트림 - PipedWriter
문자열 버퍼 스트림
입력스트림 - StringReader
출력스트림 - StringWriter
Reader메서드
int read()
int read(char[] cbuf)
abstract int read(char[] cbuf, int off, int len)
InputStream의 read와 비슷하지만 Reader의 read()는 char배열을 인자로받아 처리한다.
Writer메서드
void write(int c)
void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)