2021-12-29(수) 8주차 3일

javascript random 검색

Math.random()

Math.random() 함수는 0 이상 1 미만의 구간에서 근사적으로 균일한(approximately uniform) 부동소수점 의사난수를 반환하며, 이 값은 사용자가 원하는 범위로 변형할 수 있다.

0 * 30 <= n < 1 *30

1을 더해준다.

var no = Math.random() * 30 + 1

소수점 이하를 잘라야 된다.

Math.floor()

BoardController 백엔드

가장 유사한 코드를 찾아서 복붙하고 수정한다.

ContactController가 가장 유사함

ContactController를 복사 붙여넣기
이름은 BoardController로 한다.

ArrayList는 ContactController에서 쓰는 거
ArrayList를 복사해서 ArrayList3를 만든다
ArrayList3는 따로 수정할 필요가 없다

BoardController로 가서 수정하자!

ArrayList → ArrayList3
contact → board
Contact contact → Board board

변수명도 직관적이게 짓는 게 좋음
b 라고 하지 말고 board 라고 적기
클래스 이름과 유사하게 적는다
보통 자바에서 클래스는 대문자로 시작한다
메소드 이름은 소문자로 시작
변수명도 소문자로 시작

public Object add(Board board)
자바에서 클래스는 대문자로 시작
변수는 소문자로 시작
자바는 대소문자를 구분한다

public Object get(int index)
todo 리스트랑 똑같이 index 받는 걸로 한다
인덱스 유효성 검사 넣어준다

if (index < 0 || index >= ArrayList3.size) {
  return "";
}

public Object update(int index, Board board)
이것도 todo 리스트랑 똑같이 index 받는다
index 번호, 제목, 내용 받아야 됨
인덱스 유효한지 검사해야됨
유효한 인덱스면
return ArrayList3.set(index, board) == null ? 0 : 1;

public Object delete(int index)

package com.eomcs.mylist;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController 
public class BoardController {

  @RequestMapping("/board/list")
  public Object list() {
    return ArrayList3.toArray(); 
  }

  @RequestMapping("/board/add")
  public Object add(Board board) {
    System.out.println(board);
    ArrayList3.add(board);
    return ArrayList3.size;
  }


  @RequestMapping("/board/get")
  public Object get(int index) {
    if (index < 0 || index >= ArrayList3.size) {
      return "";
    }
    return ArrayList3.list[index];
  }

  @RequestMapping("/board/update")
  public Object update(int index, Board board) {
    if (index < 0 || index >= ArrayList3.size) {
      return 0;
    }
    return ArrayList3.set(index, board) == null ? 0 : 1;
  }

  @RequestMapping("/board/delete")
  public Object delete(int index) {
    if (index < 0 || index >= ArrayList3.size) {
      return 0;
    }
    ArrayList3.remove(index);
    return 1;
  }
}

http://localhost:8080/board/list

http://localhost:8080/board/add?

public Object add(Board board)

Board 객체를 만든다
newBoard()
이름과 일치하는 세터 메서드가 있는지 확인
스프링 부트가 BoardController가 요구하기를 파라미터 값으로 Board 객체 줘야 돼
Board 객체 만듦
Board 객체에 데이터 값을 담아야 됨
담을 때 기준이 파라미터 이름과 일치하는 세터 메서드를 찾는다
세터 메서드를 프로퍼티라고 한다.
파라미터와 일치하는 프로퍼티를 찾는다.
파라미터와 일치하는 세터 메서드를 찾는다.

http://localhost:8080/board/add?t=aaa&c=bbb

null로 되어 있음

Board 객체를 달라는 이유가 뭐야
야 스프링부트! 클라이언트가 보내는 데이터를 거기에 담아서 줘

만약에 add가 낱개로 되어 있어
낱개로 되어 있으면
add(title, content)
?title=aaa&content=bbb
근데 그게 아니라 add(Board board) 이렇게 되어 있음
다른 거 필요 없고 Board 객체 줘
그릇에 담아 달라고!
아니 그릇을 쟁반에 담아 달라고!
낱개로 주지 말고 Board 객체에 담아 달라고!

