blog

유니앱에서 H5 녹음 및 업로드, 실시간 음성 인식 및 파형 시각화하기

유니앱에서 레코더 유니코어 플러그인을 사용하면 크로스 플랫폼 녹음 기능을 달성할 수 있으며, 뷰2, 뷰3, nvue, mp3, wav, pcm, amr, ogg, g711a, g7...

Aug 28, 2025 · 9 min. read
シェア

유니앱에서 레코더-유니코어 플러그인을 사용하면 크로스 플랫폼 레코딩 기능을 달성할 수 있으며, 유니앱은 레코더와 함께 제공되는 레코더 매니저 인터페이스가 H5를 지원하지 않고, 레코딩 형식 및 실시간 콜백 온프레임레코딩 호환성이 좋지 않으며, 레코더 플러그인을 사용하면 이러한 문제를 피할 수 있습니다.

DCloud 플러그인 마켓플레이스에서 플러그인을 다운로드할 수 있습니다:

Recorder-UniCore플러그인 기능

  • vue2, vue3, nvue 지원
  • mp3, wav, pcm, amr, ogg, g711a, g711u 등 대부분의 현존하는 녹음 포맷을 지원합니다.
  • 가변 속도 피치 변경, 실시간 업로드, ASR 음성-텍스트 변환을 포함한 실시간 처리 지원
  • 시각적 파형 표시 지원

프로젝트에 통합

1, npm 설치 레코더 코어를 통해

//유니앱 프로젝트 디렉토리에 npm을 설치합니다.
npm install recorder-core

2. Recorder-UniCore 플러그인을 다운로드하여 가져옵니다.

// 플러그인 마켓플레이스 https로 이동://../plugin?name=Recorder-UniCore 플러그인 다운로드
그런 다음 프로젝트에 추가 /uni_modules/Recorder-UniCore

3, js 도입부 내의 vue 페이지 파일에서

<script> /**로직 계층은 다음과 같습니다.**/
//레코더 코어를 도입해야 합니다.
import Recorder from 'recorder-core' //가져오기, 필요 등을 사용하세요.
//도입해야 하는 RecordApp 핵심 파일
import RecordApp from 'recorder-core/src/app-support/app'
//모든 플랫폼에 도입해야 하는 유니앱 지원 파일
import '@/uni_modules/Recorder-UniCore/app-uni-support.js'
/** 애플릿으로 컴파일이 필요한 경우 애플릿 지원 파일 소개 **/
// #ifdef MP-
 import 'recorder-core/src/app-support/app-miniProgram-wx-support.js'
// #endif
/** H5애플릿 환경: 필요한 형식 인코더와 시각화 플러그인을 도입하고, 앱 환경에서는 렌더js를 도입합니다. **/
// #ifdef H5 || MP-
 //녹음 형식을 지원하는 데 필요한 파일을 가져오고, 두 가지 이상의 형식이 필요한 경우 모든 형식에 대한 인코딩 엔진 js 파일을 가져오세요.
 import 'recorder-core/src/engine/mp3'
 import 'recorder-core/src/engine/mp3-engine' //이 형식에 추가 인코딩 엔진이 있는 경우, 추가 인코딩 엔진을 추가해야 합니다.
 
 //플러그인 지원 옵션
 import 'recorder-core/src/extensions/waveview'
// #endif
</script>
<!-- #ifdef APP -->
<script module="yourModuleName" lang="renderjs">
/**앱으로 컴파일해야 하는 경우, 렌더js 모듈을 추가한 다음 위의 js를 똑같은 방식으로 임포트해야 합니다!
 ,녹음 및 오디오 인코딩은 앱에서 기본적으로 렌더js에서 수행되기 때문입니다.**/
import 'recorder-core'
import RecordApp from 'recorder-core/src/app-support/app'
import '../../uni_modules/Recorder-UniCore/app-uni-support.js' //renderjs에서 지원되지 않는 것 같습니다.@/"시작 경로, 컴파일 경로가 잘못된 경우 경로를 수정하세요.
//온디맨드 지원 파일과 필요한 녹음 형식에 대한 플러그인을 소개합니다.
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine' 
import 'recorder-core/src/extensions/waveview'
export default {
 mounted(){
 //App현재 모듈의 renderjs가 호출해야 하는 함수로, 현재 모듈 this를 전달합니다.
 RecordApp.UniRenderjsRegister(this);
 },
 methods: {
 //여기에 정의된 메서드는 RecordApp을 통해 로직 레이어에서 사용할 수 있습니다..UniWebViewVueCall(this,'this.xxxFunc()') 직접 통화
 //로직 레이어에서 메서드를 호출하려면 this를 직접 사용하세요..$ownerInstance.callMethod("xxxFunc",{args}) 통화, 바이너리 데이터를 base64로 변환하여 전달해야 함
 }
}
</script>
<!-- #endif -->

