2022.08.01/자바 정리/자료구조를 구현하고 다루는 방법:LinkedList/상속이 아닌 위임으로 코드 공유

Jimin·2022년 8월 1일
1

비트캠프

목록 보기
13/60
post-thumbnail

자료구조를 구현하고 다루는 방법: Linked List 구현 및 사용

  • 크기를 고정하는 배열 대신, 동적으로 크기를 변경할 수 있는 동적 리스트를 구현한다.
  • LinkedList의 구현 방법과 동작 원리를 이해한다.
  • 상속의 문제점을 이해한다.
    • 수퍼 클래스와 서브 클래스의 강한 결합은 기능 변경을 어렵게 한다.
      ⇒ 유지보수가 어렵다.

board-app 프로젝트에 LinkedList 적용하기

  • 기존: 배열을 이용하여 목록 관리
    • 가비지 생성이 많이된다.
    • 삭제시, 속도가 느리다.
    • 크기가 고정되어 있고, 변경시 가비지 생성이 많다.
  • 수정 후: LinkedList를 이용
    • 노드를 연결하는 방식으로 목록 관리
    • 초기 크기가 작다.
    • 크기 변경이 가능하여 가비지 생성이 적다.
    • 삭제가 빠르다.
    • 조회속도가 느리다.
  • 수퍼 클래스를 변경하면, overriding했던 메서드들도 변경해주어야 한다.
  • objectList를 상속했던 boardList, memberList뿐만 아니라, boardHandler, memberHandler까지 수정해주어야 한다.

수퍼 클래스 교체

  • 수퍼 클래스와 서브 클래스는 강하게 결합이 되어 있기 때문에, 수퍼 클래스 교체가 매우 어렵고, 교체한다 하더라도 기존 코드에 너무 많은 영향을 끼치게 된다.
    ⇒ 유지보수가 어렵다!

1단계 - LinkedList에서 값을 저장하는 Node 클래스를 정의한다.

  • Node 클래스 생성
    • 값을 저장할 필드와, 이전 노드, 다음 노드 주소를 저장할 필드를 선언한다.
//연결 리스트의 각 항목의 값을 저장하는 일을 할 클래스이다.
public class Node {
  Object value;
  Node prev;
  Node next;

  public Node(){}

  public Node(Object v) {
    this.value = v;
  }
} 

2단계 - Node 목록을 관리할 LinkedList 클래스를 정의한다.


  • LinkedList 클래스 생성한다.
    • 클래스 필드 및 생성자 정의
    • append() 메서드 구현
    • retrieve(int) 메서드 구현
    • delete(int) 메서드 구현
    • length() 메서드 구현
    • getArray() 메서드 구현
  • LinkedList 클래스
// Node를 이용해 값을 목록을 관리하는 일을 한다.

public class LinkedList {
  private Node head;
  private Node tail;
  private int size;

  public void append(Object value) {
    Node node = new Node(value);
    size++; 
    if (tail == null) {
      head = tail = node;
      return;
    }
    tail.next = node;
    node.prev = tail; 
    tail = node;
  }

  public Object retrieve(int index) {
    if (index < 0 || index >= size) {
      throw new ListException("인덱스의 범위를 초과했습니다!");
    }
    Node cursor = head;
    for (int i = 0; i < index; i++) {
      cursor = cursor.next;
    }
    return cursor.value;
  }

  public Object delete(int index) {
    if (index < 0 || index >= size) {
      throw new ListException("인덱스의 범위를 초과했습니다!");
    }
    size--;
    Object deleted;
    if (head == tail) {
      deleted = head.value; 
      head.value = null; 
      head = tail = null;
      return deleted; 
    }
    Node cursor = head;
    for (int i = 0; i < index; i++) {
      cursor = cursor.next;
    }
    if (cursor.prev != null) { 
      cursor.prev.next = cursor.next; 
    } else { 
      head = cursor.next; 
      head.prev = null; 
    }

    if (cursor.next != null) { 
      cursor.next.prev = cursor.prev; 
    } else { 
      tail = cursor.prev; 
      tail.next = null;
    }
    deleted = cursor.value; 
    cursor.value = null;
    cursor.prev = null;
    cursor.next = null;
    return deleted; 
  }

  public int length() {
    return size;
  }

  public Object[] getArray() {
    Object[] arr = new Object[size];
    Node cursor = head;
    for (int i = 0; i < size; i++) {
      arr[i] = cursor.value;
      cursor = cursor.next;
    }
    return arr;
  }
}
  • 클래스 필드 정의
