HTML5웹프로그래밍 14강 - HTML API: 미디어 제어, 드래그 앤 드롭
audio와 video가 제공하는 공통 미디어 API를 이용해 재생 위치·음량·배속을 제어하고 이벤트에 맞춰 사용자 인터페이스를 동기화하는 방법을 익힌다. 이어 HTML5 Drag and Drop API의 이벤트 흐름과 dataTransfer 객체를 이해하고 텍스트 이동, 파일 드롭, 칸반 보드를 구현하는 원리를 학습한다.
제1장 HTML API와 미디어 요소
1. 대표적인 HTML API
웹 브라우저는 HTML 문서 표시를 넘어 저장, 통신, 화면 관찰, 파일 처리, 위치 정보, 기록 제어, 실시간 통신, 그래픽 등 다양한 기능을 API로 제공한다. Web Storage API는 localStorage와 sessionStorage에 데이터를 저장하고, Fetch API는 서버와 데이터를 주고받으며, Intersection Observer API는 요소가 뷰포트에 들어왔는지를 관찰한다.
File API와 Drag and Drop API는 로컬 파일을 읽거나 끌어 놓는 인터페이스를 만들고, Geolocation API는 위도와 경도를 가져온다. History API는 페이지 전환 없는 세션 기록 제어, WebSocket API는 채팅과 시세표 같은 실시간 양방향 통신, Canvas API는 그래픽과 데이터 시각화에 사용된다.
2. audio와 video의 공통 기반
HTML5의 <audio>와 <video>는 모두 HTMLMediaElement 인터페이스를 상속한다. 따라서 재생, 일시 정지, 음량, 현재 시간, 배속, 상태 이벤트 등 자바스크립트 제어 API가 거의 같다. video에는 원본 해상도를 읽는 videoWidth와 videoHeight 같은 영상 전용 속성이 추가된다.
<video controls preload="metadata" autoplay muted
poster="thumbnail.jpg" playsinline>
<source src="movie.webm" type="video/webm">
<source src="movie.mp4" type="video/mp4">
동영상을 지원하지 않는 브라우저입니다.
</video>
3. 미디어 요소의 주요 HTML 속성
| 속성 | 역할 | 주의점 |
|---|---|---|
| src | 미디어 파일 경로 | source 요소를 여러 개 두어 형식별 대체 파일을 제공할 수 있다. |
| controls | 브라우저 기본 재생 UI 표시 | 브라우저마다 디자인이 다르다. |
| preload="metadata" | 길이·해상도 등 메타데이터 우선 로드 | 초기 네트워크 부담을 줄이면서 duration 등을 확보한다. |
| autoplay | 자동 재생 요청 | 최신 브라우저는 보통 음소거되지 않은 자동 재생을 차단한다. |
| muted, loop | 음소거, 반복 재생 | 자동 재생은 muted와 조합하는 경우가 많다. |
| poster | 재생 전 비디오 대표 이미지 | 로딩 전 첫 화면의 품질을 높인다. |
| playsinline | 모바일에서 페이지 안 재생 유도 | 비디오 전용 속성이다. |
자동 재생 정책을 고려한 실무 기본은 muted 상태이거나 사용자 클릭 뒤 play()를 호출하는 방식이다. autoplay 속성만으로 재생 성공을 가정해서는 안 된다.
제2장 미디어 속성·메서드·이벤트
1. 상태를 읽고 쓰는 속성
| 속성 | 의미 | 읽기·쓰기 |
|---|---|---|
| src | 미디어 파일 경로 | 읽기/쓰기 |
| currentTime | 현재 재생 위치, 초 단위 | 대입하면 해당 시간으로 탐색 |
| duration | 전체 길이, 초 단위 | 읽기 전용, 메타데이터 전에는 NaN 가능 |
| paused | 일시 정지 여부 | 읽기 전용 불리언 |
| volume | 음량 | 0.0부터 1.0 |
| muted | 음소거 여부 | true/false 읽기·쓰기 |
| playbackRate | 재생 배속 | 1.0이 기본 속도 |
| videoWidth, videoHeight | 원본 영상의 픽셀 크기 | video 전용 읽기 전용 |
video.currentTime = 60;은 재생 위치를 60초로 이동한다. duration은 메타데이터가 준비되기 전에 NaN일 수 있으므로 loadedmetadata 이후에 읽는 것이 안전하다.
2. 동작을 제어하는 메서드
play()는 재생을 시작하고 성공 또는 실패를 나타내는 Promise를 반환한다. 자동 재생 차단이나 리소스 오류로 거부될 수 있으므로 catch로 실패를 처리한다. pause()는 현재 위치에서 일시 정지하고, load()는 미디어 요소를 초기화해 파일을 다시 불러온다.
video.play()
.then(() => { /* 재생 성공 UI 갱신 */ })
.catch(error => { /* 자동 재생 차단 등 처리 */ });
HTMLMediaElement에는 stop() 메서드가 없다. 정지 기능은 pause()를 호출한 뒤 currentTime = 0으로 되돌려 구현한다. 자바스크립트로 src를 바꾼 뒤에는 load() 호출이 바람직하다.
3. 상태 변화 이벤트
| 이벤트 | 발생 시점 | 대표 UI 처리 |
|---|---|---|
| loadedmetadata | 길이와 해상도 등 메타데이터 로드 완료 | 전체 시간과 진행 바 최댓값 설정 |
| timeupdate | 재생 중 currentTime이 주기적으로 변경 | 진행 바와 현재 시간 갱신 |
| ended | 미디어가 끝까지 재생됨 | 다음 항목 또는 다시 재생 UI 표시 |
| play, pause | 재생 시작, 일시 정지 | 버튼 텍스트·아이콘 동기화 |
| waiting | 데이터 부족으로 재생 대기 | 로딩 스피너 표시 |
| playing | 재생 시작 또는 버퍼링 후 재개 | 로딩 스피너 숨김 |
| volumechange | volume 또는 muted 변화 | 볼륨 슬라이더와 음소거 버튼 동기화 |
| ratechange | playbackRate 변화 | 배속 선택 UI 동기화 |
상태 변경을 일으킨 코드 안에서만 UI를 바꾸기보다 해당 미디어 이벤트에서 UI를 갱신하면 키보드, 기본 컨트롤, 다른 코드 등 어떤 경로로 상태가 변해도 실제 미디어 상태와 화면을 일치시킬 수 있다.
제3장 커스텀 미디어 플레이어
1. 기본 구조와 접근성
기본 controls의 디자인은 브라우저마다 다르므로 통일된 UI가 필요하면 controls를 숨기고 재생 버튼, 시간 표시, 진행률 바, 음소거 버튼, 볼륨 슬라이더 등을 직접 연결한다. 이때 커스텀 버튼과 슬라이더에는 aria-label을 제공하여 스크린 리더가 기능을 이해할 수 있게 한다.
2. 재생과 버튼 동기화
재생 버튼을 클릭하면 paused를 확인한다. true이면 play()를 호출하고 Promise 거부를 처리하며, false이면 pause()를 호출한다. 버튼 텍스트는 클릭 핸들러에서 추측하기보다 video의 play와 pause 이벤트에서 각각 ‘일시정지’와 ‘재생’으로 바꾸는 편이 정확하다.
3. 시간과 진행률
loadedmetadata에서 progressBar.max를 duration으로 지정하고, timeupdate에서 value를 currentTime으로 갱신한다. 사용자가 range 입력을 움직이는 input 이벤트에서는 currentTime에 슬라이더 값을 대입해 탐색한다. 초 단위 값은 Math.floor와 padStart를 이용해 분:초 형식으로 표시할 수 있다.
loadedmetadata: 전체 길이와 max 준비
timeupdate: 현재 시간과 value 갱신
range의 input: currentTime 변경
4. 음량과 음소거
볼륨 슬라이더 값은 0부터 1 사이이며 input 이벤트에서 video.volume에 대입한다. 사용자가 슬라이더를 올렸는데 muted가 true이면 false로 바꿀 수 있다. 음소거 버튼은 video.muted = !video.muted로 토글한다.
버튼 텍스트와 슬라이더 위치는 volumechange에서 함께 동기화한다. muted이거나 volume이 0이면 ‘소리 켬’을 표시하고 슬라이더를 0으로 맞추며, 그렇지 않으면 ‘음소거’를 표시하고 실제 volume을 반영한다.
5. 버퍼링과 재생 준비
네트워크 데이터가 부족해 재생이 강제로 멈추면 waiting이 발생하므로 로딩 스피너 활성 클래스를 추가한다. 데이터가 충분해 재생이 이어지면 playing에서 클래스를 제거한다. canplay에서도 스피너를 숨기면 초기 로딩 시 최소 재생 준비가 끝난 상황을 안전하게 처리할 수 있다.
6. 배속 제어
배속 select의 change 이벤트에서 문자열 value를 parseFloat로 숫자로 바꿔 playbackRate에 대입한다. 재생 속도가 다른 코드로 바뀔 가능성을 고려해 ratechange 이벤트에서 select.value를 실제 playbackRate와 다시 맞춘다.
제4장 드래그 앤 드롭의 구조와 이벤트
1. Drag and Drop API
Drag and Drop은 웹 페이지의 요소를 마우스로 누른 채 끌어 다른 영역에 내려놓는 상호작용을 구현하는 표준 기술이다. 파일 업로드, 칸반 보드, 장바구니 등에 활용한다. HTML5 네이티브 DnD API는 마우스 이벤트 기반이어서 스마트폰 터치를 직접 지원하지 않으므로 모바일까지 지원하려면 터치 대응 라이브러리 등을 고려한다.
복잡한 리스트 정렬에서는 네이티브 API의 사용 난도와 브라우저 차이 때문에 SortableJS나 React-DnD 같은 라이브러리를 사용할 수 있다.
2. 세 가지 핵심 요소
| 요소 | 역할 |
|---|---|
| 소스 | 끌려가는 대상이며 일반 요소에는 draggable="true"가 필요하다. |
| 드롭 존 | 요소를 내려놓을 목적지 영역이다. |
| dataTransfer | 출발지에서 데이터를 담고 목적지에서 꺼내는 임시 전달 객체이다. |
a와 img 요소는 기본적으로 드래그 가능하지만 일반 요소는 draggable="true"를 명시한다. 모든 DnD 이벤트 객체에는 e.dataTransfer가 포함되어 출발지와 목적지 사이의 데이터를 운반한다.
3. 소스에서 발생하는 이벤트
dragstart는 드래그 시작 순간 한 번 발생하며 setData()로 데이터를 담고 effectAllowed를 지정한다. drag는 끄는 동안 계속 발생한다. dragend는 마우스를 놓아 드래그가 끝날 때 한 번 발생하며 반투명 클래스 제거처럼 소스 UI를 원래대로 복구한다.
4. 드롭 존에서 발생하는 이벤트
dragenter는 소스가 드롭 존에 들어오는 순간, dragover는 그 위에 머무르는 동안 반복해서 발생한다. dragleave는 영역을 벗어날 때, drop은 실제로 내려놓을 때 발생한다.
브라우저는 기본적으로 일반 요소 위의 드롭을 허용하지 않는다. dragover 핸들러에서 preventDefault()를 호출해야 drop 이벤트가 발생한다. drop에서도 파일 열기 같은 기본 동작을 막기 위해 preventDefault()를 호출한다.
dragenter에서 하이라이트 클래스를 추가하고 dragleave 또는 drop에서 제거하면 사용자가 내려놓을 수 있는 위치를 명확히 알 수 있다. 테두리, 배경색, 크기 변화 같은 시각적 피드백은 DnD의 중요한 사용자 경험 요소이다.
제5장 dataTransfer 객체
1. 데이터 메서드
setData(format, data)는 dragstart에서 전달할 데이터를 저장한다. format은 text/plain, text/uri-list, text/html, application/json 같은 형식을 사용한다. getData(format)는 drop에서 같은 형식의 데이터를 꺼낸다. 보안상의 이유로 일반적으로 실제 데이터 읽기는 drop에서 수행한다.
clearData([format])는 저장된 데이터를 지운다. 형식을 지정하면 해당 데이터만, 생략하면 모든 데이터를 삭제한다.
source.addEventListener('dragstart', e => {
e.dataTransfer.setData('text/plain', source.id);
});
target.addEventListener('drop', e => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
});
2. 작업 효과 속성
effectAllowed는 dragstart에서 출발지가 허용하는 작업을 정한다. copy, move, link, copyMove, all, none 등을 지정할 수 있다. dropEffect는 dragover에서 현재 드롭 존이 선택한 copy, move, link, none 작업을 나타내고 마우스 커서 피드백에 반영된다.
dropEffect는 effectAllowed가 허용한 범위와 호환되어야 한다. 예를 들어 출발지가 move만 허용한다면 목적지도 move를 선택해야 일관된 동작과 커서를 제공할 수 있다.
3. files 속성
운영체제에서 로컬 파일을 브라우저로 드롭하면 dataTransfer.files에 File 객체들의 FileList가 담긴다. 브라우저 내부 요소 이동처럼 setData와 getData로 파일 내용을 담는 것이 아니라 운영체제가 제공한 files를 읽는다. File 객체의 name과 size 등으로 파일 정보를 표시할 수 있다.
제6장 드래그 앤 드롭 활용
1. 텍스트 이동
기본 텍스트 예제에서는 소스의 draggable을 true로 하고 dragstart에서 innerText를 text/plain으로 저장한다. 드롭 존의 dragover에서 preventDefault()를 호출하고 drop에서 getData('text/plain')로 텍스트를 읽어 target.innerText에 넣는다.
dragover에서 dropEffect를 move로 지정하고 dragenter·dragleave·drop에 따라 drag-over 클래스를 추가·제거하면 커서와 영역의 시각적 피드백을 함께 제공할 수 있다.
2. 파일 드롭 UI
파일 업로드 예제는 dragenter에서 active 클래스를 추가하고 안내를 ‘이제 마우스를 놓으세요’로 바꾼다. dragover에서 기본 동작을 막고, dragleave에서 상태를 원복한다. drop에서는 다시 기본 동작을 막은 뒤 active를 제거하고 e.dataTransfer.files를 얻는다.
FileList는 Array.from(files)로 배열처럼 변환해 각 File을 처리할 수 있다. file.size가 1024 × 1024보다 큰지에 따라 MB 또는 KB로 바꾸고 toFixed나 Math.round로 보기 좋은 크기를 만든 뒤 파일명과 함께 목록에 추가한다.
강의의 파일 드롭 예제는 선택된 파일 정보를 화면에 표시하는 단계이다. 실제 서버 업로드를 완료하려면 별도의 네트워크 전송과 서버 측 파일 검증이 필요하다.
3. 칸반 보드
칸반 보드는 여러 column을 드롭 존으로, 각 task card를 draggable 소스로 구성한다. dragstart에서 카드 id를 text/plain으로 저장하고 dragging 클래스를 붙이며, dragend에서 클래스를 제거한다.
각 column의 dragenter와 dragover에서 기본 동작을 막고 drag-over 클래스를 제어한다. drop에서는 저장한 카드 id를 꺼내 document.getElementById로 요소를 찾고 column.append(draggedCard)를 호출한다. 기존 DOM 요소를 다른 부모에 append하면 복제되지 않고 해당 요소가 새 column으로 이동한다.
칸반 이동 흐름: 카드 id 저장 → drop 허용 → id 읽기 → 카드 요소 찾기 → 목적 column에 append → 시각 상태 원복
핵심 개념 정리
- audio와 video는 HTMLMediaElement를 공유하므로 currentTime, duration, paused, volume, muted, playbackRate와 play(), pause(), load()를 공통으로 사용한다.
- duration은 메타데이터 전에는 NaN일 수 있고 play()는 실패할 수 있는 Promise를 반환한다.
- loadedmetadata는 전체 길이, timeupdate는 진행률, volumechange는 음량 UI 동기화에 적합하다.
- waiting에서 버퍼링 UI를 표시하고 playing 또는 canplay에서 숨긴다.
- 커스텀 플레이어는 상태를 바꾼 코드보다 미디어 이벤트에서 버튼과 슬라이더를 동기화하는 편이 견고하다.
- HTML5 DnD는 소스, 드롭 존, dataTransfer의 세 요소로 이해한다. 일반 소스에는 draggable="true"가 필요하다.
- 소스에서는 dragstart·drag·dragend, 드롭 존에서는 dragenter·dragover·dragleave·drop이 발생한다.
- drop을 허용하려면 dragover에서 preventDefault()를 호출하고, drop에서도 브라우저 기본 동작을 막는다.
- setData는 dragstart에서 데이터를 담고 getData는 drop에서 꺼내며 clearData는 저장 데이터를 지운다.
- effectAllowed는 소스가 허용하는 작업, dropEffect는 목적지가 선택한 작업이고 서로 호환되어야 한다.
- 로컬 파일은 dataTransfer.files의 FileList로 받고, 카드 이동은 저장한 id로 요소를 찾아 목적지에 append한다.
미디어 제어와 드래그 앤 드롭 모두 실제 객체의 상태를 이벤트 기반 UI와 일치시키는 것이 핵심이다. 미디어에서는 Promise를 반환하는 play()와 메타데이터·버퍼링·음량 이벤트를 정확히 연결하고, DnD에서는 dragover의 preventDefault(), dragstart의 setData(), drop의 getData() 흐름을 지켜야 한다. 네이티브 DnD의 모바일 제약과 파일 처리의 서버 검증 필요성도 함께 고려한다.
예상문제 20선
1. audio와 video가 공통으로 상속하는 인터페이스는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
audio와 video는 HTMLMediaElement를 상속하므로 재생·음량·시간 관련 속성, 메서드, 이벤트가 거의 같다.
2. 미디어 전체 길이를 초 단위로 나타내는 읽기 전용 속성은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
duration은 전체 길이이며 메타데이터가 로드되기 전에는 NaN일 수 있다. currentTime은 현재 재생 위치이다.
3. 미디어의 현재 위치를 1분 지점으로 이동하는 코드는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
currentTime은 초 단위의 현재 재생 위치이며 값을 대입하면 해당 시점으로 탐색한다.
4. play() 메서드에 대한 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
play()는 비동기 재생 시도를 나타내는 Promise를 반환하므로 catch로 정책 차단과 리소스 오류를 처리한다.
5. stop()이 없는 미디어를 정지하고 처음으로 되돌리는 방법은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
HTMLMediaElement에는 stop()이 없으므로 pause()로 멈춘 뒤 currentTime = 0으로 처음 위치로 이동한다.
6. 메타데이터가 준비되어 duration과 진행 바 max를 설정하기 좋은 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
loadedmetadata는 길이와 해상도 같은 메타데이터가 준비된 시점이므로 전체 시간 표시와 진행 바 범위 설정에 알맞다.
7. 재생 중 진행률 바를 currentTime과 동기화하는 대표 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
timeupdate는 재생 위치가 변하는 동안 주기적으로 발생하므로 진행 바 value와 현재 시간 표시를 갱신한다.
8. 네트워크 버퍼링이 시작되어 로딩 스피너를 표시할 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
waiting은 재생할 데이터가 부족해 멈췄을 때 발생한다. playing에서 스피너를 숨길 수 있다.
9. volume과 muted의 어떤 변경 경로에도 볼륨 UI를 동기화하기 좋은 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
volumechange는 실제 volume 값 또는 muted 상태가 바뀔 때 발생하므로 버튼과 슬라이더를 한곳에서 동기화할 수 있다.
10. 재생 배속을 1.5배로 만드는 코드는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
playbackRate는 재생 속도를 나타내며 1.0이 기본, 1.5는 1.5배속이다.
11. 일반 HTML 요소를 드래그 소스로 만들 때 필요한 속성은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
일반 요소를 네이티브 DnD 소스로 사용하려면 draggable="true"를 지정한다. a와 img는 기본적으로 드래그 가능하다.
12. 드래그 시작 시 한 번 발생하며 dataTransfer에 데이터를 담는 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
dragstart는 소스에서 드래그 시작 순간 발생하며 setData()로 전달 데이터를 저장하는 시점이다.
13. drop 이벤트가 발생하도록 드롭 존의 dragover에서 반드시 호출할 메서드는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
브라우저의 기본 드롭 금지 동작을 막기 위해 dragover에서 preventDefault()를 호출해야 drop이 허용된다.
14. 드롭 존에 요소가 처음 들어왔을 때 하이라이트를 추가하기 좋은 이벤트는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
dragenter는 드래그 중인 요소가 드롭 존 안으로 들어오는 순간 발생한다. dragleave나 drop에서 하이라이트를 제거한다.
15. dragstart에서 문자열 데이터를 저장하는 dataTransfer 메서드는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
setData(format, data)는 dragstart에서 데이터를 담고 getData(format)는 drop에서 같은 형식의 데이터를 꺼낸다.
16. effectAllowed와 dropEffect의 관계로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
effectAllowed는 소스가 허용하는 copy·move 등을, dropEffect는 드롭 존이 수행할 작업과 커서 효과를 정한다.
17. 운영체제에서 드롭한 파일 객체 목록을 가져오는 속성은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
dataTransfer.files에는 운영체제가 전달한 File 객체들의 FileList가 담긴다.
18. HTML5 네이티브 DnD API의 모바일 관련 주의점은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
네이티브 HTML5 DnD는 기본적으로 마우스 기반이므로 모바일 터치 환경에는 터치 지원 라이브러리 같은 대안이 필요하다.
19. 칸반 보드에서 기존 카드를 다른 column의 자식으로 이동시키는 메서드는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
기존 DOM 요소를 새 부모에 append하면 복제하지 않고 그 요소가 새 column의 마지막 자식으로 이동한다.
20. DnD의 기본 데이터 전달 흐름으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
출발지의 dragstart에서 데이터를 담고 목적지의 drop에서 같은 format으로 꺼내 처리한다.
댓글
댓글 쓰기