통화 녹음

/**로직 레이어에서 쓰기**/ //import ... 위의 가져오기 코드 export default { data() { return {} } ,mounted() { this.isMounted=true; //페이지가 켜져 있을 때 호출해야 하는 함수로, 현재 컴포넌트 this를 전달합니다. RecordApp.UniPageOnShow(this); } ,onShow(){ //onShow마운트하기 전에 실행될 수 있으며, 페이지가 아직 준비되지 않았을 수 있습니다. if(this.isMounted) RecordApp.UniPageOnShow(this); } ,methods:{ //녹음 권한 요청 recReq(){ //앱을 컴파일할 때 제공되는 라이선스로, 라이선스를 입력하지 않으면 앱을 열고 처음 호출할 때 "상업용 라이선스 없이 앱에서 테스트 목적으로만 사용합니다"라는 팝업이 표시되어 녹음 권한을 요청합니다.” //RecordApp.UniAppUseLicense='UniAppID가 부여되었습니다.=*****상업적 라이선스'; RecordApp.UniWebViewActivate(this); //App먼저 현재 페이지 웹보기로 전환해야 합니다. RecordApp.RequestPermission(()=>{ console.log("녹음 권한이 부여되었으므로 지금 녹음을 시작할 수 있습니다."); },(msg,isUserNotAllow)=>{ if(isUserNotAllow){//사용자가 녹음 권한을 거부했습니다. //여기에 사용자가 다른 플랫폼에 대해 별도로 녹음 권한을 부여하도록 안내하는 코드를 작성해야 합니다. } console.error("녹음 권한을 요청하지 못했습니다:"+msg); }); } //녹음 시작 ,recStart(){ //녹음 구성 정보 var set={ type:"mp3",sampleRate:16000,bitRate:16 //mp3형식, 샘플링 속도 hz, 비트 전송률 kbps 지정, 기타 매개 변수는 기본 구성을 사용합니다. 참고 : 디지털 매개 변수는 숫자를 제공해야하며 문자열을 사용하지 마십시오. 유형 유형을 사용해야하며 wav 형식 사용과 같은 파일을 지원하기 위해 미리 형식을로드해야하는 경우 미리로드해야합니다..js코딩 엔진 ,onProcess:(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd)=>{ //모든 플랫폼에 공통: 실시간 데이터 업로드, 레코더와 함께 작업 가능.SampleData메서드를 사용하여 버퍼에 있는 새 데이터를 지속적으로 pcm으로 변환하여 업로드하거나 모의 메서드를 사용하여 새 데이터를 다른 형식으로 지속적으로 트랜스코딩하여 업로드할 수 있습니다.> 실시간 트랜스코딩 및 업로드 - 일반 버전; 이 기능을 기반으로 실시간 데이터 전달, 실시간 데이터 저장, 실시간 음성 인식 등을 수행할 수 있습니다. //참고: 앱 내부에서 실제 오디오 포맷 인코딩 작업은 렌더js에서 수행되며, 여기서 버퍼 데이터는 렌더js에서 실시간으로 전달되고, 여기서 버퍼 데이터를 수정해도 렌더js의 버퍼는 변경되지 않으므로 생성된 오디오 파일은 변경되지 않으며, 이는 onProcess에서 확인할 수 있습니다._renderjs버퍼 메모리를 정리하려면 여기와 onProcess에서 할 수 있습니다._renderjsH5에서 모두 정리해야 하며, 애플릿에는 이 제한이 없습니다. //참고: 브라우저에서만 지원되는 레코더 확장 플러그인을 앱 내부에서 사용하려면 렌더js에 확장 플러그인을 도입한 다음 onProcess에 새 플러그인을 추가하세요._renderjs이 플러그인 호출; H5는 여기에서 직접 호출 할 수 있으며 애플릿은 이러한 유형의 플러그인을 지원하지 않습니다. 플러그인을 호출하는 로직이 더 복잡한 경우 로직 계층 인 렌더js가 직접 가져 오기 위해 준비 할 필요가 없도록 js 파일로 캡슐화하는 것이 좋습니다. //H5애플릿과 같은 시각적 그래픽 드로잉은 로직 레이어에서 직접 실행되며, 앱 내부에서는 온프로세스_renderjs다음에서 이러한 작업을 수행합니다. // #ifdef H5 || MP- if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate); // #endif } ,onProcess_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd){ //App여기서 버퍼를 변경하면 생성된 오디오 파일이 변경됩니다. //App이것은 렌더js의 시각적 그래픽 도면이므로 여기에 작성해야 하며, "이"는 렌더js 모듈의 "이"입니다. 코드가 더 복잡하면 렌더js 안에 직접 xxxFunc 메서드를 넣어주세요. 코드가 더 복잡하다면, 렌더js 모듈 안에 xxxFunc 메서드를 넣고 여기에 this를 사용하세요..xxxFunc(args)전화 걸기 if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate); }` ,takeoffEncodeChunk:true?null:(chunkBytes)=>{ //모든 플랫폼에 공통: 인코더에서 인코딩된 오디오 클립 데이터를 실시간으로 수신하며, chunkBytes는 Uint8Array 바이너리 데이터로, 실시간으로 업로드할 수 있습니다. //AppRecordApp이 구성되지 않은 경우.UniWithoutAppRenderjs이 콜백을 제공하는 것이 좋습니다. 녹음이 끝나면 전체 녹음 파일이 렌더링에서 로직 레이어로 다시 전송되므로 uni-app의 로직 레이어와 렌더링 레이어 데이터 상호 작용 성능이 너무 많아서 대용량 파일의 전송 속도가 느려지므로 대용량 데이터 전송시 중지를 피하려면이 콜백을 제공하는 것이 좋습니다. } ,takeoffEncodeChunk_renderjs:true?null:`function(chunkBytes){ //App렌더js 모듈의 "이것"은 렌더js에서만 작동하거나 그렇지 않은 작업을 수행할 수 있는 곳입니다. }` ,start_renderjs:`function(){ //App이 모듈에 함수를 넣을 수 있으며, Start가 성공하면 renderjs가 여기에 있는 코드를 먼저 호출하며, this는 renderjs 모듈의 this입니다. //초기화와 같이 렌더제이에서만 작동하는 것을 넣는 것은 제공하지 않아도 괜찮습니다. }` ,stop_renderjs:`function(arrayBuffer,duration,mime){ //App이 모듈에 함수를 넣을 수 있으며, Stop이 성공하면 renderjs가 여기에 있는 코드를 먼저 호출하며, this는 renderjs 모듈의 this입니다. //렌더제이스로만 작동하는 것을 제공하지 않아도 괜찮습니다. }` }; RecordApp.UniWebViewActivate(this); //App먼저 환경의 현재 페이지 WebView로 전환해야 합니다. RecordApp.Start(set,()=>{ console.log("녹음이 시작되었습니다"); //오디오 비주얼 그래픽 드로잉을 만드는데, 앱 환경에서는 렌더JS로, H5, 애플릿 등에서는 로직 레이어에서 그려지므로 두 개의 동일한 코드를 제공해야 합니다. //view내부에 캔버스를 넣고 캔버스는 너비와 높이를 지정해야합니다. //<canvas type="2d" class="recwave-WaveView" style="width:300px;height:100px"></canvas> RecordApp.UniFindCanvas(this,[".recwave-WaveView"],` this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100}); `,(canvas1)=>{ this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100}); }); },(msg)=>{ console.error("녹음을 시작하지 못했습니다:"+msg); }); } //녹음 일시 중지 ,recPause(){ if(RecordApp.GetCurrentRecOrNull()){ RecordApp.Pause(); console.log("일시 중단됨"); } } //녹음 계속하기 ,recResume(){ if(RecordApp.GetCurrentRecOrNull()){ RecordApp.Resume(); console.log("계속 녹음 중..."); } } //녹음 중지 ,recStop(){ RecordApp.Stop((arrayBuffer,duration,mime)=>{ //모든 플랫폼에 공통: arrayBuffer는 파일로 저장하거나 서버로 전송할 수 있는 오디오 파일 바이너리 데이터입니다. //App시작 매개 변수에 중지를 지정하면_renderjs,renderjs의 함수가 이 함수보다 먼저 실행됩니다. //참고: Start에서 takeoffEncodeChunk가 제공되면 녹음 파일 데이터를 사용자가 직접 실시간으로 저장해야 하므로 Stop에서 반환되는 arrayBuffer의 길이는 0바이트가 됩니다. //현재 환경이 블롭을 지원하는 경우 블롭 파일 객체로 직접 구성할 수도 있으며, 레코더는 동일한 블롭을 사용합니다. if(typeof(Blob)!="undefined" && typeof(window)=="object"){ var blob=new Blob([arrayBuffer],{type:mime}); console.log(blob, (window.URL||webkitURL).createObjectURL(blob)); } },(msg)=>{ console.error("녹음을 종료하지 못했습니다:"+msg); }); } } }

