You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/3-terrarium/3-intro-to-DOM-and-closures/translations
Sanjay cc833ce34f
Added intial files for hindi translation
4 years ago
..
.github Added intial files for hindi translation 4 years ago
README.es.md update broken translations links 4 years ago
README.ko.md FIX : fix url for translations folder 4 years ago
assignment.es.md folder names 4 years ago
assignment.hi.md Added intial files for hindi translation 4 years ago

README.ko.md

Terrarium 프로젝트 파트 3: DOM 조작과 클로저

DOM and a closure

Sketchnote by Tomomi Imura

강의 전 퀴즈

Pre-lecture quiz

소개

DOM 또는 "Document Object Model"을 조작하는 것은 웹 개발의 핵심입니다. MDN에 따르면, "The Document Object Model (DOM) is the data representation of the objects that comprise the structure and content of a document on the web." 이라고 합니다. 웹의 DOM 조작과 관련하여 간혹 DOM을 관리하기 위해 순수 JavaScript 대신 JavaScript 프레임워크를 도전하게 되는 원동력이지만, 우리는 스스로 관리할 것입니다!

추가로, 이 강의에서는 JavaScript closure에 대한 아이디어를 소개합니다. 내부 함수가 외부 함수의 범위에 접근할 수 있도록 합니다.

DOM을 조작하기 위해 클로저를 사용할 예정입니다.

DOM을 웹 페이지 문서를 제어할 수 있는 모든 방법으로 나타낼 수 있는 트리로 생각하십시오. 프로그래머가 선택한 프로그래밍 언어를 사용하여 DOM에 접근하고 편집, 변경, 재배치하며 관리할 수 있는 다양한 API(Application Program Interfaces)가 작성되어 있습니다.

DOM tree representation

DOM과 참조하는 HTML 마크업의 표현입니다. From Olfa Nasraoui

이 강의에서는, 사용자가 페이지에서 식물을 조작할 수 있는 JavaScript를 작성하여 대화식 terrarium 프로젝트를 완료합니다.

준비물

terrarium에 대한 HTML과 CSS를 작성해두어야 합니다. 이 강의가 끝나면 식물을 드래그하여 terrarium에서 이동할 수 있습니다.

작업

terrarium 폴더에서, script.js라고 불리는 파일을 만듭니다. 파일의 <head> 부분에 넣습니다:

	<script src="./script.js" defer></script>

Note: 외부 JavaScript 파일을 html 파일로 가져올 때 defer 를 사용하여 HTML 파일이 완전히 불러질 때만 JavaScript가 실행되도록 합니다. HTML 파일이 파싱되는 동안 스크립트를 실행할 수 있는 async 속성을 사용할 수 있지만, 우리는 드래그 스크립트를 실행하기 전에 HTML 요소를 완전히 드래그할 수 있어야 한다는 점이 중요합니다.


DOM 요소

가장 먼저 할 일은 DOM에서 조작하려는 요소에 대한 참조를 만드는 것입니다. 우리의 경우, 현재 사이드바에서 대기하고 있는 14개의 식물입니다.

작업

dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));

여기서 어떤 일이 일어나고 있나요? 특정 Id의 요소를 찾기 위해 문서를 참조하려고 해당 DOM을 봅니다. HTML에 대한 첫번째 강의에서 각 식물 이미지(id="plant1")에 개별 Id를 준 것을 기억하시나요? 이제 그 노력을 쓰겠습니다. 각 요소를 식별한 후, 1분 안에 작성될 dragElement라고 불리는 함수에 해당 아이템을 전달합니다. 따라서 HTML의 요소는 이제 드래그를 할 수 있거나 곧 합니다.

왜 우리는 Id로 요소를 참조하나요? CSS 클래스가 아닌 이유는 무엇일까요? 이 질문에 답하기 위해 CSS에 대한 전 강의를 참조할 수 있습니다.


클로저

이제 내부 함수를 감싸는 외부 함수인 dragElement 클로저를 만들 준비가 되었습니다(이 경우에는, 3개가 있습니다).

클로저는 하나 이상의 함수가 외부 함수의 범위로 접근하는 순간 유용합니다. 예시는 다음과 같습니다:

function displayCandy(){
	let candy = ['jellybeans'];
	function addCandy(candyType) {
		candy.push(candyType)
	}
	addCandy('gumdrops');
}
displayCandy();
console.log(candy)

이 예제에서, displayCandy 함수는 새 캔디 타입을 이미 존재하는 배열로 푸시하는 함수를 감쌉니다. 이 코드를 실행한다면, candy 배열은 지역 변수(클로저 로컬)이므로 정의되지 않습니다.

candy 배열에 어떻게 접근할 수 있습니까? 클로저 밖으로 이동해보세요. 이 방식은 배열이 클로저의 로컬뿐만 아니라, 전역 범위에서도 사용할 수 있습니다.

작업

script.js의 요소 선언 아래, 함수를 만듭니다:

function dragElement(terrariumElement) {
	//set 4 positions for positioning on the screen
	let pos1 = 0,
		pos2 = 0,
		pos3 = 0,
		pos4 = 0;
	terrariumElement.onpointerdown = pointerDrag;
}

dragElement는 스크립트 상단의 선언에서 terrariumElement 객체를 가져옵니다. 그러고, 함수에 전달된 객체의 일부 로컬 위치를 0으로 설정합니다. 클로저 안에서 드래그 앤 드롭 기능을 각 요소에 추가할 때 각 요소에 대해 조작될 지역 변수입니다. terrarium에 드래그된 요소로 채워지므로, 애플리케이션은 배치된 위치를 추적해야 합니다.

추가로, 이 함수에 전달되는 terrariumElement에는 DOM 관리에 도움이 되도록 설계된 web APIs의 일부인 pointerdown 이벤트가 할당됩니다. onpointerdown은 버튼이 눌리거나 드래그 하는 요소가 터치될 때 발생합니다. 이 이벤트 핸들러는 몇 가지 예외를 제외하고, 웹과 모바일 브라우저 다 작동합니다.

event handler onclick은 크로스 브라우저 지원이 훨씬 더 많습니다. 왜 여기서 사용하지 않나요? 여기에서 만들고자 하는 정확한 화면 상호작용 타입에 대해 생각해보세요.


Pointerdrag 함수

terrariumElement를 드래그할 준비가 되었습니다; onpointerdown 이벤트가 시작되면, pointerDrag 함수가 호출됩니다. 줄 바로 아래에 해당 함수를 추가하시기 바랍니다: terrariumElement.onpointerdown = pointerDrag;:

작업

function pointerDrag(e) {
	e.preventDefault();
	console.log(e);
	pos3 = e.clientX;
	pos4 = e.clientY;
}

몇 가지 일이 발생합니다. 처음으로, e.preventDefault();를 사용하여 포인터 다운 시 일상적으로 발생하는 기본 이벤트가 발생하지 않도록 합니다. 이러면 인터페이스의 동작을 더 잘 제어할 수 있습니다.

스크립트 파일을 완전히 작성했으면 이 줄로 돌아와서 e.preventDefault() 없이 시도해보세요. - 어떻게 되나요?

두 번째로는, 브라우저 창에서 index.html을 열고, 인터페이스를 검사합니다. 식물을 클릭하면, 'e' 이벤트가 캡처되는 방식을 볼 수 있습니다. 이벤트를 뜯어보면서 한 번의 포인터 다운 이벤트로 얼마나 많은 정보가 수집되는지 확인해보시기 바랍니다!

다음으로 지역 변수 'pos3'과 'pos4'가 어떻게 e.clientX 로 설정되어 있는 지 확인합니다. 검사 창에서 e 값을 찾을 수 있습니다. 이 값은 식물을 클릭하거나 터치하는 순간 식물의 x 와 y 좌표를 캡처합니다. 식물을 클릭하고 드래그할 때 식물의 동작을 세밀하게 제어하여 좌표를 추적해야 합니다.

하나의 앱이 하나의 큰 클로저로 만들어진 이유가 더 분명해지나요? 아니라면, 14개의 드래그 가능한 식물을 어떻게 각각의 범위를 지킬까요?

pos4 = e.clientY 아래에 포인터 이벤트 조작을 2개 더 추가하여 함수 초기화를 완료합니다:

document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;

