본문 바로가기
개발자 꿈나무의 하루/01_Boot Camp

(네이버클라우드 부트캠프) 38일차 - 실습프로젝트(파일입출력)

by kk_naks 2024. 7. 19.

Stack 리팩토링 - menuPath

1. 기존 스택의 문제점

    //기존 menuGroup에서 Stack이 사용되는 소스코드
      public MenuGroup(String title) {
        super(title);
        this.menuPath = new Stack<>();
      }
    
      public void setParent(MenuGroup parent) {
        this.parent = parent;
        this.menuPath = parent.menuPath;
      }
    
      private String getMenuPathTitle(Stack<String> menuPath) {
        StringBuilder strBuilder = new StringBuilder();
        for (String s : menuPath) {
          if (!strBuilder.isEmpty()) {
            strBuilder.append("/");
          }
          strBuilder.append(s);
        }
        return strBuilder.toString();
      }
  • 기존 소스코드에서 Stack은 menuGroup이 인스턴스 될 때마다 새로운 Stack을 생성한다.
  • 부모의 menuGroup이 있다면 인스턴스된 Stack은 Garbage가 된다.
  • menuPath를 호출하는 메서드는 Stack의 구조로 꺼내는 것이 아니라 List타입으로 탐색을 한다.

2. 스택 구조 변경

  1. 현재 위치의 menuTitle을 push한다.
  2. 부모가 있다면 부모의 menuPath로 이동한다.
  3. root의 부모인 null를 만날때 까지 1~2를 반복한다.
  4. image

      public MenuGroup(String title) {
        super(title);
      }
    
      public void setParent(MenuGroup parent) {
        this.parent = parent;
      }
    
      private String getMenuPath() {
        Stack<String> menuPath = new Stack<>();
        MenuGroup menuGroup = this;
        while (menuGroup != null) {
          menuPath.push(menuGroup.title);
          menuGroup = menuGroup.parent;
        }
    
        StringBuilder strBuilder = new StringBuilder();
        while (!menuPath.empty()) {
          if (!strBuilder.isEmpty()){
            strBuilder.append("/");
          }
          strBuilder.append(menuPath.pop());
        }
        return strBuilder.toString();
      }

파일 입출력 FILE I/O

1. 바이너리 데이터 입출력

  • 바이너리 타입의 입력은 텍스트 타입 보다 메모리를 적게사용하고, 속도가 빠르다.
  • 타입별로 인코딩하는 방식이 다르다.
    • "abc" --> UTF-8 문자코드 바이트
    • 20 --> 2의 보수
    • 3.14 --> IEEE-754 규칙
    • true --> 2의 보수
  • 설정 방법이 복잡하다.
  • PDF, PPT, DOC, GIF, JPEG, MP3, AVI 등이 있다.

2. 데이터 입출력 적용

1) 데이터 입출력 흐름

    image

2) byte[] 만들기

  • 객체를 byete[] 배열에 1바이트씩 담아야한다.
  • ByteArrayOutputStream 클래스를 이용하여 바이트배열 담을 저장소를 만든다.
  • 이후 toByteArray()메서드로 통해 바이트 배열을 리턴 할 수 있다.
  •   public byte[] getBytes() throws IOException {
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
          //객체의 정보들을 byte로 담는다.
          return out.toByteArray();
        }
      }

3) write(int) 메소드

  • 객체를 byete[] 배열에 1바이트씩 담을때 write 메서드를 사용한다.
  • write(int) int 타입을 받지만 1바이트만 읽고 저장할 수 있다.
  • 비트 연산자를 통해 바이트를 1바이트까지 이동 해야한다.
  •     ByteArrayOutputStream out = new ByteArrayOutputStream()
        out.write(no >> 24);
        out.write(no >> 16);
        out.write(no >> 8);
        out.write(no);

4) 데이터 구조

  • 데이터의 구조에 따라 바이트의 할당규칙을 정해야 load과정에서 해당규칙을 통해 값을 읽을 수 있다.
  • image

    //유저정보를 바이트배열로 변환 
    public byte[] getBytes() throws IOException {
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
          //회원번호 넣기
          out.write(no >> 24);
          out.write(no >> 16);
          out.write(no >> 8);
          out.write(no);
    
          //이름 넣기
          byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
          out.write(bytes.length >> 8);
          out.write(bytes.length);
          out.write(bytes);
    
          //이메일 넣기
          bytes = email.getBytes(StandardCharsets.UTF_8);
          out.write(bytes.length >> 8);
          out.write(bytes.length);
          out.write(bytes);
    
          //password
          bytes = password.getBytes(StandardCharsets.UTF_8);
          out.write(bytes.length >> 8);
          out.write(bytes.length);
          out.write(bytes);
    
          //tel
          bytes = tel.getBytes(StandardCharsets.UTF_8);
          out.write(bytes.length >> 8);
          out.write(bytes.length);
          out.write(bytes);
    
          return out.toByteArray();
        }
      }

5) 파일로 출력

  • 데이터의 구조에 따라 바이트배열들을 병합하고 객체의 전체 개수를 구한다 .
  • FileOutputStream에 바이트의 배열들 개수를 넣고 저장한 바이트 배열들을 넣는다.
  • image

    //바이트배열로 변환된 유저정보를 파일로 출력
    private void saveUser() {
        try (FileOutputStream out = new FileOutputStream("user.data")) {
          int userLength = userList.size();
          out.write(userLength >> 8);
          out.write(userLength);
    
          for (User user : userList) {
            byte[] bytes = user.getBytes();
            out.write(bytes.length >> 8);
            out.write(bytes.length);
            out.write(bytes);
          }
        } catch (IOException e) {
          System.out.println("회원 정보 저장 중 오류 발생" + e.getMessage());
        }
      }

6) 파일로 입력

  • 입력은 출력과정의 역순으로 진행한다.
  • image

    //파일을 테이터 배열로 전환
     private void loadUser() {
        try (FileInputStream in = new FileInputStream("user.data")) {
          int userLength = in.read() << 8 | in.read();
          int maxUserNum = 0;
          for (int i = 0; i < userLength; i++) {
            int len = (in.read() << 8) | in.read();
            byte[] bytes = new byte[len];
            in.read(bytes);
    
            User user = User.valueOf(bytes);
            userList.add(user);
    
            maxUserNum = Math.max(maxUserNum, user.getNo());
          }
          User.initSeqNo(maxUserNum);
        } catch (IOException e) {
          System.out.println("회원 정보 로딩 중 오류 발생" + e.getMessage());
        }
      }
    
    // 데이터 배열을 유저 객체로 전환
    public static User valueOf(byte[] bytes) throws IOException {
        try (ByteArrayInputStream in = new ByteArrayInputStream(bytes)) {
          User user = new User();
          user.setNo(in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read());
    
          byte[] buffer = new byte[10000];
          int len = in.read() << 8 | in.read();
          in.read(buffer, 0, len);
          user.setName(new String(buffer, 0, len, StandardCharsets.UTF_8));
    
          len = in.read() << 8 | in.read();
          in.read(buffer, 0, len);
          user.setEmail(new String(buffer, 0, len, StandardCharsets.UTF_8));
    
          len = in.read() << 8 | in.read();
          in.read(buffer, 0, len);
          user.setPassword(new String(buffer, 0, len, StandardCharsets.UTF_8));
    
          len = in.read() << 8 | in.read();
          in.read(buffer, 0, len);
          user.setTel(new String(buffer, 0, len, StandardCharsets.UTF_8));
          return user;
        }
      }