위의 코드는 녹화 시작, 녹화 종료, 일시 중지, 메서드 코드의 기능 계속, 뷰에 몇 개의 버튼을 넣어 클릭하여 호출하고, 녹화 데이터의 실시간 처리에서 onProcess 콜백을 수행할 수 있으며, 시각적 그래픽 그리기 작업도 onProcess, H5, 앱, 앱에서 수행할 수 있습니다.

앱으로 컴파일하려면 먼저 manifest.json에서 Android 및 iOS 녹화 권한 문을 구성해야 합니다:

//Android두 번째 권한은 반드시 체크해야 하며, 그렇지 않으면 H5 녹음을 사용할 때 마이크를 켤 수 없습니다.
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
//iOS선언해야 하는 권한
NSMicrophoneUsageDescription

녹음 업로드

위의 녹음 recStop 코드에서 녹음이 끝나면 ArrayBuffer 바이너리 데이터를 가져온 후 서버에 업로드 할 수 있으며 실시간 처리도 업로드를 지원하며 다음 업로드 방법에 따라 ArrayBuffer 오디오 데이터를 실시간으로 가져올 수 있습니다.

업로드 방법 1: Base64 텍스트로 변환하여 업로드하기

//base64 텍스트이므로 일반 인터페이스 요청을 직접 사용할 수 있으므로 코드가 간단하고 H5, 앱, 앱 및 작은 프로그램 공통입니다.
uni.request({
	url: "인터페이스 주소 업로드 "
	,method: "POST"
	,header: { "content-type":"application/x-www-form-urlencoded" }
	,data: {
		audio: uni.arrayBufferToBase64(arrayBuffer)
		,... 기타 양식 매개변수 ...
	}
	,success: (res) => { }
	,fail: (err)=>{ }
});