스프링 부트한테 나 이런 데이터 받고 싶으니까 이런 이름으로 보낸 데이터 있으면 담아 줘

Board 객체에 담아 달라는 거

그냥 막 담는 게 아니라 클라이언트가 보낸 이름과 일치하는 프로퍼티, 즉 세터 메서드를 찾아서 세터 메서드를 호출해서 담는다

http://localhost:8080/board/add?title=aaa&content=bbb

서버 쪽에서 시간을 결정해야 됨

board.setCreatedDate(new java.sql.Date);

import java.sql.Date;

board.setCreatedDate(new Date());

현재 시간을 밀리세컨드로 리턴해주는 메서드가 있음

board.setCreatedDate(new Date(System.currentTimeMillis()));

안에 있는 System.currentTimeMillis() 이게 제일 먼저 실행됨
그다음 new Date() 실행됨 → Date 객체가 만들어짐. Date 객체 주소.

http://localhost:8080/board/add?title=aaa1&content=bbb1

http://localhost:8080/board/list

http://localhost:8080/board/get?index=0

get도 잘 나옴

근데 조회할 때마다 조회수 올라가야 됨

Board board = (Board) ArrayList3.list[index];

(Board) 라고 명시해준다
하위 분류는 상위 분류를 가리킬 수 없어
Object는 Board의 상위 분류이기 때문에 담을 수 없다

board 객체 주소로 찾아간다

  @RequestMapping("/board/get")
  public Object get(int index) {
    if (index < 0 || index >= ArrayList3.size) {
      return "";
    }
    Board board = (Board) ArrayList3.list[index];
    board.viewCount++;
    return board;
  }

이제 get으로 조회하면 조회수 1 올라감

http://localhost:8080/board/update?index=1&title=xxx&content=yyy

update 하면 조회수가 0으로 초기화 되어버림
기존 객체(old)를 버리고 새 객체(board)를 집어 넣어서

  @RequestMapping("/board/update")
  public Object update(int index, Board board) {
    if (index < 0 || index >= ArrayList3.size) {
      return 0;
    }
    
    Board old = (Board) ArrayList3.list[index];
    board.viewCount = old.viewCount;
    board.createdDate = old.createdDate;
    
    return ArrayList3.set(index, board) == null ? 0 : 1;
  }

이제 update 해도 조회수 유지됨

delete는 따로 수정할 거 없음

http://localhost:8080/board/delete?index=1

잘 지워짐

백엔드 개발 다 끝남

package com.eomcs.mylist;

import java.sql.Date;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController 
public class BoardController {

  @RequestMapping("/board/list")
  public Object list() {
    return ArrayList3.toArray(); 
  }

  @RequestMapping("/board/add")
  public Object add(Board board) {
    board.setCreatedDate(new Date(System.currentTimeMillis()));
    System.out.println(board);
    ArrayList3.add(board);
    return ArrayList3.size;
  }


  @RequestMapping("/board/get")
  public Object get(int index) {
    if (index < 0 || index >= ArrayList3.size) {
      return "";
    }
    Board board = (Board) ArrayList3.list[index];
    board.viewCount++;
    return board;
  }

  @RequestMapping("/board/update")
  public Object update(int index, Board board) {
    if (index < 0 || index >= ArrayList3.size) {
      return 0;
    }

    Board old = (Board) ArrayList3.list[index];
    board.viewCount = old.viewCount;
    board.createdDate = old.createdDate;

    return ArrayList3.set(index, board) == null ? 0 : 1;
  }

  @RequestMapping("/board/delete")
  public Object delete(int index) {
    if (index < 0 || index >= ArrayList3.size) {
      return 0;
    }
    ArrayList3.remove(index);
    return 1;
  }
}

