[코드스쿼드] MV* 1 역할나누기
코드스쿼드 프론트엔드 레벨3 웹자판기(DOM&Event) 과정 중 6번째 MV* 1 역할나누기에 대해서 정리한 글입니다.
MV*
Model
View
- 명확함
- 데이터를 가지고 있음
- 화면을 구성함
- 중간자
Model
과View
관계는 느슨하게 만들 필요가 있음MVC
에서의C
는 입력장치 역할을 담당함Model
과View
관계를 더 느슨하게 만들어주는 것을- presenter 라고 일컫음
MVP
- presenter 라고 일컫음
우리가 집중할 중재자
Model
과View
역할을 명확하게 분리Model
과View
가 많아지면- 서로 참조하면서 그 관계가 복잡해짐
- 따라서
M,V 관계
를 느슨하게 만들 필요가 있음 - 또한
Views
간에도 관계가 느슨하면 좋겠음
controller
- M,V 관계를 구분- 가장 중요한 것
각 역할이 무엇인지에 집중하는 것
역할 정의
Model
- 데이터 갱신
추가
변경
제거
획득
- 데이터를 획득하는 로직
Ajax
LocalStorage
이 포함될 수 있음- Controller에서 만들어서 줄 수도 있음
- 데이터 갱신
Controller
전체적인 목표는 Model과 View 간 변경사항을 연결하는 것
- event handler
Listener
구현체 만들기 - View가 렌더링을 잘 할 수 있도록 데이터 가공
viewModel
- 데이터 변경이 필요한 경우는
Model
에 전달
- event handler
View
DOM조작 (View 변경) 에만 집중
- 데이터를 받아 그대로 DOM에 추가
- Add Event Listener
실습
- 할일을 입력하면 ‘해야 할 일들’ 에 노출된다
- Event Delegation를 사용한다 (Document에만 event를 등록한다)
- Template Literal 을 활용한 View용 HTML 데이터 생성
- Model
- 획득
- 일단 태그들을 획득 해오자
- toDoInputTextBox
Event Delegation
하위요소에 각각 이벤트를 붙이지 않고, 상위요소에서 하위요소의 이벤트들을 제어하는 방식
- Child element 각각 이벤트 핸들러를 달지 않고 - Parent Element에 달고 이벤트가 발생한 노드를 필터링 해서 처리함
document.getElementById( 'myTable' ).addEventListener( 'click', function( e ) { if( e.target && e.target.nodeName == 'TD' ) { //you code goes here... } }, false );
<HTML> <table id="myTable"> <tbody> <tr> <td id="myTd">1, 1</td> <td>1, 2</td> </tr> <tr> <td>2, 1</td> <td>2, 2</td> </tr> </tbody> </table>
사용자의 액션에 의해 이벤트 발생 시 이벤트는 Document 레벨까지 버블링되어 올라간다.
왜 사용하는가?
- 새로운 노드들이 추가되는 상황에서 (아이템 객체 등) 해당 노드들에 이벤트 리스너를
일일이
달아야 하는 번거로움이 생김 - 리스트 아이템들이 많아지면 많아질수록 해당 작업 자체가 고문이됨
- 그래서 번거로운 작업들을 처리할 수 있는게
이벤트 위임(Event Delegation)
임
- 새로운 노드들이 추가되는 상황에서 (아이템 객체 등) 해당 노드들에 이벤트 리스너를
Event Bubbling
특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성
- 어떤 태그에서 이벤트가 발생했을 때, 부모 태그로 올라가면서 이벤트를 발생시킴
- 브라우저가 이벤트를 감지하는 방식 때문
- 브라우저는 특정 화면 요소에서 이벤트가 발생했을 때, 그 이벤트를 최상위에 있는 화면요소까지 이벤트를 전파시킴
- 하위에서 상위 요소로의 이벤트 전파 방식을
이벤트 버블링(Event Bubbling)
이라고 함 - Bubbling
Event Capture
이벤트 버블링과 반대로 진행되는 이벤트 전파방식
실제 코드에서는 거의 사용되지 않지만 때로는 유용할 수 있음
DOM 이벤트는 3가지 단계를 거침
- 캡쳐(Capture) 단계 - 이벤트가 요소로 이동함
- 목표(Target) 단계 - 이벤트가 대상 요소에 도달함
- Bubbling phase - 이벤트가 요소에서 튀어 오름 (?)
the event bubbles up from the element
그러나 캡쳐단계는 거의 사용되지 않아서, 일반적으로 알 수 없음
Handler 는 2,3 단계만 이용
캡쳐 단계의 이벤트를 잡으려면 addEventListener 의 3번째 변수를
true
로 설정<script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`)); } </script>
Event stopPropagation
DOM 이벤트에서 진행되는 Capture, Bubbling 을 막아주는 메서드
- 그러나 한 이벤트에 여러 이벤트핸들러가 존재할 때
- 그 중 하나가 버블링을 중지하더라도 다른 이벤트 핸들러는 여전히 실행됨
- 즉,
event.stopPropagation()
메서드를 사용하면 상위로의 이동은 멈추지만 (캡쳐의 경우 하위)- 현재 요소에서 다른 처리기가 존재한다면 실행됨
- 버블링을 멈추고 현재 요소의 핸들러가 실행되지 않도록 하려면
event.stopImmediatePropagation()
을 사용하면 됨
- 이후에는 다른 핸들러가 실행되지 않음
Template literals
내장된 표현식을 허용하는 문자열 리터럴이며, 여러 줄로 이뤄진 문자열과 문자 보간기능(?)을 사용할 수 있음
템플릿 리터럴(Template Literals)은 백틱(` `)을 사용함
플레이스 홀더를 이용하여 표현식을 넣을 수 있음
- $ 와 중괄호를 이용함
$ {expression}
- 함수로 전달됨
- $ 와 중괄호를 이용함
Multi-line string
기존의 Multi-line String
console.log("string text line 1\n"+ "string text line 2);
template literlas
console.log(`string text line 1 string text line 2`);
Expression interpolation (표현식 삽입법)
기존의 표현식(expression)을 문자열에 삽입
var a = 5; var b = 10; console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + "."); // "Fifteen is 15 and // not 20."
template literals
var a = 5; var b = 10; console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`); // "Fiteen is 15 and // note 20."
Nesting templates (이건 뭔지 잘 모르겠다)
// In ES5 var classes = 'header' classes += (isLargeScreen() ? '' : item.isCollapsed ? ' icon-expander' : ' icon-collapser');
// ES2015 에서 중첩(nesting) 없이 템플릿 리터럴 사용한 경우 const classes = `header ${ isLargeScreen() ? '' : (item.isCollapsed ? 'icon-expander' : 'icon-collapser')}`;
// ES2015 에서 중첩된(nested) 템플릿 리터럴을 사용한 경우 const classes = `header ${ isLargeScreen() ? '' : `icon-${item.isCollapsed ? 'expander' : 'collapser'}`}`;
Tagged templates
template literals 에서 더욱 발전한 형태
var person = 'Mike'; var age = 28; function myTag(strings, personExp, ageExp) { var str0 = strings[0]; // "that " var str1 = strings[1]; // " is a " // 사실 이 예제의 string에서 표현식이 두 개 삽입되었으므로 // ${age} 뒤에는 ''인 string이 존재하여 // 기술적으로 strings 배열의 크기는 3이 됩니다. // 하지만 빈 string이므로 무시하겠습니다. // var str2 = strings[2]; var ageStr; if (ageExp > 99){ ageStr = 'centenarian'; } else { ageStr = 'youngster'; } // 심지어 이 함수내에서도 template literal을 반환할 수 있습니다. return str0 + personExp + str1 + ageStr; } var output = myTag`that ${ person } is a ${ age }`; console.log(output); // that Mike is a youngster
- 함수의 첫번 째 인자는 String 배열
- 나머지 인수는 표현식과 관련됨
- 함수는 조작된 문자열을 반환할 수 있음
function template(strings, ...keys) { return (function(...values) { var dict = values[values.length - 1] || {}; var result = [strings[0]]; keys.forEach(function(key, i) { var value = Number.isInteger(key) ? values[key] : dict[key]; result.push(value, strings[i + 1]); }); return result.join(''); }); } var t1Closure = template`${0}${1}${0}!`; t1Closure('Y', 'A'); // "YAY!" var t2Closure = template`${0} ${'foo'}!`; t2Closure('Hello', {foo: 'World'}); // "Hello World!"
Raw String
Escape Sequences 를 처리하지 않고 원시 문자열을 입력한대로 액세스 할 수 있음
역슬래시를 가진 문자열 생성이 필요할 때 유용함
function tag(strings) { console.log(strings.raw[0]); } tag`string text line 1 \n string text line 2`; // logs "string text line 1 \n string text line 2" , // including the two characters '\' and 'n'
함수를 만들지 않아도 String 에
raw
메서드가 존재함String.raw`Hi\n${2+3}!`