private Node head; // 첫 노드의 주소를 저장
private Node tail; // 마지막 노드의주소를 저장
private int size; // 저장된 데이터의 개수
  • append() 메서드 구현
    • 처음으로 추가하는 노드인가?
//파라미터로 주어진 값을 노드에 담아 리스트 끝에 연결한다.
  public void append(Object value) {
    // Node 생성 후 값을 저장한다.
    Node node = new Node(value);
    size++; // 목록의 크기를 한 개 증가시킨다.
    // 리스트의 끝에 노드를 붙인다.
    // 만약, 리스트에 노드가 없다면 
    if (tail == null) {
      head = tail = node; // 첫 노드를 등록한다.
      return;
    }
    tail.next = node; // 리스트 끝에 새 노드를 연결한다.
    node.prev = tail; // 새 노드가 현재의 끝 노드를 가리키게 한다. 
    tail = node; // 새 노드를 끝 노드로 만든다.
  }
  • retrieve(int) 메서드 구현
public Object retrieve(int index) {
    // 인덱스의 유효 여부 검사
    if (index < 0 || index >= size) {
      throw new ListException("인덱스의 범위를 초과했습니다!");
    }
    // 인덱스에 해당하는 노드를 찾을 때 head 부터 시작한다. 
    Node cursor = head;
    // 지정된 인덱스의 노드 주소를 알아낸다.
    for (int i = 0; i < index; i++) {
      cursor = cursor.next;
    }
    // cursor가 가리키는 노드의 값을 꺼내 리턴한다.
    return cursor.value;
  }
  • delete(int) 메서드 구현
    • 삭제한 노드의 값을 return해준다.
    • 삭제한 노드는 항상 null로 초기화 해준다.
    • garbage 객체가 다른 garbage 객체를 참조하지 않게 한다.
  1. 삭제하려는 노드가 마지막 노드인가?
  2. 삭제하려는 노드가 맨 앞의 노드인가?
  3. 삭제하려는 노드가 맨 뒤의 노드인가?
 public Object delete(int index) {

    // 인덱스의 유효 여부 검사
    if (index < 0 || index >= size) {
      throw new ListException("인덱스의 범위를 초과했습니다!");
    }

    // 목록 크기를 한 개 줄인다.
    size--;

    // 삭제할 값을 임시 보관하여 메서드를 리턴할 때 호출자에게 전달한다.
    Object deleted;

    if (head == tail) { // 마지막 남은 노드를 제거할 때
      deleted = head.value; // 노드를 삭제하기 전에 리턴할 수 있도록 값을 임시 보관한다.
      head.value = null; // 노드에 들어 있는 값 객체의 주소를 비운다.
      head = tail = null;
      return deleted; // 메서드를 종료할 때 호출자에게 삭제한 값을 리턴한다.
    }

    // 삭제할 노드를 찾기 위해 시작 노드를 head로 설정한다.
    Node cursor = head;

    // 지정된 인덱스의 노드 주소를 알아낸다.
    for (int i = 0; i < index; i++) {
      cursor = cursor.next;
    }

    // 찾은 노드의 앞, 뒤 노드를 바로 연결한다.
    if (cursor.prev != null) { // 맨 앞 노드가 아니라면
      cursor.prev.next = cursor.next; // 현재 노드의 다음 노드 주소를 이전 노드의 next 저장
    } else { // 맨 앞 노드라면
      head = cursor.next; // 삭제할 다음 노드를 시작 노드로 설정한다. 
      head.prev = null; // 시작 노드이기에 앞노드를 가리키지 않게 한다.
    }

    if (cursor.next != null) { // 마지막 노드가 아니라면
      cursor.next.prev = cursor.prev; // 현재 노드의 이전 노드 주소를 다음 노드의 prev 저장
    } else { // 마지막 노드라면 
      tail = cursor.prev; // 현재 커서의 이전 노드를 마지막 노드로 설정한다.
      tail.next = null; // 마지막 노드이기에 다음 노드를 가리키지 않게 한다.
    }

    // 삭제할 노드를 초기화시킨다.
    // => garbage 객체가 다른 garbage 객체를 참조하지 않게 한다.
    deleted = cursor.value; // 노드를 삭제하기 전에 노드에 들어 있는 값을 임시 보관해 둔다.
    cursor.value = null;
    cursor.prev = null;
    cursor.next = null;

    return deleted; // 메서드를 리턴할 때 삭제된 값을 호출자에게 전달한다.
  }
  • length() 메서드 구현