Board 프론트엔드

  fetch("/board/list")
    .then(function(response) {
      return response.json();
    })
    .then(function(boards) {
      console.log(boards);
      for (var i = 0; i < boards.length; i++) {
        var board = boards[i];
        var tr = document.createElement("tr");
        tr.innerHTML = `<td><a href="view.html?index=${i}">${board.title}</a></td>
        <td>${board.viewCount}</td>
        <td>${board.createdDate}</td>`;
        tbody.appendChild(tr);
      }
    });
  제목* : <input id="x-title" type="text"><br>
  내용* : <textarea id="x-content" cols="60" rows="10"></textarea><br>
  조회수 : <span id="x-view-count"></span><br>
  등록일 : <span id="x-created-date"></span><br>
  // 3) 서버에서 데이터 가져오기
  fetch(`/board/get?index=${index}`)
    .then(function(response) {
      return response.json();
    })
    .then(function(board) {
      // 4) 연락처 상세 정보를 화면에 출력한다.
      xTitle.value = board.title;
      xContent.value = board.content;
      xViewCount.innerHTML = board.viewCount;
      xCreatedDate.innerHTML = board.createdDate;
    })

span 태그로 바꿨으니까 value가 아니라 innerHTML

  document.querySelector("#x-update-btn").onclick = function() {
    if (xTitle.value == "" || xViewCount.value == "") {
      window.alert("필수 입력 항목이 비어 있습니다.");
      return;
    }

    fetch(`/board/update?index=${index}&title=${xTitle.value}&content=${xContent.value}`)
      .then(function(response) {
        return response.text();
        // return response.json(); // json 형식의 문자열을 자바스크립트 객체로
      })
      .then(function(text) {
        console.log(text);
        location.href = "index.html";
      });

  };
  document.querySelector("#x-delete-btn").onclick = function() {
    fetch(`/board/delete?index=${index}`)
      .then(function(response) {
        return response.json();
      })
      .then(function(result) {
        console.log(result);
        location.href = "index.html"
      })
  };

완성!

05.1 인스턴스 멤버 사용법: 인스턴스 필드 활용하기

public class ArrayList {
  static Object[] list = new Object[5];
  static int size = 0;

ArrayList에 있는 메서드 모두 Object[] ← 이 배열을 사용한다.

ArrayList2, ArrayList3에도 static으로 된 Object[] 변수가 있다.

ArrayList Object (복합, composition, 강한 연결)
ArrayList가 만들어질 때 이 배열이 만들어지고 ArrayList가 사라지면 배열이 사라진다.

composition은 다이아몬드 색을 채운다
Life cycle이 같다
ArrayList와 Object[]가 생명을 같이 함
단순히 포함관계, 집합관계라고 하지 않고 composition(복합관계)라고 함
container가 사라지면 container가 포함하고 있는 것도 사라진다

↑ A는 B를 포함하는데, A 객체가 사라지면 A가 포함하는 B도 사라진다는 거

UML을 보고 해석할 줄 알아야 됨
뉘앙스

A ⟶ B ⟵ X
A가 B를 단순히 사용한다는 거
다른 애도 B를 사용할 수 있다

A ⋄⟶ B ⟵⨯ X
단순히 사용하는 걸 넘어서
X가 B를 사용하지 않는다
A만이 B를 사용한다는 거
A만이 B를 사용한다는 건 맞는데 A가 삭제된다고 B도 삭제되는 건 아니고 B를 다른 애한테 줄 수 있다는 거
사람과 자동차의 관계
사람은 자동차를 소유하고 있다
소유자가 죽었다고 자동차가 죽는 거 아님
다이아몬드 안 채운 거. A만이 B를 쓴다는 거. 다만 A가 삭제될 때 B도 같이 삭제되는 건 아니고 다른 애한테 줄 수 있음. 사람과 자동차의 관계 같은 거임. 사람이 자동차를 쓰다가 다른 사람에게 넘길 수 있음.

ArrayList2도 Object[]를 사용
ArrayListt3도 Object[]를 사용

ArrayList, ArrayList2, ArrayList3 모두 메서드는 같음. 메서드가 중복됨!

메서드 중복은 기능 변경, 추가를 어렵게 한다.
⟹ 유지보수가 어렵다.
⟹ 같은 일을 하는 클래스가 여러 개 존재하는 문제 발생!

BoardController에서 인덱스 유효성 검사 하는 부분 주석으로 막기
어차피 ArrayList3 remove()에서 하니까

