mount 안에서 render()를 사용할 때 this를 써줘야 한다.
스코프에 의해 그냥 사용할 수 있을 줄 알았는데 왜 그런지 궁금해졌다.
render 함수가 객체 안에 있기 때문에 render는 인스턴스 메소드이다.
인스턴스 메소드는 this를 써야지만 접근이 가능하기 때문에 this를 사용해서 접근해야 한다.
CounterButton 클래스에서 #element는 프라이빗으로 되어 있어 접근이 불가능 해야 하는데
크롬 개발자 모드에선 접근이 가능하다
그래서 vscode 내에서 로그를 찍어봤다. 로그에서 찍었을 땐 구문에러가 발생했다,
왜 그런지 알아봤더니 브라우저는 호환성을 중요시 하기 때문에 이런 현상들을 허용해주는 것이었다.
정리해서 말하자면 크롬은 엄격모드가 아니기 때문에(느슨한 모드) 접근이 가능했던 것이었다.
class CounterButtonComponent extends HTMLElement {
#config = {
count: 0,
step: 1,
};
constructor() {
super();
this.#init();
}
#init() {
const userConfig = {
count: Number(this.getAttribute("count")),
step: Number(this.getAttribute("step")) || 1,
};
this.#config = { ...this.#config, ...userConfig };
}
// 버튼 클릭 이벤트를 처리하고, 카운트 값을 증가시키고 컴포넌트를 다시 렌더링하며, update 이벤트를 발생시킵니다
/*
dispatchEvent()를 사용하여 "update"라는 이름의 커스텀 이벤트를 발생시키고, this.#config.count 값을 이벤트의 detail 속성에 전달
"update" 이벤트를 리스닝하는 다른 요소에서 해당 값을 사용하여 UI나 상태를 업데이트 할 수 있음
*/
#bindEvent(e) {
if (e.target.matches("button")) {
this.#setCount();
this.render();
// 참고: https://developer.mozilla.org/ko/docs/Web/Events/Creating_and_triggering_events
this.dispatchEvent( // DOM 요소에 이벤트를 발생시키기 위해 사용되는 메서드
new CustomEvent("update", { detail: this.#config.count }) // (update라는 이름의 커스텀 이벤트를 생성 , 이벤트에 전달할 추가 정보를 담고 있는 객체 { detail: ... } 형태로 전달)
);
}
}
#setCount() {
const { count, step } = this.#config;
this.#config.count = count + step;
}
connectedCallback() { // 컴포넌트가 DOM에 연결될 때 호출되는 메서드로, 초기 렌더링을 수행하고 클릭 이벤트를 등록
// console.log('connected');
this.render();
this.addEventListener("click", (e) => this.#bindEvent(e));
}
disconnectedCallback() { // 컴포넌트가 DOM에서 연결이 끊어질 때 호출되는 메서드로, 클릭 이벤트를 제거
// console.log('disconnected');
this.removeEventListener("click", (e) => this.#bindEvent(e));
}
render() {
const { count } = this.#config;
this.innerHTML = `<button type="button">${count}</button>`;
}
}
customElements.define("counter-button", CounterButtonComponent);
const counterButtonEl = document.querySelector("counter-button");
/*
counterButtonEl.addEventListener("update", ...): counter-button 요소에 대한 update 이벤트를 처리합니다.
해당 이벤트가 발생하면 카운트 값이 변경된 내용을 .web-component 클래스를 가진 요소의 텍스트로 업데이트합니다.
이 코드를 실행하면 <counter-button> 요소가 등록되어 웹 컴포넌트로 사용됩니다. 이 버튼을 클릭하면 카운트 값이 증가하며, update 이벤트가 발생하여 다른 요소의 텍스트가 업데이트됩니다.
*/
counterButtonEl.addEventListener("update", (e) => { // update -> 사용자 정의 이벤트 사용
document.querySelector('.web-component').textContent = String(e.detail);
});
상태(state)와 변경 가능성(mutable)을 최소화하고, 대신 함수의 조합으로 복잡한 로직을 해결
const dummyDocument = {
body: {
innerHTML: "",
},
};
// ----------------------------------------
// 함수는 하나 이상의 기능을 제공할 수 있습니다.
// - 일반적으로 함수를 작성할 때 여러 일을 처리하도록 구성합니다.
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>
`;
console.log(dummyDocument.body.innerHTML);
})
.catch((error) => console.error(error.message));
}
fetchAndRenderAndLogAlbumList();
한 함수의 여러가지 기능이 있는 함수를 분리해보았다.
function fetchData(endpoint) {
return fetch(endpoint)
.then((response) => response.json())
.catch((error) => console.error(error.message));
}
function renderAlbumList(data, container) {
container.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>
`;
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.querySelector('#demo'));
// 로그
log(container)
}
run();