업로드 방법 2: 업로드 양식을 사용하여 멀티파트/양식 데이터 업로드하기

//멀티파트/양식 데이터 양식을 사용한 파일 업로드는 유니앱에서 잘 지원되지 않으며, 각 플랫폼에서 개별적으로 처리됩니다.
// #ifdef H5
	//H5브라우저에서 제공하는 파일 인터페이스를 사용하여 브라우저에서 직접 파일을 구성하려면
	uni.uploadFile({
		url: "인터페이스 주소 업로드 "
		,file: new File([arrayBuffer], "recorder.mp3")
		,name: "audio"
		,formData: {
			... 기타 양식 매개변수 ...
		}
		,success: (res) => { }
		,fail: (err)=>{ }
	});
// #endif
// #ifdef APP
	//App바이너리 데이터를 로컬 파일에 직접 저장한 다음
	RecordApp.UniSaveLocalFile("recorder.mp3",arrayBuffer,(savePath)=>{
		uni.uploadFile({
			url: "인터페이스 주소 업로드 "
			,filePath: savePath
			,name: "audio"
			,formData: {
				... 기타 양식 매개변수 ...
			}
			,success: (res) => { }
			,fail: (err)=>{ }
		});
	},(err)=>{});
// #endif
// #ifdef MP-
	//바이너리 데이터를 애플릿의 로컬 파일에 저장한 다음 업로드해야 합니다.
	var savePath=wx.env.USER_DATA_PATH+"/recorder.mp3";
	wx.getFileSystemManager().writeFile({
		filePath:savePath
		,data:arrayBuffer
		,encoding:"binary"
		,success:()=>{
			wx.uploadFile({
				url: "인터페이스 주소 업로드 "
				,filePath: savePath
				,name: "audio"
				,formData: {
					... 기타 양식 매개변수 ...
				}
				,success: (res) => { }
				,fail: (err)=>{ }
			});
		}
		,fail:(e)=>{ }
	});
// #endif

ASR음성 인식

Read next

ffmpeg로 오디오 및 비디오 병합하기

에프엠피그 다운로드 및 설치 방법 에프엠피그를 이용한 에프엠피그 사용 방법 오디오 및 비디오 병합 에프엠피그 다운로드 및 설치 방법

Aug 25, 2025 · 2 min read