public int length() {
    return size;
  }
  • getArray() 메서드 구현
public Object[] getArray() {
    // 값을 담을 배열을 준비
    Object[] arr = new Object[size];

    // 노드를 따라 가면서 값을 꺼내 배열에 담는다.
    Node cursor = head;
    for (int i = 0; i < size; i++) {
      arr[i] = cursor.value;
      cursor = cursor.next;
    }

    return arr;
  }

LinkedList Test 과정

public class LinkedListTest {
  public static void main(String[] args) {
    LinkedList list = new LinkedList();
    list.add("홍길동");
    list.add("임꺽정");
    list.add("유관순");
    list.add("안중근");

    System.out.println(list.get(0));
    System.out.println(list.get(1));
    System.out.println(list.get(2));
    System.out.println(list.get(3));
    // System.out.println(list.get(-1));
    // System.out.println(list.get(4));

    //중간 노드 삭제 테스트.
    list.remove(2); // 유관순 삭제.
    printList(list);

    // 맨 앞 노드 삭제 테스트.
    list.remove(0); // 홍길동 삭제
    printList(list);

    // 맨 마지막 노드 삭제 테스트.
    list.remove(1); // 안중근  삭제.
    printList(list);
  }

  static void printList(LinkedList list) {
    System.out.println("---------------------------------------------------");
    for(int i=0;i<list.size();i++) {
      System.out.println(list.get(i));
    }
  }
}
  • LinkedList 초기부터 노드 추가하는 과정



3단계 - BoardList와 MemberList의 수퍼 클래스를 ObjectList에서 LinkedList로 교체한다.

  • BoardList 클래스 변경
    • 수퍼 클래스 교체에 맞추어 오버라이딩 메서드 변경
  • MemberList 클래스 변경
    • 수퍼 클래스 교체에 맞추어 오버라이딩 메서드 변경
  • BoardList 클래스
    • extends 하는 클래스를 Object 클래스에서 LinkedList로 교체하고 메소드 명들을 바꿔준다.
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.LinkedList;

// 게시글 목록을 관리하는 역할
//
public class BoardList extends LinkedList {

  private int boardNo = 0;

  @Override
  public void append(Object e) {
    Board board = (Board) e;
    board.no = nextNo();
    super.append(e);
  }

  @Override
  public Board retrieve(int boardNo) {
    for (int i = 0; i < length(); i++) {
      Board board = (Board) super.retrieve(i);
      if (board.no == boardNo) {
        return board;
      }
    }
    return null;
  }

  @Override
  public Object delete(int boardNo) {
    for (int i = 0; i < length(); i++) {
      Board board = (Board) super.retrieve(i);
      if (board.no == boardNo) {
        return super.delete(i);
      }
    }

    return null;
  }

  private int nextNo() {
    return ++boardNo;
  }
}
  • MemberList 클래스
    • BoardList와 마찬가지로 수정해준다.

4단계 - BoardList와 MemberList의 변경에 맞추어 XxxHandler 클래스를 변경한다.

  • BoardHandler 클래스 변경
    • BoardList 변경에 맞추어 오버라이딩 메서드의 이름을 변경한다.
  • MemberHandler 클래스 변경
    • MemberList 변경에 맞추어 오버라이딩 메서드의 이름을 변경한다.
  • BoardHandler 클래스
    • extends 하는 클래스를 Object 클래스에서 LinkedList로 교체하고 메소드 명들을 바꿔준다.
    • 특히, delete() 메소드의 경우, if문 안에서는 boolean값이 와야하는데 삭제된 노드 값을 가지고 있으므로 null이 아닌 경우로 수정해준다.
import java.text.SimpleDateFormat;
import java.util.Date;
import com.bitcamp.board.dao.BoardList;
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.Prompt;

public class BoardHandler {

  private String title; // 게시판의 제목

  // 게시글 목록을 관리할 객체 준비
  private BoardList boardList = new BoardList();

  public BoardHandler() {
    this.title = "게시판";
  }

  public BoardHandler(String title) {
    this.title = title;
  }