  @RequestMapping("/board/delete")
  public Object delete(int index) {
    //    if (index < 0 || index >= ArrayList3.size) {
    //      return 0;
    //    }
    return ArrayList3.remove(index) == null ? 0 : 1;
  }

index -1 삭제해보기

이 메서드를 실행하다가 에러가 났다는 거

remove() 메소드 44번째줄 ↓
Object old = list[index];
-1 이라는 인덱스는 없어서 Array Index Out Of Bounds 에러난 거

이번에는 index를 100으로 해보자
유효하지 않은 인덱스를 지정하면 에러남

ArrayList3 remove() 메서드에 제대로 방어 프로그램을 안 짰음
set() 메서드에서는 인덱스가 유효한지 무효한지 검사하도록 했는데
remove() 메서드에서는 검사를 안 함
거기에 대한 방어적인 프로그램을 안 짰음
set() 인덱스 유효성 검사하는 부분 복사해서 remove()에 붙여넣기

  static Object remove(int index) {
    if (index < 0 || index >= size) { // 값이 저장된 위치가 무효한 인덱스라면 
      return null;
    }
    Object old = list[index];
    for (int i = index + 1; i < size; i++) {
      list[i - 1] = list[i];
    }
    size--;
    return old;
  }

이제 에러 안 뜸. 방어 코드를 작성한 거.

다른 개발자가 사용할 것을 생각해서 방어 코드를 짜자

remove()에 추가한 코드를 ArrayList, ArrayList2에도 추가해줘야됨

ArrayList remove(), ArrayList2 remove() 모두 수정함

ArrayList에 변경사항이 발생하면 ArrayList를 복사해서 만든 클래스들도 변경해야 한다.
⟹ 매우 번거롭다!

지금 3개여서 그렇지 만약 ArrayList가 더 많았으면

코드는 공유하되 이 코드가 사용하는 배열은 다르게 하는 방법!
그럴 수만 있으면 쓸데없이 ArrayList를 여러 개 만들 필요 없음

코드가 중복된다는 것은 이런 문제가 있다

static 변수 대신 인스턴스 변수를 사용하는 방법을 알아보자!

1단계 - ArrayList의 배열 관련 변수를 인스턴스 필드로 전환한다.

인스턴스 필드를 사용하여 ArrayList 코드를 공유하기

레퍼런스 배열과 size 변수를 인스턴스 필드로 바꾼다
static 빼면 됨

public class ArrayList {
  static Object[] list = new Object[5];
  static int size = 0;
public class ArrayList {
  // 인스턴스 필드(변수)
  // => 인스턴스 필드는 new 명령을 통해 생성한다.
  Object[] list = new Object[5];
  int size = 0;

ArrayList 인스턴스 = ArrayList 설계도에 따라 만든 변수들

실행할 때 사용할 인스턴스 주소를 알려준다.

add() 라는 메서드가 있으면
add(ArrayList, Board)
어떤 ArrayList를 쓸 건지
ArrayList : 사용할 인스턴스 주소를 준다
인스턴스 주소에 따라 add()가 사용하는 배열이 결정된다.
옛날에는 클래스에 소속된 변수를 쓸 때는 그냥 add 해서 바로 썼지만

메서드를 호출할 때마다 인스턴스 주소를 알려줘야됨
첫 번째 파라미터로 ArrayList that 넣어주기

package com.eomcs.mylist;

public class ArrayList {

  // 인스턴스 필드(변수)
  // => 인스턴스 필드는 new 명령을 통해 생성한다.
  Object[] list = new Object[5];
  int size = 0;

  static void add(ArrayList that, Object obj) {
    if (that.size == that.list.length) { 
      that.list = grow(that);
    }
    that.list[that.size++] = obj;
  }

  static Object[] grow(ArrayList that) {
    Object[] arr = new Object[newLength(that)];
    copy(that.list, arr);
    return arr;
  }

