할 일 목록처럼 배열에 담긴 데이터 개수만큼 화면에 <li> 줄을 그려야 할 때는 자바스크립트로 새 요소를 만들어 부모 요소에 붙입니다. 이때 쓰는 목록 그리기 패턴은 언제나 1. 목록 비우기 → 2. 하나씩 꺼내 요소 만들기 → 3. 붙이기 세 단계로 이루어집니다.
1요소 하나 만들어 붙이기: createElement와 append
새 HTML 요소를 만들 때는 **document.createElement('태그이름')**를 부르고, 만든 요소를 화면의 부모 요소 끝에 넣을 때는 **부모.append(자식)**를 부릅니다.
'use strict';
const list = document
.querySelector('#todo-list');
const li = document
.createElement('li');
li.textContent = '우유 사기';
list.append(li);
<li>우유 사기</li> 추가됨document.createElement('li')를 부른 직후에li는 아직 메모리 안에만 떠 있어 화면에 보이지 않습니다.list.append(li)로 화면에 있는#todo-list에 붙여야 비로소 눈에 보입니다.append에는 여러 자식 요소를 쉼표로 나란히 넘겨li.append(box, span, btn)처럼 한 번에 순서대로 붙일 수도 있습니다.
2패턴: 목록 그리기 (render 함수)
배열 todos를 화면의 #todo-list로 그리는 함수를 render()로 묶어 봅니다. 세 단계 주석을 눈여겨보세요.
'use strict';
const todos = [
{ id: 1, text: '우유 사기' },
{ id: 2, text: '복습하기' },
{ id: 3, text: '산책하기' },
];
const list = document
.querySelector('#todo-list');
이어서 세 단계로 목록을 그리는 render 함수를 만들고 부릅니다.
function render() {
// 1. 목록 비우기
list.textContent = '';
// 2. 하나씩 꺼내 요소 만들기
for (const t of todos) {
const li = document
.createElement('li');
li.textContent = t.text;
// 3. 붙이기
list.append(li);
}
}
render();
- 우유 사기
- 복습하기
- 산책하기- 1. 목록 비우기 (
list.textContent = '';): 이전에 그려 둔 자식<li>들을 먼저 모두 지웁니다. - 2. 하나씩 꺼내 요소 만들기:
for (const t of todos)로 배열을 돌며 각 항목마다 새<li>를 만들고 글자와 클래스를 채웁니다. - 3. 붙이기 (
list.append(li)): 완성한<li>를 부모list끝에 붙입니다.
3왜 그리기 전에 목록을 먼저 비워야 할까?
만약 render() 첫 줄에서 list.textContent = '';로 비우는 단계를 빼먹고 render()를 두 번 부르면 어떻게 될까요? 배열 동작으로 똑같이 확인해 봅니다.
const items = [
'우유',
'복습',
'산책',
];
const screen = [];
function badRender() {
for (const x of items) {
screen.push(x);
}
}
badRender();
badRender();
console.log(screen.length);
console.log(screen.join(', '));
6
우유, 복습, 산책, 우유, 복습, 산책이전에 그려 둔 3줄 뒤에 새 3줄이 또 붙어서 3개가 6개로 두 배가 됩니다. 반면 첫 줄에서 기존 목록을 먼저 비우면 render()를 몇 번 다시 불러도 언제나 배열 items와 똑같은 3줄만 깔끔하게 그려집니다.
예제
결과를 먼저 생각한 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.
할 일 줄 <li> 안에 <span class="text">와 삭제 단추 <button class="delete"> 두 자식을 만들어 붙이는 코드입니다. 빈칸 없이 실행했을 때 만들어진 li 안의 자식 순서는 어떻게 될까요?
'use strict';
const li = document
.createElement('li');
const span = document
.createElement('span');
span.className = 'text';
span.textContent = '우유 사기';
const btn = document
.createElement('button');
btn.className = 'delete';
btn.textContent = '×';
li.append(span, btn);
결과 보기
<li>
<span>우유 사기</span>
<button>×</button>
</li>li.append(span, btn)은 넘긴 순서대로 span을 먼저 붙이고 그 뒤에 btn을 자식 요소로 붙입니다.
자주 틀리는 곳
render()첫 줄에서list.textContent = ''를 빼먹는다: 할 일을 하나 추가하고render()를 다시 불렀을 때 기존 3줄이 그대로 남은 채 4줄이 더 붙어 7줄이 됩니다. 다시 그리는 함수는 반드시 비우기부터 시작합니다.document.createElement만 부르고append를 안 한다: 요소를 만들고 속성까지 다 채웠더라도 부모 요소에.append()로 붙이지 않으면 화면에는 아무것도 나타나지 않습니다.- 배열
todos.push(...)만 하고render()를 다시 부르지 않는다: 배열에 새 객체를 넣었다고 해서 브라우저가 알아서 새<li>를 만들어 주지 않습니다. 배열을 바꾼 뒤에는 반드시render()를 다시 불러야 화면이 따라 바뀝니다.
확인 문제
목록 그리기패턴의 세 단계를 순서대로 쓰세요.
정답
1. 목록 비우기 (list.textContent = '';) → 2. 하나씩 꺼내 요소 만들기 (document.createElement) → 3. 붙이기 (list.append)입니다.
document.createElement('p')로 문단 요소p를 만들고p.textContent = '할 일이 없습니다.';까지만 실행했을 때 화면에 문단이 보이지 않는 까닭은 무엇이며,main요소 끝에 보이게 하려면 어떤 코드를 더해야 하나요?
정답
createElement로 만든 요소는 아직 메모리 안에만 있고 문서 나무에 붙지 않았기 때문입니다. main.append(p);를 실행해 부모 요소에 붙여야 화면에 보입니다.
todos배열에 항목이 3개 들어 있습니다.list.textContent = '';가 없는render()함수를 세 번 연달아 부르면#todo-list안의<li>는 모두 몇 개가 되나요?
정답
비우지 않고 3개씩 세 번 덧붙이므로 9개가 됩니다.