  public void execute() {
    while (true) {
      System.out.printf("%s:\n", this.title);
      System.out.println("  1: 목록");
      System.out.println("  2: 상세보기");
      System.out.println("  3: 등록");
      System.out.println("  4: 삭제");
      System.out.println("  5: 변경");
      System.out.println();

      try {
        int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");

        displayHeadline();

        // 다른 인스턴스 메서드를 호출할 때 this에 보관된 인스턴스 주소를 사용한다. 
        switch (menuNo) {
          case 0: return;
          case 1: this.onList(); break;
          case 2: this.onDetail(); break;
          case 3: this.onInput(); break;
          case 4: this.onDelete(); break;
          case 5: this.onUpdate(); break;
          default: System.out.println("메뉴 번호가 옳지 않습니다!");
        }

        displayBlankLine();

      } catch (Exception ex) {
        System.out.printf("예외 발생: %s\n", ex.getMessage());
      }
    } // 게시판 while
  }

  private static void displayHeadline() {
    System.out.println("=========================================");
  }

  private static void displayBlankLine() {
    System.out.println(); // 메뉴를 처리한 후 빈 줄 출력
  }

  private void onList() {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    System.out.printf("[%s 목록]\n", this.title);
    System.out.println("번호 제목 조회수 작성자 등록일");

    // boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
    Object[] list = this.boardList.getArray();

    for (Object item : list) {
      Board board = (Board) item;
      Date date = new Date(board.createdDate);
      String dateStr = formatter.format(date); 
      System.out.printf("%d\t%s\t%d\t%s\t%s\n",
          board.no, board.title, board.viewCount, board.writer, dateStr);
    }

  }

  private void onDetail() {
    System.out.printf("[%s 상세보기]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("조회할 게시글 번호? ");
        break;
      } catch (Exception ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    // 해당 번호의 게시글이 몇 번 배열에 들어 있는지 알아내기
    Board board = this.boardList.retrieve(boardNo);

    // 사용자가 입력한 번호에 해당하는 게시글을 못 찾았다면
    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    System.out.printf("번호: %d\n", board.no);
    System.out.printf("제목: %s\n", board.title);
    System.out.printf("내용: %s\n", board.content);
    System.out.printf("조회수: %d\n", board.viewCount);
    System.out.printf("작성자: %s\n", board.writer);
    Date date = new Date(board.createdDate);
    System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);

  }

  private void onInput() {
    System.out.printf("[%s 등록]\n", this.title);

    Board board = new Board();

    board.title = Prompt.inputString("제목? ");
    board.content = Prompt.inputString("내용? ");
    board.writer = Prompt.inputString("작성자? ");
    board.password = Prompt.inputString("암호? ");
    board.viewCount = 0;
    board.createdDate = System.currentTimeMillis();

    this.boardList.append(board);

    System.out.println("게시글을 등록했습니다.");
  }

