updateTodo 맨 밑에 추가하기
titleInput.style["display"] = "";
titleInput 숨김으로 시작
주석 풀기
var titleInput = document.querySelector("#x-title-input");
titleInput.style["display"] = "none";
변경 취소 버튼
button 종료태그 넣기
취소 버튼 추가하기
<button type="button" onclick="cancelTodo(${i})">취소</button>
취소 버튼이 처음에 보이면 안 됨
편집 상태일 때
인라인 스타일은 우선순위 1위
<button type="button" onclick="cancelTodo(${i})" style="display:none;">취소</button>
버튼에 아이디 부여
<button id="x-update-btn-${i}" type="button" onclick="updateTodo(${i})">변경</button>
변경을 누르면 인풋 박스가 생기고 변경 버튼이 사라지고 취소 버튼이 보여야 됨
tr을 찾는 걸로 바꾼다
자신이 속한 tr을 찾는 거
변경 버튼을 눌렀을 때 몇 번을 눌렀는지 알 수 있으니까 tr에 index를 붙인다
<td><span class="todo-title" ${titleTdOption}>${todoList[i].title}</span></td>
var titleSpan = document.querySelector(`tr[data-no="${no}"] .todo-title`);
td가 부모로부터 두 번째 자식
var titleSpan = document.querySelector(`tr[data-no="${no}"] > td.todo-title > span`);
var no = titleInput.parentNode.parentNode.getAttribute("data-no");
absolute로 공중부양 시켜서 위에 띄우는 경우가 있음
그런 경우에는 부모가 td가 아님
// updateTodo
titleInput.setAttribute("data-no", no);
Esc 키 27번
if (e.keyCode == 27) { // ESC 키를 눌러 편집을 취소한다면
titleSpan.style["display"] = "";
titleInput.style["display"] = "none";
document.body.appendChild(titleInput);
}
목록 상단에 입력 폼을 추가한다. 키 이벤트 리스너를 추가한다.
입력 후 엔터 키를 누르면 목록에 추가한다. 서버 REST API 연동
document.querySelector("#x-todo-input").onkeyup = function(e) {
if (e.keyCode == 27) {
e.target.value = "";
} else if (e.keyCode == 13) {
if (e.target.value == "") {
window.alert("필수 입력 항목이 비어 있습니다.");
return;
}
fetch(`/todo/add?title=${e.target.value}`)
.then(function(response) {
return response.text();
})
.then(function(text) {
console.log(text);
location.reload();
});
}
};
목록을 가져온 다음에 커서를 인풋 박스에 두기
document.querySelector("#x-todo-input").focus();
User Experience : 사용자 편의를 고려한 디자인
90-MyList프로젝트1 / 25 페이지
배열의 타입이 다르면 코드도 변경해야 한다
배열의 타입이 다르면 ArrayList 클래스의 코드도 변경해야 한다.
이런 문제점을 해결하고자 한다.
만약 배열의 타입이 같다면 코드 변경을 최소화할 수 있다
여러 타입의 객체 주소를 받을 수 있는 배열은?
① Object 레퍼런스는 모든 타입의 객체 주소를 담을 수 있다.
② Object로 바꾸는 대신 이 배열을 사용하는 측에서 형변환을 처리해야 한다.
③ 같은 타입의 배열이기 때문에 별도의 코드 변경 최소화!
기존의 ArrayList의 레퍼런스 배열을 Object[]
타입으로 변경한다.
Object 타입
Object obj = new Object();
Object obj = new Contact();
Object obj = new Todo();
그것을 가리키는 통상적인 용어
저거요
Object 타입이 저거라는 거
90-MyList프로젝트1 / 28 페이지
클래스(분류)
하위 타입의 객체를 가리킬 수 있다
하위 타입의 객체 주소를 저장할 수 있다
무생물 타입의 레퍼런스는 하위 타입 객체 주소를 담을 수 있다
생물이라는 레퍼런스는 하위에 있는 모든 객체의 주소를 담을 수 있다
그 객체를 찾아갈 수 있다는 건 가리키고 있다는 거
Object
90-MyList프로젝트1 / 29 페이지
다형적 변수 : 하위 타입의 객체를 가리킬 수 있다 (주소를 저장할 수 있다)
Object는 최상위 클래스
Contact, Todo는 Object의 서브 타입이다.
상위 분류를 가리킬 때 화살표로 가리킨다
하위 분류는 상위 분류를 못 가리킨다
super type = parent
sub type = child
class Contact extends Object {
}
1단계
static Object[] list = new Object[5];
add()의 파라미터 타입 변경
static void add(Object obj) {
if (size == list.length) { // 배열이 꽉 찼다면,
list = grow(); // 메서드 이름에서 해당 코드에 대한 설명을 짐작할 수 있다.
}
list[size++] = obj;
}
toArray()의 리턴 타입 변경
static Object[] toArray() {
Object[] arr = new Object[size]; // 배열에 저장된 값만 복사할 새 배열을 만든다.
for (int i = 0; i < size; i++) {
arr[i] = list[i]; // 전에 배열에서 값이 들어 있는 항목만 복사한다.
}
return arr; // 복사한 항목들을 담고 있는 새 배열을 리턴한다.
}
grow()의 리턴 타입 변경
static Object[] grow() {
Object[] arr = new Object[newLength()];
copy(list, arr);
return arr;
}
newLength() 변경
static int newLength() {
return list.length + (list.length >> 1);
}
static void copy(Object[] source, Object[] target) {
// 개발자가 잘못 사용할 것을 대비하여 다음 코드를 추가한다.
// 즉 target 배열이 source 배열보다 작을 경우 target 배열 크기만큼만 복사한다.
int length = source.length;
if (target.length < source.length) {
length = target.length;
}
for (int i = 0; i < length; i++) {
target[i] = source[i];
}
}
remove()
static Object remove(int index) {
Object old = list[index];
for (int i = index + 1; i < size; i++) {
list[i-1] = list[i]; // 한 칸씩 앞으로 당긴다
}
size--;
return old;
}
Object 객체 주소를 리턴할 수도 있지만 하위 클래스 중에서 하나를 리턴할 수도 있다는 거
static Object set(int index, Object obj) {
if (index < 0 || index >= size) { // 값이 저장된 위치가 무효한 인덱스라면
return null;
}
Object old = list[index];
list[index] = list;
return old;
}
remove를 손코딩
배열을 앞으로 당기는 걸 할 수 있는지
혼자서 안 보고 짤 수 있을 정도로 이해해야 됨
@RequestMapping("/contact/get")
public Object get(String email) {
int index = ArrayList.indexOf(email);
if (index == -1) {
return "";
}
return ArrayList.list[index];
}
indexOf 옮기기
90-MyList프로젝트1 / 30 페이지
ContactController
클라이언트의 요청을 처리하는 메서드들이 들어 있다.
ArrayList
배열을 다루는 메서드 및 변수들이 들어 있다.
indexOf()
를 ContactController로 이동
why?
indexOf()
는 이메일을 가지고 배열의 항목을 찾는다.
즉 indexOf()
메서드로 모든 타입에 대해 사용할 수 없고 Contact를 다룰 때만 사용한다. 그래서 그 역할을 수행하는 클래스로 옮긴 것이다.
Object[]
예전과 다르게 특정 타입의 배열을 다루는 것이 아니라 모든 타입의 배열을 다룬다.
즉 ArrayList Contact 뿐만 아니라 다양한 타입의 목록을 다루도록 변경했기 때문에 indexOf()
를 다른 클래스로 옮긴 것이다.
역할이 바뀌면 바뀐 역할에 따라서 메서드가 옮겨지기도 한다.
절대 안 바뀌는 게 아니라 클래스 역할이 바뀌면 이동하거나
이런 것들 하는 게 객체 지향 프로그래밍
역할이 바뀜
지금 ArrayList는 어떤 타입의 배열도 다 다룰 수 있음
Todo에는 email이 없음
email은 Contact 라는 타입에만 있는 거
그럼 indexOf()
가 ArrayList에 존재하는 건 말이 안 됨
리팩토링 기법 중에서 '메서드 이동' (Move Method)
객체 간의 기능 이동 - '메서드 이동' (Move Method)
리팩토링 → 이해하기 쉽도록 코드를 정리하는 거
Contact contact = (Contact) ArrayList.list[i];
컴파일러 입장에서는 모름
주소를 Contact에 담으라고 하니까 어이가 없는 거
그 주소가 Contact인지 어떻게 알고 담으라는 거냐
Todo 객체가 들어 있을 수도 있잖아
컴파일러 입장에서는 그 시도 자체가 잘못 된 거
Object obj = new Contact();
Contact r = obj; // 에러
컴파일러 속였다고 끝나는 거 아님
JVM을 속일 순 없음
실제로 괄호 안에 적은 타입이 변수에 들어 있어야 됨
ArrayList2.list[index].done = done;
list는 Object 타입
Object에는 done이라는 속성이 없다!
((Todo) ArrayList2.list[index]).done = done;
Todo 라고 했는데 Todo가 아니라 Contact가 들어 있으면 어떡해?
JVM은 명령어를 실행할 때마다 일일이 검사한다
JVM에서는 검사하기 때문에 그때 에러난다
JVM을 속일 방법은 없다
한 번 밖에 안 쓰는 변수를 만드는 건 낭비
레퍼런스 배열의 타입을 Object로 변경하니까 ArrayList를 복사했을 때 변경할 게 없다
90-MyList프로젝트1 / 31 페이지
동물 obj;
obj = O ← 동물 객체 또는 그 하위 분류의 객체를 저장할 수 있다.
X obj = O; ← X 객체 주소나 X의 하위 타입 객체들의 주소가 올 수 있다.
메서드 파라미터가 클래스 이름이면 타입을 본다
90-MyList프로젝트1 / 32 페이지
컴퓨터에 있는 메모리 포함
메모리만 떼서 사용할 수 있음
TodoController - ArrayList2 - Object[]
BoardController - ArrayList3 - Object[]
90-MyList프로젝트1 / 33 페이지
의사소통 하려고 만든 건데
UML, 실전에서는 이것만 쓴다
https://book.naver.com/bookdb/book_detail.nhn?bid=6439362
인스턴스 변수와 인스턴스 메소드
1단계 - 게시글 요청을 처리할 REST API를 만든다.