제 식물을 이동할 때 포인터와 함께 식물을 끌고, 식물 선택을 취소할 때 드래그 제스처를 중지하도록 지정합니다. onpointermoveonpointerup은 모두 onpointerdown과 동일한 API의 일부입니다. 아직 elementDragstopElementDrag 함수를 정의하지 않아 인터페이스에서 오류가 발생하므로, 다음에 작성하십시오.

elementDrag와 stopElementDrag 함수

식물을 드래그하고 멈출 때 일어나는 일을 처리하는 두 내부 함수를 더 추가하여 클로저를 완료합니다. 원하는 동작은 언제든지 식물을 드래그하여 화면의 아무 곳에 둘 수 있다는 것입니다. 이 인터페이스는 식물을 추가, 제거 그리고 재배치하여 원하는대로 terrarium을 정확하게 디자인할 수 있도록 (예를 들어 drop zone이 없음)하는 것에 의견이 없습니다.

작업

pointerDrag의 닫는 중괄호 바로 뒤에 elementDrag 함수를 추가합니다:

function elementDrag(e) {
	pos1 = pos3 - e.clientX;
	pos2 = pos4 - e.clientY;
	pos3 = e.clientX;
	pos4 = e.clientY;
	console.log(pos1, pos2, pos3, pos4);
	terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
	terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
}

이 함수에서는, 외부 함수에서 로컬 변수로 설정한 초기 위치 1-4를 많이 편집합니다. 여기서 어떤 일이 일어나고 있습니까?

드래그할 때, pos1을 현재 e.clientX 값을 뺀 pos3(이전에e.clientX로 설정)과 동일하게 만들어 pos1을 다시 할당합니다. pos2와 유사한 작업을 수행합니다. 그런 뒤에, pos3pos4를 요소의 새로운 X 와 Y 좌표로 다시 설정합니다. 드래그하면 콘솔에서 변경 사항을 볼 수 있습니다. 그런 뒤에, 식물의 css 스타일을 조작하여 pos1pos2의 새로운 위치를 기반으로 위치를 설정하고, 오프셋을 이러한 새 위치와 비교하여 식물의 위쪽과 왼쪽 XY 좌표를 계산합니다.

offsetTopoffsetLeft는 상위 위치를 기준으로 요소의 위치를 설정하는 CSS 속성입니다. 상위는 static으로 두지 않은 모든 요소가 될 수 있습니다.

모든 위치 다시 계산하며 terrarium과 그 식물의 움직임을 미세하세 조정할 수 있습니다.

작업

인터페이스를 완성하기 위한 마지막 작업은 elementDrag의 닫는 중괄호 뒤에 closeElementDrag 함수를 추가하는 것입니다:

function stopElementDrag() {
	document.onpointerup = null;
	document.onpointermove = null;
}

이 작은 함수는 onpointeruponpointermove 이벤트를 다시 설정하므로 다시 드래그하여 식물의 진행 상황을 다시 시작하거나, 새 식물을 드래그할 수 있습니다.

이러한 이벤트를 null로 설정하지 않으면 어떻게 될까요?

이제 프로젝트를 완료했습니다!

🥇 축하합니다! 아름다운 terrarium을 완성했습니다. finished terrarium


🚀 도전

클로저에 새로운 이벤트 핸들러를 추가하여 식물에 더 많은 일을 시킵니다. 예를 들어, 식물을 더블 클릭하여 앞으로 가져옵니다. 창의력을 발휘하십시오!

강의 후 퀴즈

Post-lecture quiz

리뷰 & 자기주도 학습

화면에서 요소를 드래그하는 것은 사소한 것처럼 보이지만, 원하는 효과에 따라 여러 가지 방식과 함정이 있습니다. 실제로, 시도할 수 있는 전체 drag and drop API가 있습니다. 우리가 원하는 효과가 다소 다르기 때문에 이 모듈에서는 사용하지 않았지만, 그러나 이 API를 자신의 프로젝트에서 시도해보고 얻을 수 있는 게 무엇인지 확인하시기 바랍니다.

포인터 이벤트에 대한 자세한 내용은 W3C docsMDN web docs에서 확인하세요.

항상 CanIUse.com으로 브라우저 기능을 확인하십시오.

과제

Work a bit more with the DOM