
부트캠프 JavaScript 막바지쯤 브라우저에서 제공하는 음성 녹음 기능을 구현했다. 한 번도 구현해 본 적이 없는 기능이어서 정리해보려고 한다.

화면에 위와같이 두 버튼을 만들어준다
HTML 코드
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>음성 녹음</title>
<link rel="icon" type="image/png" href="#" />
<script src="jquery-3.7.1.min.js"></script>
<script src="voiceRecord.js"></script>
</head>
<body>
<button id="recordBtn">녹음</button>
<button id="stopBtn">정지</button>
<!-- <audio> 컨트롤 동적 추가 -->
<div id="sound-clips"></div>
</body>
</html>
윈도우 실행 시 코드를 실행하도록 window.onload() 함수 안에 정의한다.
window.onload = function() {...}
id를 이용해서 태그를 가져온다.
let record = document.getElementById("recordBtn");
let stop = document.getElementById("stopBtn");
let soundClips = document.getElementById("sound-clips");
브라우저에 media device라는 인터페이스가 있는지 확인 후,
if (navigator.mediaDevices)
있으면 if문 안에 constraints를 정의해준다.
var constraints = {
audio:true // 마이크 사용하겠다!
// video: false // 카메라는 안 쓰겠다!
}
녹음 데이터를 저장할 배열을 생성하고,
let chunks = [];
브라우저에서 제공하는 기본 객체를 가져다가
navigator
현재 연결된 미디어 입력장치로의 접근 방법 제공하는 인터페이스를 호출해서
navigator.mediaDevices
사용자에게 카메라나 마이크를 사용할 수 있는 권한을 요청한다. 이때 constraints를 넘겨주어 요청이 필요한 것에만 요청한다.
navigator.mediaDevices.getUserMedia(constraints)

여기까지 과정이 끝나면 브라우저에 위와같이 권한 요청 창이 뜬다.
getUserMedia()는 Promise를 반환하는데 사용자가 마이크 접근을 허락하면 이 약속이 "성공" 상태로 바뀌고, then() 안에 있는 함수를 실행할 수 있다.
.then((stream) => { ... }
(여기서 stream은 브라우저가 마이크를 열어주어 그 마이크로부터 들어오는 오디오 데이터이다.)
녹음을 위한 MediaRecorder 클래스 사용해서 녹음 객체를 생성하고 stream객체를 생성자에 전달한다.
const mediaRecorder = new MediaRecorder(stream);
녹음 버튼을 클릭했을 때 작업을 정의해준다.
record.onclick = () => {
mediaRecorder.start(); // 녹음 시작
record.style.background = "red"; // 녹음 버튼 배경 색상 변경
record.style.color = "black"; // 녹음 버튼 글자 색상 변경
}

브라우저에는 녹음 아이콘이 표시된다

유효한 녹음 데이터가 들어온다면 다음 함수를 실행하게된다.
이벤트 객체에는 녹음데이터가 data라는 이름으로 담겨있다. 이를 앞서 정의한 배열에 넣어준다.
mediaRecorder.ondataavailable = e => {
chunks.push(e.data)
}
(이때 e.data는 아직 사용할 수 없는 상태이다. mp3 및 코덱 설정을 아직 안해주었기 때문이다.)
정지버튼을 클릭했을 때 작업을 정의해준다.
stop.onclick = () => {
mediaRecorder.stop(); // 녹음 정지
record.style.background = ""; // 원래 상태로 되돌림
record.style.color = ""; // 원래 상태로 되돌림
}
녹음이 정지되면 발생하는 onstop이벤트를 정의해준다
mediaRecorder.onstop = e => {...}
이제 녹음을 끝냈으니 stop이벤트 후 녹음 파일을 다운받을 수 있도록 해 줄 것이다.
순서는 다음과 같다.
1. audio 담을 컨테이너 객체 생성
const clipContainer = document.createElement('article'); // article 태그 생성
const audio = document.createElement('audio');
audio.setAttribute('controls', ''); // name, value설정
// 재생, 일시정지, 음량 조절 등 기본 UI를 브라우저에 표시
// ontrols 같은 boolean 속성은 빈 문자열도 값으로 허용
clipContainer.appendChild(audio);
if (soundClips.hasChildNodes()){
soundClips.removeChild(soundClips.childNodes[0]);
}
soundClips.appendChild(clipContainer);
const blob = new Blob(chunks, {
'type':'audio/mp3 codcs=opus'
})
chunks = []; // 녹음 내용이 누적되지 않도록 초기화
const audioURL = URL.createObjectURL(blob); // mp3, 코덱 설정 후 url 객체 생성
audio.src = audioURL;
const clipName = "voiceRecord"; // 다운로드시 사용할 파일 이름
const a = document.createElement('a'); // a 태그를 생성
clipContainer.appendChild(a);
// a태그는 download 속성이 있고 파일명을 주면 클릭이벤트 발생시 해당 파일명으로 다운로드를 진행함
a.href = audio.src;
a.download = clipName;
a.click();
정지 버튼을 누르면

이렇게 컨트롤 바가 뜨고

이렇게 파일 저장 화면이 나온다
외부 장치를 사용했으니 .then() 예외처리는 필수다