  static int newLength(ArrayList that) {
    return that.list.length + (that.list.length >> 1);
  }

  static void copy(Object[] source, Object[] target) {
    int length = source.length;
    if (target.length < source.length) {
      length = target.length;
    }
    for (int i = 0; i < length; i++) {
      target[i] = source[i];
    }
  }

  static Object[] toArray(ArrayList that) {
    Object[] arr = new Object[that.size]; 
    for (int i = 0; i < that.size; i++) { 
      arr[i] = that.list[i]; 
    }
    return arr; 
  }

  static Object remove(ArrayList that, int index) {
    if (index < 0 || index >= that.size) { // 값이 저장된 위치가 무효한 인덱스라면 
      return null;
    }
    Object old = that.list[index];
    for (int i = index + 1; i < that.size; i++) {
      that.list[i - 1] = that.list[i];
    }
    that.size--;
    return old;
  }

  static Object set(ArrayList that, int index, Object obj) {
    if (index < 0 || index >= that.size) { // 값이 저장된 위치가 무효한 인덱스라면 
      return null;
    }
    Object old = that.list[index];
    that.list[index] = obj;
    return old;
  }
}

메서드의 파라미터에서 메서드를 호출할 때마다 인스턴스 주소를 알려줘야됨
ArrayList의 인스턴스 주소를 메서드 파라미터로 넘기도록 메서드를 변경한다.
기존 메서드는 파라미터로 받은 ArrayList 인스턴스 변수(list 배열과 size)를 사용한다.

3단계 - ArrayList 변경에 맞춰 페이지 컨트롤러 클래스를 변경한다.

ContactController 클래스 변경
Contact 객체의 목록을 저장할 배열을 ArrayList

indexOf()에서 static 지우기

package com.eomcs.mylist;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController 
public class ContactController {

  // Contact 객체 목록을 저장할 메모리 준비
  // => Object[] list = new Object[5];
  // => int size = 0;
  ArrayList contactList = new ArrayList();

  @RequestMapping("/contact/list")
  public Object list() {
    return ArrayList.toArray(contactList); 
  }

  @RequestMapping("/contact/add")
  public Object add(Contact contact) {
    //    System.out.println(contact);
    ArrayList.add(contactList, contact);
    return contactList.size;
  }


  @RequestMapping("/contact/get")
  public Object get(String email) {
    int index = indexOf(email);
    if (index == -1) {
      return "";
    }

    return contactList.list[index];
  }

  @RequestMapping("/contact/update")
  public Object update(Contact contact) {
    int index = indexOf(contact.email);
    if (index == -1) {
      return 0;
    }

    return ArrayList.set(contactList, index, contact) == null ? 0 : 1;
  }

  @RequestMapping("/contact/delete")
  public Object delete(String email) {
    int index = indexOf(email);
    if (index == -1) {
      return 0;
    }

    ArrayList.remove(contactList, index);
    return 1;
  }

  int indexOf(String email) {
    for (int i = 0; i < contactList.size; i++) {
      Contact contact = (Contact) contactList.list[i];
      if (contact.email.equals(email)) { 
        return i;
      }
    }
    return -1;
  }
}

ArrayList2 삭제하기

TodoController 수정하기

Retactor - Rename

메서드를 호출할 때마다 어떤 배열을 써야 되는지 알려준다
ArrayList.remove(todoList, index);

같은 ArrayList에 있는 걸 쓴다

ArrayList3 삭제하기

05.1 인스턴스 멤버 사용법: 인스턴스 필드 활용하기

ContactController는 list와 size를 갖고 있는 ArrayList의 인스턴스를 알고 있다.
TodoController

ArrayList 설계도에 따라 만든 메모리 = ArrayList의 인스턴스

BoardController도 ArrayList 설계도에 따라 만든 list 배열과 size가 들어 있는

ArrayList.add(200, Contact)

클라이언트 요청을 처리하는 메서드에서 ArrayList의 메서드를 호출할 때 항상 ArrayList 인스턴스 주소를 알려준다.

좋은 웹페이지 즐겨찾기