You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
3번 문제: sort() 안에 넣을 콜백함수 인수도 a, b로 하지 말고 todo1, todo2 등으로 명명해주면 훨씬 이해하기 쉬운 코드를 작성할 수 있다.
5번 문제: 사용자는 기대하는 타입의 값을 넣지 않는다. 숫자를 받아서 해당 숫자를 id로 갖는 객체에 작업하려고 하는데 만약 문자열로 데이터가 들어올 경우 비교연산자의 매개변수로 들어오는 식별자(e.f. todo.id === id)에 +로 숫자 타입캐스팅을 해주면 보다 안전하게 만들 수 있다.
7번 문제: 화살표함수의 리턴값을 객체로 줄 때, 그리고 화살표함수의 인수를 객체 디스트럭쳐링 할당으로 줄 때 JS엔진은 {}가 코드블럭인지 객체인지 판단하지 못해 오류를 일으킨다. 그러므로 꼭 ({})의 형태로 작업할 것.
9번 문제: Math.mas()의 인수에 아무것도 전달되지 않으면 -Infinity를 리턴하므로, 만약 배열을 풀어 넣어 인수로 주고 싶으면 0을 같이 주거나 인수에 삼항조건연산문으로 todos.length? todos.map(todo => todo.id) : 0을 넣어줄 것
식별자가 올 자리에 배열, 객체 리터럴을 넣고 그 안에 위치한 값의 자리에 식별자를 넣으면 해당하는 값이 식별자에 할당된다.
// 편의상 각 줄은 서로다른 파일이라 생각하고 중복선언을 피하는 걸로 생각한다.// 편의상 주석은 할당문이 아닌 식별자가 가진 값을 가리키는 것으로 생각한다.const[a,b]=[1,2];// a = 1, b = 2const[b,a]=[2,1];// a = 2, b = 1const[a]=[1,2];// a = 1const[a,b,c]=[1,2];// a = 1, b = 2, c = undefinedconst[a,b,c]=[1,,3];// a = 1, b = undefined, c = 3
주목할만한 흥미로운 점: const 키워드는 원래 선언과 초기화가 필수인데 디스트럭쳐링 할당에서는 초기값이 할당되지 않으면 에러가 아닌 undefined가 할당되는 현상이 일어난다.
객체 디스트럭쳐링 할당
배열은 순서를 기준으로 할당했던 반면, 객체는 key를 기준으로 값을 할당한다.
const{ a, b }={a : 1,b : 2};// a = 1, b = 2const{ b, a }={a : 1,b : 2};// a = 1, b = 2
인수가 객체인 경우에는 매개변수에 디스트럭쳐링 할당을 하면 식별자에 인수의 객체 내 프로퍼티 값을 바로 넣어줄 수 있다.
DOM (Document Object Model)
브라우저의 역할
브라우저는 내 컴퓨터에서 동작하므로, 서버에 있는 리소스를 요청하여 받아와야 하는데, 이 때 서버에서 리소스는 랜선을 타고 날아온다.
브라우저의 역할은 다음과 같다.
주소창을 통해 서버에 리소스를 요청한다. (= request)
요청하여 전달받은 리소스로, HTML과 CSS를 재료로 하여 브라우저 창을 색칠한다. (= rendering)
HTML 마크업 작업은 내 컴퓨터에서 하더라도 결국 서버에 갖다놔야 하는데, 이를 배포라고 한다.
브라우저의 렌더링 과정
주소창에 naver.com을 치면 네이버의 수천대의 서버 중 메인 페이지 리소스를 가지고 있는 하나의 서버로 요청이 간다.
클라이언트가 찾아가서 접속을 해야 통신을 통해 리소스를 받을 수 있다.
서버에는 랜카드라는 것이 있는데 랜카드의 일련번호는 naver.com이라는 주소와 매핑되어 있다.
매핑만 관리하는 서버를 DNS(Domain Name System)이라고 하는데, 주소창에 URL을 입력하면 URL에 입력된 문자열 즉 호스트 이름이 DNS를 통해 변환되어 해당 주소를 갖는 서버에게 요청을 전송한다.
그냥 naver.com만 쓰면 기본적으로 루트 요청을 한 것으로 간주해 index.html을 보내준다.
index.html 의 html 태그에서도 또다른 요청을 보낸다. (e.g. img src에서는 이미지 소스를 요청)
그러나 위와 같은 요청들은 html에 종속되어 있으며 html이 왕초이다.
HTML 파싱과 DOM 생성
HTML은 사람이 읽을 수 있도록 만들어진 문자열인데, 이를 브라우저가 이해하려면 각 문자를 해석해야 한다. 그 해석을 parsing이라고 하며 parsing의 결과물이 DOM.
HTML 태그로 시작하고 닫히며 html 태그는 단 두 개의 자식요소(head, body)를 갖는다.
html 파일을 parsing하면 DOM이라는 자료구조가 생성되는데, 부모자식관계로 각 요소노드가 표현되는 Tree 자료구조이다.
<!DOCTYPE> // 이것은 요소로 치지 않는다.
<html><head></head><body><ul><li>Apple</li><li>Banana</li><li>Orange</li></ul></body></html>
위의 html은 루트인 document 밑 html 아래 head, body, 그리고 body 밑 ul, ul 밑 li들, li 밑에 각각의 텍스트 노드를 갖는다.
document: 루트이며 진입점이다. document를 통해서만 이 DOM tree 자료구조에 들어올 수 있다. DOM TREE를 조작하려면 무조건 document.querySelector() 등 document를 통해야 한다.
루트 외에 중간이나 끝에 있는 요소들을 node라고 부른다.
JS를 통해 DOM을 조작하는 순간 repaint가 된다.
JS가 하는 일은 결국 HTML로 만들어진 DOM Tree를 조작하여 정적인 화면을 동적으로 만드는 것이다
const$apple=document.getElementById('apple');// 요소노드임을 명확히 하기 위해 식별자에 달러 사인을 붙인다.$apple.textContent='Grape'// HTML 텍스트를 조작$apple.style.color='red'// CSS 스타일링을 조작
HTML 요소 조작하기
html 마크업 태그는 HTML 요소라고 부르며, 이를 parsing한 결과로 생성된 것은 HTML 요소 노드 객체라고 부르자.
HTML 요소 참조
querySelector({CSS 선택자와 동일한 이름}): 처음 참조된 값만 가져온다.
태그명으로 요소를 가져오는 것은 위험하기 때문에 좋은 방법이 아니다.
id로 가지고 올 때는 getElementById({id})를 사용한다.
CSS에서는 케밥케이스를 쓰지만 JS에서 스타일을 조작할 땐 같은 내용을 카멜케이스로 바꿔서 사용한다. (background-color => backgroundColor)
class 이름으로 탐색된 첫 요소를 가지고 올 때는 querySelector에 CSS처럼 클래스명 앞에 .을 붙인다.
class 이름으로 탐색된 모든 요소를 가져오려면 querySelectorAll을 쓰는데, 이 프로퍼티의 값은 Nodelist라는 자료구조의 형태로 리턴된다. Nodelist는 유사배열객체이며 iterable이다.
배열로 변환해서 배열메서드를 쓰는 것이 편하다. [ ... Nodelist ] 또는 [ ... HTMLCollection ]으로 만들어 배열메서드로 각 요소를 순회하며 작업할 것.
위와 같은 html에서 ul의 클래스명인 'fruits'를 통해 document.querySelector('.fruits').children 또는 document.querySelector('.fruits').childNodes로 자식요소를 가져올 수 있다.
document.querySelector('.fruits').children: HTMLCollection이라는 유사배열객체의 자료구조로 자식요소 정보를 준다.
document.querySelector('.fruits').childNodes: 엔터와 스페이스바로 입력된 모든 공백텍스트노드까지 가져온다. 공백을 가지고 작업할 일은 없으니 사용을 지양해야.
동일한 문제가 firstElementChild와 firstChild, lastElementChild와 lastChild에도 발생한다. 전자는 공백 텍스트노드를 제외한 첫 요소, 마지막요소가 취득되고, 후자는 공백텍스트노드가 취득된다.
첫째/막내 외의 중간 자식들에 접근하는 방법은 첫째/막내를 통하는 방법밖에는 없다. 위와 동일한 이슈를 갖는 previousElementSibling, previousSibling, nextElementSibling, nextSibling을 사용해 탐색하여 취득할 수 있다.
부모노드 또한 traversing이 가능하다. parentNode라는 프로퍼티로 취득할 수 있는데, 부모요소는 절대 텍스트노드가 될 수 없으므로 무조건 요소노드객체가 취득된다.
querySelectorAll vs. 부모요소에서 children
querySelectorAll: 클래스명이 같은 모든 요소에 작업이 되기 때문에 위험한 방법.
부모요소에서 접근: 코드 자체에서 내가 작업하려는 범위를 알 수 있으며 그 범위 안의 요소들에게만 작업이 되기 때문에 안전하다.
콘텐츠 바꾸기
textContent: 클래스명을 사용하여 참조한 요소 노드 객체의 textContent를 재할당하면 HTML 마크업을 넣어줘도 문자열로 인식해서 text처럼 브라우저에 렌더링된다.
innerHTML: 요소 안의 콘텐츠영역에 유효한 HTML 마크업을 넣어주려면 innerHTML 프로퍼티 값을 재할당(갱신) 해주면 parsing되면서 DOM 요소 노드객체로 만들어진 후 텍스트가 들어온다.
이는 매우 편리하지만 innerHTML을 통해 스크립트 코드가 들어올 경우 XSS 공격이라고도 불리는 JS를 통한 조작과 실행이 이뤄질 수 있다.
사용자가 입력하는 데이터는 untrusted data이므로 아예 입력할 수 없게 하는 게 원칙이지만, 그렇게 할지라도 살균해주는 library를 사용하여 innerHTML로 들어오는 텍스트 속 스크립트를 모두 제거해줘야 한다.
Attribute 조작
getElementById로 취득한 요소에 id를 마침표연산자로 참조하면 해당 태그의 id값이 나온다.
querySelector로 취득한 요소에 class를 동일하게 참조하면 나오지 않는다. class는 JS의 예약어이기 때문에 메서드명으로 사용하지 않으므로 다음 두 메서드로 참조해야만 한다.
className: class라는 어트리뷰트가 가진 문자열을 그대로 알려주는데, 여러 개의 class 있을 경우에도 공백과 함께 문자열 1개가 온다.
classList: 해당 요소가 가진 모든 클래스명을 객체로 리턴
classList 메서드
classList.contains: 인수로 넣은 클래스명이 있는지 확인
classList.add: 인수로 넣은 클래스명을 더할 수 있다.
classList.remove: 인수로 넣은 클래스명을 삭제할 수 있다.
classList.toggle: 인수로 넣은 클래스명이 있으면 삭제, 없으면 추가
느낀 점
드디어 DOM을 통해 HTML과 CSS를 조작하는 JavaScript의 진정한 파워를 살짝 엿보았다. HTML 배울 때부터 javascript는 DOM을 통해 이 모든걸 조작할 수 있다는 이야기를 누누히 들어왔는데, 이걸 직접 눈으로 보고 해보니 과장을 조금 보태자면 온 몸에 전율이 돈다.