자바스크립트의 객체지향(OOP)는 OOJS라고 불리기도 한다.
new CountUpButton({ count: 3 })
를 전달해 class 내부 계산에 의해 값을 수정할 수 있지만, 사용자가 직접 class 내부의 값을 수정할 수는 없다.count: props.count ?? 0
nulish 병합 연산자
: props
로 전달된 객체에 count
라는 property가 있다면(truthy) 그 값을 사용하고 아니라면(falthy) 0을 사용.
a ?? b
:a
가null
도 아니고undefined
도 아니면a
, 그 외의 경우는b
render()
내부에서 button
을 생성하고, 생성된 button
을 return
return
으로 반환된 button
을 mount(container)
에서 전달받은 container
에 append
, container?.append?.(this.render())
?.
obj?.prop
– obj
가 존재하면 obj.prop
을 반환하고, 그렇지 않으면 undefined
를 반환함obj?.[prop]
– obj
가 존재하면 obj[prop]
을 반환하고, 그렇지 않으면 undefined
를 반환함obj?.method()
– obj
가 존재하면 obj.method()
를 호출하고, 그렇지 않으면 undefined
를 반환함
inserAdjacentHtml
를 사용하는 방법
위의 코드에서inserAdjacentHtml
대신append
를 사용한 이유는 위에서 생성한button
이 HTMLElement Node이기 때문이다.
그렇다면inserAdjacentHtml
를 사용해 버튼을 생성하는 방법은?
class에 의해 생성된 인스턴스에 전달된 매개변수는 class내부에서 계산된다.
static defaultProps = {
count: 0,
step: 1,
max: 10,
};
이때 class의 static field의 defaultProps를 ... spread syntax
을 통해 전개하고, 사용자가 전달한 props
또한 전개해 새로운 객체로 생성한다.
이때 겹치는 propert가 존재한다면 뒤에 오는 property의 값으로 덮어 써진다.
render()
가 실행되면서 생성된 button
요소에 이벤트를 생성하고 싶다면, bindEvents()
함수를 생성해 이벤트를 실행시키면 된다.bindEvents()
에서는 button
에 접근할 수 없는데, private field에 #button
을 등록해 render()
가 실행되면서 button
이 #button
에 할당해 bindEvents()
가 #button
즉, button
에 접근 가능하도록 한다.bindEvents()
는 render()
가 실행되면서 실행되도록 하면서 이벤트의 주체는 this.#button
이 되도록 한다.웹 컴포넌트는 개발자가 직접 정의한 요소를 뜻하고, 이를 이용해 재사용 가능하게 만든다.
사용자가 정의한 <count-up-button>
사용하기
1. class
생성 및 customElements
정의
customElements
를 정의하는데 첫 번째 인자인 이름은 반드시 케밥 케이스로 작성해야하고, extends
HTMLElement
를 상속하여 새로운 사용자 정의 요소를 생성super()
HTMLElement
클래스의 생성자가 호출되고, 해당 클래스의 속성 및 메소드를 CountComponent
클래스에서 사용할 수 있도록 해준다.extends
로 상속받기 위해서는 super()
를 반드시 사용해야한다.CountComponent
생성자로 인해 <button>
이 출력되고.CountComponent
class
는 특정 HTML 구조에 해당하는 사용자 정의 HTMLElement
를 만든다.detail
프로퍼티로만 전달할 수 있으므로 데이터가 꼬일 일이 없다.detail
프로퍼티로 구분할 수 있다.mousedown → mouseup → click
순서로 이벤트가 처리되기 때문에 mousedown
이벤트 핸들러가 끝나기 전에는 click 이벤트 핸들러를 호출할 수 없다.`syntheticEvent이란?
- 함수형 프로그래밍을 사용하는 이유
payload - 전송되는 데이터
function fetchAndRenderAndLogAlbumList() {
fetch("https://jsonplaceholder.typicode.com/album/1/photos?_start=0&_limit=4")
.then((response) => response.json())
.then((data) => {
dummyDocument.body.innerHTML = `
<ul class="albumList">
${data
.map(
({ albumId, id, title, url, thumbnailUrl }) =>
`
<li class="albumItem">
<a class="albumLink" href="${url}">
<img class="albumThumbnail" src="${thumbnailUrl}" alt="" />
<div role="group" class="albumInfo">
<strong class="albumTitle">${title}</strong>
<span class="albumId">${albumId}</span>
</div>
</a>
</li>
`
)
.join("")}
</ul>
`;
})
.catch((error) => console.error(error.message));
}
fetchAndRenderAndLogAlbumList();
위의 코드를 하나의 함수가 하나의 기능에 집중하게 구성하기
// 데이터 패치(가져오기)
function fetchData(endpoint) {
// Promise
return fetch(endpoint)
.then((response) => response.json())
.catch((error) => console.error(error.message));
}
// 데이터 기반 렌더링
function renderAlbumList(data, container) {
container.innerHTML = `
<ul class="albumList">
${data
// list rendering
// render lists
.map(
({ albumId, id, title, url, thumbnailUrl }) =>
`
<li class="albumItem">
<a class="albumLink" href="${url}">
<img class="albumThumbnail" src="${thumbnailUrl}" alt="" />
<div role="group" class="albumInfo">
<strong class="albumTitle">${title}</strong>
<span class="albumId">${albumId}</span>
</div>
</a>
</li>
`
)
.join("")}
</ul>
`;
return container;
}
// 로그
function log(container) {
console.log(container.outerHTML);
}
// 함수 실행
async function run() {
// 데이터 패치(가져오기)
const responseData = await fetchData(
"https://jsonplaceholder.typicode.com/album/1/photos?_start=0&_limit=4"
);
// 데이터 기반 렌더링
const container = renderAlbumList(
responseData,
document.getElementById("demo")
); // as HTMLDivElement, TS에서 요소로 변경하는 방법
// 로그
log(container);
}
run();