Java - 간단한 게시판 구현을 통한 직렬화(Serialization) 이해하기(2) - 게시판 구현 및 직렬화 역직렬화 테스트
들어가며
이번 시간에는 게시판 구현을 통해 직렬화와 역직렬화 테스트를 진행한다.
(1)글 링크
https://sjparkk-dev1og.tistory.com/124
게시판 만들기
해당 예제의 순서는 List<Article> 타입의 직렬화된 데이터가 있다면 역직렬화를 통해서 읽어오고 없다면 새로운 입력을 받아 직렬화를 통해 데이터를 저장하고 저장된 데이터를 읽어와 보여주는 방식으로 진행한다.
구현 전체 코드
package com.company;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Board {
static List<Article> list = new ArrayList<>();
public static void main(String[] args) throws IOException, ClassNotFoundException {
try {
// 직렬화된 파일을 읽어와 보여줌
readFromFile();
} catch(FileNotFoundException e) {
// 없다면 e 발생
e.printStackTrace();
}
//입력을 받음
Scanner s = new Scanner(System.in);
while(true) {
prompt(s);
}
}
private static void prompt(Scanner s) throws FileNotFoundException, IOException {
displayBoard();
System.out.println(">> 입력을 원하면 w, 종료를 원하면 x를 입력해주세요.");
String command = s.nextLine();
if(command.equalsIgnoreCase("w")) {
write(s);
} else if (command.equalsIgnoreCase("x")) {
System.exit(0);
}
}
private static void write(Scanner s) throws FileNotFoundException, IOException {
System.out.print("작성자 이름을 입력해주세요.");
String writer = s.nextLine();
System.out.print("글 내용을 입력해주세요.");
String message = s.nextLine();
int seq = 0;
if(list.size() != 0) {
seq = list.get(list.size() - 1).seq + 1;
}
list.add(new Article(seq, writer, message));
System.out.println();
// 직렬화 출력하기: 글이 작성되었으면 메모리의 객체를 직렬화한 뒤
// 물리적인 파일로 하드디스크에 저장한다.
saveToFile();
}
private static void displayBoard() {
System.out.println("순서\t글쓴이\t메시지");
System.out.println("-----------------------");
if(list.size() == 0) {
System.out.println("[아직 글이 없습니다.]");
}
list.forEach(System.out::println);
}
private static void saveToFile() throws FileNotFoundException, IOException {
// 파일 출력 스트림 객체를 만든 후, 이름을 "board.txt"라고 지정하고
// fos를 바탕으로 오브젝트 출력 스트림을 생성한 뒤 writeObject 한다.
try(FileOutputStream fos = new FileOutputStream("board.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(list);
}
}
@SuppressWarnings("unchecked")
private static void readFromFile() throws FileNotFoundException, IOException, ClassNotFoundException {
// FileInputStream을 통해 파일 입력 스트림 객체를 만든 후 "board.txt" 파일로부터 읽어온다.
// fis를 바탕으로 오브젝트 입력 스트림을 생성한 뒤 readObject 한다.
// 오브젝트 형태로 읽으면 안에 있는 내용이 무슨 타입인지 정확히 알기 어려우므로 타입 캐스팅 해준다.
try(FileInputStream fis = new FileInputStream("board.txt");
ObjectInputStream ois = new ObjectInputStream(fis);) {
list = (List<Article>) ois.readObject();
}
}
}
동작 순서 설명
먼저 가장 처음 돌렸을때는 직렬화된 파일(board.txt)이 없기 때문에 FileNotFoundException에 걸려 에러를 발생한다.
그 후 displayBoard 메서드를 통해서 List에 내용이 있는지 없는지 판단 후 Scanner를 통해 입력을 할지 종료를 할지 입력 받고 입력을 선택하면 작성자의 이름과 글의 내용을 입력 받아 list에 저장 후 saveToFile 메서드를 통해서 파일 출력 스트림 객체를 만들어 파일 이름을 정하고 오브젝트 출력 스트림을 이용하여 list에 내용을 저장한다. 이 과정이 직렬화 과정이다.
try(FileOutputStream fos = new FileOutputStream("board.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(list);
}
한번 입력을 마쳤다면 종료 후에 다시 시작을 해보면 내용이 있기 때문에 e 발생시키지 않고 리스트를 읽어오는 것을 확인할 수 있다. 이때 역직렬화 과정을 통해 읽어온다.
try(FileInputStream fis = new FileInputStream("board.txt");
ObjectInputStream ois = new ObjectInputStream(fis);) {
list = (List<Article>) ois.readObject();
}
그리고 옆에 프로젝트를 확인해보면 board.txt가 생성된 것을 확인할 수 있다.
눌러 보면 다음과 같이 직렬화된 데이터 값이 들어있다.
transient
마지막으로 transient에 대해서 알아보자. transient는 직렬화하려는 클래스에 필드에 붙여서 직렬화시 transient가 붙은 대상을 제외하고 직렬화한다. transient 필드는 역직렬화 시 null 또는 타입의 기본값으로 반환된다.
만약 다음과 같이 message를 transient 처리하게 되면 출력 시 직렬화 대상에서 제외되어 null로 처리 되는 것을 확인 할 수 있다.
깃 링크
더 디테일한 구현과정이 궁금하시다면 아래 깃헙에 올려준 commit 내역을 참고하길 바란다.
https://github.com/sjparkk/board-serialization
마치며
이렇게 코드로 직접 구현까지 해보니 처음에 이론적이 부분만 봤을때 보다는 이해력이 올라간 것을 느낀다.