문제 상황:
내 프로그램은 파일(storage.ser)에다가 object를 동적으로 write/read하는 프로그램이다.
첫번째 실행에서 os.writeObject()로 어떤 파일(.ser)에
동적으로 writeObject() 여러번 하고 readObject()했을 때는 아무 문제가 없었다.
그런데 프로그램 종료후 다시 실행한 두번째 실행에서
해당 파일에 이어서 wirteObject()하고 다시 readObject하는 상황에서,
이어서 writeObject()한 부분은 전혀 읽어오지 못했다.
그냥 단순하게 이런 코드들을 사용한 건데 왜 문제가 생겼을까?
os = new ObjectOutputStream(new FileOutputStream(fileName, true));
os.writeObject(v);
ObjectInputStream is = new ObjectInputStream(new FileInputStream(fileName));
Voucher obj = (Voucher) is.readObject();
문제의 원인은
ObjectInputStream, ObjectOutputStream을 new하는 시점을 잘 몰랐기 때문이다.
해결 방법은
각 object를 writeObject()할 때마다 new ObjectOutputStream(...)해줬고,
각 object를 readObject()할 때마다 new ObjectInputStream(...)해줬다.
다시말해서 object를 read하려고 할때마다 new ObjectInputStream(fis)한다.
*참고*
var fis = new FileInputStream(fileName)
var ois = new ObjectInputStream(fis);
이때 new FileInputStream(fileName)은 한번만 해줘야 했다.
object 하나 읽을 때마다 new FileInputStream도 매번 하면, file의 첫번째 object만 무한 반복해서 읽더라!
지금까지는 결론 요약이고..
좀 헤맨 과정을 아래 기록한다.
이번에 object input / output stream을 다루면서 다음 두종류의 Exception을 만났다.
[ 만난 Exception ]
두 예외 모두 new ObjectInputStream(...)을 할 때 발생한 예외다!
- StreamCorruptedException : invalid stream header: 7372002D
- StreamCorruptedException : invalid type code AC
두 경우의 근본적 원인은 이거 같다.
전자 : stream header가 적절하지 않아서 생긴 문제
후자 : new ObjectInputStream을 하는 시점의 문제
각 Exception을 만난 경우를 좀 더 자세히 풀어보겠다.
1. StreamCorruptedException : invalid stream header: 7372002D를 만난 이유:
이건 객체를 file에 저장할 때 header를 부적절하게 집어넣어서 생긴 문제로 추측한다.
일반적으로 new ObjectOutputStream을 할 때 이렇게 작성한다.
os = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName, true)));
객체를 file에 이어쓰기 할 때 추가된 객체를 readObject하는 문제가 생겨서 비슷한 예제들을 찾아보다가,
이런 솔루션을 발견했다.
writeStreamHeader()를 재정의해서 쓰는 솔루션이었다.
os = new AppendableObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName, true)));
class AppendableObjectOutputStream extends ObjectOutputStream{
public AppendableObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
@Override
protected void writeStreamHeader() throws IOException {
// make it do nothing!!!
}
}
결론적으로 나는 이 방법대로
new AppendableOutputStreamObject하여 writeObject한 뒤에
file에 저장된 object를 읽으려고 new OutputInputStream(..)하니까
StreamCorruptedException : invalid stream header: 7372002D를 만났다.
그 이유를 나름대로 생각해봤다.
(주의!! 정답이 아님. 그냥 끼워맞춘 것에 가까움..)
이 솔루션은,
1. 각 object를 file에 저장할 때,
ObjectOutputStream 생성시 header정보를 없애고 writeObject하기를 반복함.
그럼 각 object에 대한 header가 없을 것임 (<= ?)
2. file에 저장된 object를 읽으려 할 때,
각 object를 읽기 위해 ObjectInputStream를 매번 생성하는데,
file에 저장된 object의 header정보가 유효하지 않아서 invalid stream header: 7372002D를 만난 것이 아닐까?
이 솔루션이 어느 상황에 적절한지는 아직 모르겠다.
(↓일단 이렇게 메모해두고 넘어가기로 한다.)
왜 object를 file에 이어쓰기(append)할 경우 writeStreamHeader()를 재정의할까?
사실 난 잘 모르겠다. 이 방법으로 해결을 못했기 때문이다.
그런데 일단 ObjectOutputStream 생성자 내부적으로 writeStreamHeader()를 호출하고,
해당 메서드에서는 뭔가 헤더를 작성하는 부분이 있는 듯하다.
(메서드를 재정의하는 솔루션에서는, 이 메서드 동작을 아예 없앤다.)
(아무튼 나는 이 솔루션 이해를 못했으니까 이부분은 여러분께 아무 도움이 안될 것이다.)
![](https://blog.kakaocdn.net/dn/qD8eI/btrUN05W1oL/21nnkCmOk5MiycLJb9pdM1/img.png)
![](https://blog.kakaocdn.net/dn/bLr4BF/btrUQcdyRfo/adTamzN4UZYOI6U83AdtC1/img.png)
2. StreamCorruptedException : invalid type code AC 를 만난 이유:
https://stackoverflow.com/questions/2393179/streamcorruptedexception-invalid-type-code-ac
└> 이것도 이해에 도움이 될 수 있는 정보같다.
그런데 나는.. 아직 감이 잘 안온다..
아무튼 자세한 건 아래 정리한다.
문제 이유: stream 이해 부족
└> input / output stream을 열고 닫는 경우에 대한 이해가 부족했다.
└> stream header에 대한 이해가 부족했다.
(사실 완전 이해 못했는데 확실하게 해결되었다. 그래서 원인을 추측/가정하여 정리해뒀다.)
일단 이 문제를 해결하면서 stream header 관련 문제가 있음을 알았다.
자꾸 new ObjectInputStream할 때 내부 메서드인 readStreamHeader()에서 걸렸기 때문이다.
*참고*
ObjectOutputStream(...) 생성자나 ObjectInputStream(...) 생성자 모두 각각 내부적으로
writeStreamHeader() 및 readStreamHeader() 메서드를 호출함!
사실 이번에 만난 모든 StreamCorruptedException은 모두 readStreamHeader()에서 발생했다.
해결한 방법은 이렇다.
각 object를 writeObject()할 때마다 new ObjectOutputStream(...)해줬고
각 object를 readObject()할 때마다 new ObjectInputStream(...)해줬다.
아 그렇다면..
∨ file에 write object할 때
object 하나마다 ObjectOutputStream을 생성하며 header가 연관되나보다.
∨ file에서 read object할 때
object 하나마다 ObjectInputStream을 생성하며 header가 연관되나보다.
*참고*
그런데 왜 stream의 header가 object 하나하나에 영향을 주는지 이해가 안간다.
왜 object 하나마다 ObjectOutputStream과 ObjectInputStream 하나씩 생성해서 써야 하는거지?
└> 모르겠다.
의문점 :
1. stream의 header가 무슨 역할을 하는거지..
2. 왜 object 하나마다 Object[Input/Output]Stream을 새로 new하는 거지..
사실 모르겠는데 일단 그냥 넘어갔다. (나중에 또 알게되겠지!)
아래와 같이 생각하면 해결이 된다.
☆ 일단 아묻따 직접적으로 도움이 된 링크 : https://technote-mezza.tistory.com/23
└> 내 코드 상황이랑 완전 일치했다!
내 프로그램에서도 객체를 동적으로 writeObject하고 있다.
이럴 경우 객체의 stream header가 바뀐다고 한다
└> 의문점: 객체의 stream header라고? stream이 개별객체랑 무슨 상관이지?
그래서 readObject()할 때도, object를 하나하나 따로따로 읽을 수 있도록
input stream을 object마다 새로 생성해줘야 한다고 한다.
└> 의문점: 진짜... 도대체 stream header랑 개별 object랑 무슨 상관?
계속 이해가 안가는 부분은 개별 object랑 stream header가 하나하나 대응된다는 것이다.
가정 : 그냥 stream header랑 개별 object랑 매치된다고 가정해보자.
▼ 정답이 아니다! 일단 내맘대로 가정하고 맘대로 생각해본 것임!
1. output stream에서:
write object하고싶을 때마다 output stream을 새로 생성해서 write object한다.
다시말해 개별 객체를 write할 때에 새로운 stream을 열어서 write한 것이고, 개별 객체마다 header 하나가 매치된다.
2. input stream에서:
어떤 file input stream에서 read object하고싶을 때, new ObjectInputStream(fis) 해야한다.
이때! 객체를 저장한 file에는 미리 저장된 여러 객체들이 들어있는 상태다.
또한! file에 저장된 각 객체들은 무언가 stream header를 가지고 있다. (이해 안가지만 가정에 의함)
new ObjectInputStream할 때 file에 저장된 각 객체는 적절하게 stream header를 가져야 한다.
=> 개별 객체가 적절한 stream header를 갖지 못하면 ObjectInputStream 생성이 불가능하다.
▼ 정리하자면,
file의 여러 객체를 읽기 위해 아래와 같이 동작한다고 생각해봤다.
while(true){
1. 객체를 읽기 위해 is = new ObjectInputStream한다.
(읽을 객체의 header가 적절해야 new 가능)
2. is.readObject()를 통해 객체하나를 읽어온다.
}
그래서 다음은 object를 read하는 내 코드! (정상 동작)
객체 read할 때마다 is = new ObjectInputStream(...)을 매번 새로 해줬다.
내 결론 다시 보기
동적으로 object를 write, read할 때마다
new ObjectOutputStream(...)과 new ObjectInputStream(...)을 매번 해줬더니 해결되었다는 결론
설명과 해결과정이 너무.. 장황하고... 오래걸렸던 것에 비해서..
깔끔히 이해하지는 못했다.
그냥.. 감만 대충 온다.
이런거 더 많이 연습해보면 언젠가 납득이 되겠지? 하면서 일단 넘어간다.
* 아시는 내용 가르쳐주신다면 너무너무 감사할 것 같아요..
'Database' 카테고리의 다른 글
[DB] 인덱스 사용 이유 이해하기 (0) | 2023.10.17 |
---|---|
[H2] Column이 drop 되지 않음 | JPA Bean을 직접 주입하는 실습 중 (0) | 2023.01.25 |
mysql 서버 상태 확인하기(show status) | db connection 전후 Threads 관련 상태변수 확인 (0) | 2023.01.11 |
[DB] 상속 관계 매핑 전략 선택하기 (2) | 2022.12.30 |
FK 제약조건 삭제하기 - Cannot drop column <FK컬럼명>: needed in a foreign key constraint (1) | 2022.10.11 |