  private void onDelete() {
    System.out.printf("[%s 삭제]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("삭제할 게시글 번호? ");
        break;
      } catch (Exception ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    if (boardList.delete(boardNo) != null) {
      System.out.println("삭제하였습니다.");
    } else {
      System.out.println("해당 번호의 게시글이 없습니다!");
    }
  }

  private void onUpdate() {

    System.out.printf("[%s 변경]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("변경할 게시글 번호? ");
        break;
      } catch (Throwable ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    Board board = this.boardList.retrieve(boardNo);

    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    String newTitle = Prompt.inputString("제목?(" + board.title + ") ");
    String newContent = Prompt.inputString(String.format("내용?(%s) ", board.content));

    String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
    if (input.equals("y")) {
      board.title = newTitle;
      board.content = newContent;
      System.out.println("변경했습니다.");
    } else {
      System.out.println("변경 취소했습니다.");
    }
  }
}


공통 코드(필드, 메서드)를 공유하는 방법: 연관(association)

  • 상속 대신, 연관을 이용하여 기존 코드를 활용한다.
  • 이전 버전의 코드를 연관 방식으로 변경한다.

0단계 - 프로젝트 소스 준비

  • 이전 단계의 소스를 가져 온다.

1단계 - ObjectList를 상속하는 대신 연관(포함) 관계로 바꾼다.

  • BoardList 클래스 변경




    • ObjectList를 포함하는 관계로 바꾼다.
    • 기능을 ObjectList 로 위임할 수 있도록 메서드를 추가, 변경한다.
    • 서브 클래스가 아닌 관계로 클래스 이름을 BoardDao로 변경한다.
    • 메서드 이름도 데이터를 다루는 이름으로 적절하게 변경한다.
      • 기존의 ObjectList 는 인스턴스를 다룬다는 의미로 메서드 이름도 그에 맞춰서 지었다.
      • BoardDao는 인스턴스라는 기술적인 용어 대신 데이터에 초점을 맞춘다.
      • 따라서 데이터에 초점을 맞춘 메서드 이름으로 변경한다.

단순 인스턴스 목록을 관리하는 관점에서 게시글 데이터를 다루는 관점으로 변경시켰기 때문에 데이터 관점에서 이름을 변경해준다.

  • BoardDao class
    • BoardDao는 ObjectList 객체를 포함한다. ⇒ 위임
    • BoardDao는 ObjectList의 기능에 의존한다.
      ⇒ 따라서 ObjectList는 BoardDao가 의존하는 객체이다. ==> "dependency" 의존 객체
    • findAll() 에서 Object에서 Board로 바로 형변환해주기
// 게시글 목록을 관리하는 역할
//
public class BoardDao {

  ObjectList list = new ObjectList();

  private int boardNo = 0;

  // BoardDao 에서 제공할 메서드를 정의하고,
  // 이 메서드가 호출되면 ObjectList의 도움을 받아 처리한다.
  public void insert(Object e) {

    // 게시글 객체를 적절하게 준비한 다음
    Board board = (Board) e;
    board.no = nextNo();

    // 의존 객체 ObjectList를 사용하여 목록에 추가한다.
    list.add(e);
  }

  public Board findByNo(int boardNo) {

    // 의존 객체 BoardList를 이용하여 기존에 저장된 게시글 목록 중에 
    // 해당 번호의 게시글을 찾는다.
    for (int i = 0; i < list.size(); i++) {
      Board board = (Board) list.get(i);
      if (board.no == boardNo) {
        return board;
      }
    }

    return null;
  }

  public boolean delete(int boardNo) {

    // 의존 객체 ObjectList을 이용하여 목록에 저장된 게시글을 찾아 삭제한다.
    for (int i = 0; i < list.size(); i++) {
      Board board = (Board) list.get(i);
      if (board.no == boardNo) {
        return list.remove(i);
      }
    }

    return false;
  }

  public Board[] findAll() {

    // 의존 객체 ObjectList를 이용하여 목록에 저장된 게시글을 가져온다.
    Object[] arr = list.toArray();

    // Object[] 배열에 담긴 인스턴스 목록을 Board[] 배열에 담아 리턴한다.
    Board[] boards = new Board[arr.length];

    for (int i = 0; i < arr.length; i++) {
      boards[i] = (Board) arr[i];
    }

    return boards;
  }

  private int nextNo() {
    return ++boardNo;
  }
}
  • com.bitcamp.board.dao.MemberList 클래스 변경
    • BoardList를 변경한 것과 똑같이 처리한다.
public class MemberDao {

  // MemberDao 가 사용할 의존 객체를 선언한다.
  ObjectList list = new ObjectList();

  // ObjectList를 상속 받지 않기 때문에 
  // 목록에 데이터를 추가하고 싶다면 
  // MemberDao 클래스에 해당 메서드를 직접 정의해야 한다.
  // 물론, 실제 작업은 ObjectList 가 할 것이다.
  //
  public void insert(Member member) {
    list.add(member);
  }

  // MemberList 에서 MemberDao 로 바꿔는 것에 맞춰
  // 메서드의 이름도 데이터에 초점을 맞춰 변경한다.
  //
  public Member findByEmail(String email) {
    for (int i = 0; i < list.size(); i++) {
      Member member = (Member) list.get(i);
      if (member.email.equals(email)) {
        return member;
      }
    }
    return null;
  }

  public boolean delete(String email) {
    for (int i = 0; i < list.size(); i++) {
      Member member = (Member) list.get(i);
      if (member.email.equals(email)) {
        return list.remove(i);
      }
    }
    return false;
  }

  public Member[] findAll() {
    //목록에 저장된 회원 데이터를 가져온다.
    Object arr[] = list.toArray();
    //Object배열의 값을 Member배열로 옮긴다.
    Member [] members = new Member[arr.length];
    for(int i=0; i < arr.length; i++) {
      members[i] = (Member)arr[i];
    }
    return members;
  }
}

2단계 - BoardDao와 MemberDao에 맞추어 XxxHandler를 변경한다.

  • BoardHandler 클래스 변경
    • BoardList 대신 ObjectList에 의존하고 있는 BoardDao를 사용한다.
  • MemberHandler 클래스 변경
    • MemberList 대신 ObjectList에 의존하고 있는 BoardDao를 사용한다.
  • BoardHandler 클래스
    • 객체 이름과 메서드명만 올바르게 수정해준다.
public class BoardHandler {

  private String title; // 게시판의 제목

  // 게시글 목록을 관리할 객체 준비
  private BoardDao boardDao = new BoardDao();

  public BoardHandler() {
    this.title = "게시판";
  }

  public BoardHandler(String title) {
    this.title = title;
  }

  public void execute() {
    while (true) {
      System.out.printf("%s:\n", this.title);
      System.out.println("  1: 목록");
      System.out.println("  2: 상세보기");
      System.out.println("  3: 등록");
      System.out.println("  4: 삭제");
      System.out.println("  5: 변경");
      System.out.println();

      try {
        int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");

        displayHeadline();

        // 다른 인스턴스 메서드를 호출할 때 this에 보관된 인스턴스 주소를 사용한다. 
        switch (menuNo) {
          case 0: return;
          case 1: this.onList(); break;
          case 2: this.onDetail(); break;
          case 3: this.onInput(); break;
          case 4: this.onDelete(); break;
          case 5: this.onUpdate(); break;
          default: System.out.println("메뉴 번호가 옳지 않습니다!");
        }

        displayBlankLine();

      } catch (Exception ex) {
        System.out.printf("예외 발생: %s\n", ex.getMessage());
      }
    } // 게시판 while
  }

  private static void displayHeadline() {
    System.out.println("=========================================");
  }

  private static void displayBlankLine() {
    System.out.println(); // 메뉴를 처리한 후 빈 줄 출력
  }

  private void onList() {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    System.out.printf("[%s 목록]\n", this.title);
    System.out.println("번호 제목 조회수 작성자 등록일");

    // boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
    Board[] boards = this.boardDao.findAll();

    for (Board board : boards) {
      Date date = new Date(board.createdDate);
      String dateStr = formatter.format(date); 
      System.out.printf("%d\t%s\t%d\t%s\t%s\n",
          board.no, board.title, board.viewCount, board.writer, dateStr);
    }

  }

  private void onDetail() {
    System.out.printf("[%s 상세보기]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("조회할 게시글 번호? ");
        break;
      } catch (Exception ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    // 해당 번호의 게시글이 몇 번 배열에 들어 있는지 알아내기
    Board board = this.boardDao.findByNo(boardNo);

    // 사용자가 입력한 번호에 해당하는 게시글을 못 찾았다면
    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    System.out.printf("번호: %d\n", board.no);
    System.out.printf("제목: %s\n", board.title);
    System.out.printf("내용: %s\n", board.content);
    System.out.printf("조회수: %d\n", board.viewCount);
    System.out.printf("작성자: %s\n", board.writer);
    Date date = new Date(board.createdDate);
    System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);

  }

  private void onInput() {
    System.out.printf("[%s 등록]\n", this.title);

    Board board = new Board();

    board.title = Prompt.inputString("제목? ");
    board.content = Prompt.inputString("내용? ");
    board.writer = Prompt.inputString("작성자? ");
    board.password = Prompt.inputString("암호? ");
    board.viewCount = 0;
    board.createdDate = System.currentTimeMillis();

    this.boardDao.insert(board);

    System.out.println("게시글을 등록했습니다.");
  }

  private void onDelete() {
    System.out.printf("[%s 삭제]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("삭제할 게시글 번호? ");
        break;
      } catch (Exception ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    if (boardDao.delete(boardNo)) {
      System.out.println("삭제하였습니다.");
    } else {
      System.out.println("해당 번호의 게시글이 없습니다!");
    }
  }

  private void onUpdate() {

    System.out.printf("[%s 변경]\n", this.title);

    int boardNo = 0;
    while (true) {
      try {
        boardNo = Prompt.inputInt("변경할 게시글 번호? ");
        break;
      } catch (Throwable ex) {
        System.out.println("입력 값이 옳지 않습니다!");
      }
    }

    Board board = this.boardDao.findByNo(boardNo);

    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    String newTitle = Prompt.inputString("제목?(" + board.title + ") ");
    String newContent = Prompt.inputString(String.format("내용?(%s) ", board.content));

    String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
    if (input.equals("y")) {
      board.title = newTitle;
      board.content = newContent;
      System.out.println("변경했습니다.");
    } else {
      System.out.println("변경 취소했습니다.");
    }
  }
}
profile
https://github.com/Dingadung

0개의 댓글