From af7955a515caa4933982cfc738125017c31411b6 Mon Sep 17 00:00:00 2001 From: Ham BeomJoon Date: Wed, 1 Jul 2026 19:35:47 +0900 Subject: [PATCH 1/4] =?UTF-8?q?style:=20=EC=97=B0=EC=8A=B5=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EA=B2=B0=EA=B3=BC=20=ED=99=94=EB=A9=B4=EC=9D=98=20?= =?UTF-8?q?=EB=A7=90=ED=95=98=EA=B8=B0=20=EC=86=8D=EB=8F=84=20=EC=B9=A9=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `PracticeAnalysisResultPage` 내 말하기 속도 표시를 위한 `PrezelChip`에 `ChipHierarchy.SECONDARY` 속성 적용 --- .../impl/analysis/component/PracticeAnalysisResultPage.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt index 43096664..87ebda97 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.actions.area.PrezelButtonArea import com.team.prezel.core.designsystem.component.actions.button.PrezelButton +import com.team.prezel.core.designsystem.component.chip.chip.ChipHierarchy import com.team.prezel.core.designsystem.component.chip.chip.PrezelChip import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme @@ -151,7 +152,10 @@ private fun PracticeAnalysisMetricRow( horizontalArrangement = Arrangement.SpaceBetween, ) { PracticeAnalysisMetricLabel(text = stringResource(R.string.feature_practice_impl_practice_recording_analysis_speed)) - PrezelChip(text = stringResource(speed.labelResId)) + PrezelChip( + text = stringResource(speed.labelResId), + hierarchy = ChipHierarchy.SECONDARY, + ) } } } From c591178a153e219f60b8b0364c971d548998a57e Mon Sep 17 00:00:00 2001 From: Ham BeomJoon Date: Wed, 1 Jul 2026 19:53:05 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=EC=97=B0=EC=8A=B5=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=8B=A4=ED=8C=A8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=84=B8=EB=B6=84=ED=99=94=20=EB=B0=8F=20UI=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `PracticeAnalysisErrorType`별 에러 타이틀 및 설명 문구 분리 - 음성 인식 실패와 일반 분석 실패 상황에 대한 전용 문자열 리소스 추가 - `PracticeAnalysisFailurePage`에서 에러 타입별 리소스 매핑 로직(`titleResId`, `descriptionResId`) 구현 - `PracticeAnalysisErrorType` enum 상수 순서 조정 --- .../component/PracticeAnalysisFailurePage.kt | 19 +++++++++++++++++-- .../model/PracticeAnalysisErrorType.kt | 2 +- .../impl/src/main/res/values/strings.xml | 6 ++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt index 425a5918..20e8e4cb 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt @@ -1,6 +1,7 @@ package com.team.prezel.feature.practice.impl.analysis.component import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -27,8 +28,8 @@ internal fun PracticeAnalysisFailurePage( modifier: Modifier = Modifier, ) { StatusView( - title = stringResource(R.string.feature_practice_impl_practice_recording_analysis_error_title), - description = stringResource(R.string.feature_practice_impl_practice_recording_analysis_error_description), + title = stringResource(errorType.titleResId), + description = stringResource(errorType.descriptionResId), modifier = modifier, visual = { Image( @@ -58,6 +59,20 @@ private val PracticeAnalysisErrorType.drawableResId: Int PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> CoreUiR.drawable.core_ui_error_voice } +private val PracticeAnalysisErrorType.titleResId: Int + @StringRes + get() = when (this) { + PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_title + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_voice_recognition_failed_title + } + +private val PracticeAnalysisErrorType.descriptionResId: Int + @StringRes + get() = when (this) { + PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_description + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_voice_recognition_failed_description + } + @BasicPreview @Composable private fun PracticeAnalysisAnalyzeFailurePagePreview() { diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt index 5af82af9..25c7a395 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt @@ -1,6 +1,6 @@ package com.team.prezel.feature.practice.impl.analysis.model internal enum class PracticeAnalysisErrorType { - VOICE_RECOGNITION_FAILED, ANALYSIS_FAILED, + VOICE_RECOGNITION_FAILED, } diff --git a/Prezel/feature/practice/impl/src/main/res/values/strings.xml b/Prezel/feature/practice/impl/src/main/res/values/strings.xml index 272f590f..51e85922 100644 --- a/Prezel/feature/practice/impl/src/main/res/values/strings.xml +++ b/Prezel/feature/practice/impl/src/main/res/values/strings.xml @@ -12,8 +12,10 @@ 녹음을 재생하지 못했습니다. 분석중 잠시만 기다려주세요 - 분석에 실패했어요 - 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 시도해 주세요. + 분석할 음성을 인식하지 못했어요. + 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 녹음해 주세요. + 분석 중 문제가 발생했어요. + 일시적인 오류로 분석을 완료하지 못했어요.\n잠시 후 다시 시도해 주세요. 다시 시도하기 완료 발화 From 9edb5a96befeb18292b37ba1b90e9b188648b676 Mon Sep 17 00:00:00 2001 From: Ham BeomJoon Date: Wed, 1 Jul 2026 20:13:33 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=EC=9D=8C=EC=84=B1=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=20=EC=8B=A4=ED=8C=A8=20=EA=B2=80=EC=A6=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=97=B0=EC=8A=B5=20=EC=9E=AC=EC=8B=9C?= =?UTF-8?q?=EB=8F=84=20=EB=82=B4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `PracticeMapper`에서 `accuracyScore`가 0인 경우 음성 인식 실패 예외(`VOICE_RECOGNITION_FAILED`)를 발생시키도록 검증 로직 추가 - `AppErrorExt`에서 `AppException`이 발생했을 때 중복 래핑 없이 그대로 전달되도록 수정 - `PracticeEntryBuilder`에 `PracticeRecordingRetryNavKey`를 추가하고, 재시도 시 내비게이션 스택을 초기화(`clearStack = true`)하도록 개선 - `PracticeRecordingScreen` 진입 로직을 `PracticeRecordingRoute`로 분리하여 코드 중복 제거 및 구조 개선 --- .../prezel/core/data/error/AppErrorExt.kt | 2 + .../prezel/core/data/mapper/PracticeMapper.kt | 25 +++++++-- .../impl/navigation/PracticeEntryBuilder.kt | 53 ++++++++++++++----- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt index eac35399..87053a31 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt @@ -17,6 +17,8 @@ private fun Throwable.toDomainThrowable(): Throwable = when (this) { is CancellationException -> this + is AppException -> this + is ApiException -> AppException( error = errorCode.toDomainError(), diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt index e40d299e..722937fd 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt @@ -1,5 +1,7 @@ package com.team.prezel.core.data.mapper +import com.team.prezel.core.common.error.AppError +import com.team.prezel.core.common.error.AppException import com.team.prezel.core.model.practice.PracticeRecordingAnalysisResult import com.team.prezel.core.model.practice.PracticeRecordingOverallEvaluation import com.team.prezel.core.model.practice.PracticeScript @@ -15,10 +17,23 @@ internal fun PracticeSentenceResponse.toDomain(): PracticeScript = ) internal fun AnalyzePracticeRecordingResponse.toDomain(): PracticeRecordingAnalysisResult = - PracticeRecordingAnalysisResult( - pronunciationScore = accuracyScore.roundToInt(), - speed = RecordingSpeed.from(value = speedEvaluation), - overallEvaluation = PracticeRecordingOverallEvaluation.from(overallEvaluation), - ) + validateVoiceRecognition().let { response -> + PracticeRecordingAnalysisResult( + pronunciationScore = response.accuracyScore.roundToInt(), + speed = RecordingSpeed.from(value = response.speedEvaluation), + overallEvaluation = PracticeRecordingOverallEvaluation.from(response.overallEvaluation), + ) + } + +private fun AnalyzePracticeRecordingResponse.validateVoiceRecognition(): AnalyzePracticeRecordingResponse = + apply { + if (accuracyScore.roundToInt() == VOICE_RECOGNITION_FAILED_SCORE) { + throw AppException( + error = AppError.VOICE_RECOGNITION_FAILED, + message = "분석할 음성 인식 실패", + ) + } + } private const val PRACTICE_SCRIPT_ID = 0L +private const val VOICE_RECOGNITION_FAILED_SCORE = 0 diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt index 37742951..86bae477 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt @@ -1,5 +1,6 @@ package com.team.prezel.feature.practice.impl.navigation +import androidx.compose.runtime.Composable import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey @@ -14,30 +15,30 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityRetainedComponent import dagger.multibindings.IntoSet +import kotlinx.serialization.Serializable internal fun EntryProviderScope.featurePracticeEntryBuilder() { entry { key -> - val navigator = LocalNavigator.current + PracticeRecordingRoute(presentationId = key.presentationId) + } - PracticeRecordingScreen( - onBack = navigator::goBack, - navigateToAnalysis = { recordingFilePath, referenceText -> - navigator.navigate( - PracticeAnalysisNavKey( - presentationId = key.presentationId, - recordingFilePath = recordingFilePath, - referenceText = referenceText, - ), - ) - }, - ) + entry { key -> + PracticeRecordingRoute(presentationId = key.presentationId) } entry { key -> val navigator = LocalNavigator.current PracticeAnalysisScreen( - onRetry = navigator::goBack, + onRetry = { + navigator.navigate( + key = PracticeRecordingRetryNavKey( + presentationId = key.presentationId, + recordingFilePath = key.recordingFilePath, + ), + clearStack = true, + ) + }, onComplete = { navigator.replaceRoot(HomeNavKey) }, viewModel = hiltViewModel { factory -> factory.create( @@ -50,6 +51,30 @@ internal fun EntryProviderScope.featurePracticeEntryBuilder() { } } +@Composable +private fun PracticeRecordingRoute(presentationId: Long) { + val navigator = LocalNavigator.current + + PracticeRecordingScreen( + onBack = navigator::goBack, + navigateToAnalysis = { recordingFilePath, referenceText -> + navigator.navigate( + PracticeAnalysisNavKey( + presentationId = presentationId, + recordingFilePath = recordingFilePath, + referenceText = referenceText, + ), + ) + }, + ) +} + +@Serializable +private data class PracticeRecordingRetryNavKey( + val presentationId: Long, + val recordingFilePath: String, +) : NavKey + @Module @InstallIn(ActivityRetainedComponent::class) object FeaturePracticeModule { From 0dbf60bf7b6b97e1d827c40d6b5ccfa03302549b Mon Sep 17 00:00:00 2001 From: Ham BeomJoon Date: Tue, 7 Jul 2026 12:05:58 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=EC=9D=8C=EC=84=B1=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=20=EB=B0=8F=20=EB=85=B9=EC=9D=8C=20=EB=B6=84=EC=84=9D?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `PracticeMapper`에서 `accuracyScore` 0점 기반의 수동 유효성 검사 및 예외 발생 로직 제거 - `ServerErrorCode`에 `VOICE_RECORDING_EMPTY_OR_SILENT` (V003) 에러 코드 정의 추가 - 서버의 무음 또는 빈 녹음 에러를 `AppError.VOICE_RECOGNITION_FAILED`로 매핑하도록 수정하여 에러 처리 일원화 --- .../prezel/core/data/error/AppErrorExt.kt | 4 ++- .../prezel/core/data/mapper/PracticeMapper.kt | 25 ++++--------------- .../prezel/core/network/model/BaseResponse.kt | 1 + .../component/PracticeAnalysisFailurePage.kt | 5 ++-- .../impl/src/main/res/values/strings.xml | 4 +-- 5 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt index 87053a31..3a27dee3 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt @@ -76,7 +76,9 @@ private fun ServerErrorCode.toDomainError(): AppError = ServerErrorCode.INVALID_ID_TOKEN, -> AppError.UNAUTHORIZED - ServerErrorCode.VOICE_RECOGNITION_FAILED -> AppError.VOICE_RECOGNITION_FAILED + ServerErrorCode.VOICE_RECOGNITION_FAILED, + ServerErrorCode.VOICE_RECORDING_EMPTY_OR_SILENT, + -> AppError.VOICE_RECOGNITION_FAILED ServerErrorCode.UNKNOWN -> AppError.UNKNOWN } diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt index 722937fd..e40d299e 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.kt @@ -1,7 +1,5 @@ package com.team.prezel.core.data.mapper -import com.team.prezel.core.common.error.AppError -import com.team.prezel.core.common.error.AppException import com.team.prezel.core.model.practice.PracticeRecordingAnalysisResult import com.team.prezel.core.model.practice.PracticeRecordingOverallEvaluation import com.team.prezel.core.model.practice.PracticeScript @@ -17,23 +15,10 @@ internal fun PracticeSentenceResponse.toDomain(): PracticeScript = ) internal fun AnalyzePracticeRecordingResponse.toDomain(): PracticeRecordingAnalysisResult = - validateVoiceRecognition().let { response -> - PracticeRecordingAnalysisResult( - pronunciationScore = response.accuracyScore.roundToInt(), - speed = RecordingSpeed.from(value = response.speedEvaluation), - overallEvaluation = PracticeRecordingOverallEvaluation.from(response.overallEvaluation), - ) - } - -private fun AnalyzePracticeRecordingResponse.validateVoiceRecognition(): AnalyzePracticeRecordingResponse = - apply { - if (accuracyScore.roundToInt() == VOICE_RECOGNITION_FAILED_SCORE) { - throw AppException( - error = AppError.VOICE_RECOGNITION_FAILED, - message = "분석할 음성 인식 실패", - ) - } - } + PracticeRecordingAnalysisResult( + pronunciationScore = accuracyScore.roundToInt(), + speed = RecordingSpeed.from(value = speedEvaluation), + overallEvaluation = PracticeRecordingOverallEvaluation.from(overallEvaluation), + ) private const val PRACTICE_SCRIPT_ID = 0L -private const val VOICE_RECOGNITION_FAILED_SCORE = 0 diff --git a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt index 6037d95b..0b518369 100644 --- a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt +++ b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt @@ -66,6 +66,7 @@ enum class ServerErrorCode( SENTENCE_NOT_FOUND("S001"), VOICE_RECOGNITION_FAILED("V001"), VOICE_ANALYSIS_FAILED("V002"), + VOICE_RECORDING_EMPTY_OR_SILENT("V003"), UNKNOWN("UNKNOWN"), ; diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt index 20e8e4cb..24b3884f 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt @@ -63,14 +63,15 @@ private val PracticeAnalysisErrorType.titleResId: Int @StringRes get() = when (this) { PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_title - PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_voice_recognition_failed_title + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> + R.string.feature_practice_impl_practice_recording_voice_recognition_failed_title } private val PracticeAnalysisErrorType.descriptionResId: Int @StringRes get() = when (this) { PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_description - PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_voice_recognition_failed_description + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_voice_recognition_failed_description } @BasicPreview diff --git a/Prezel/feature/practice/impl/src/main/res/values/strings.xml b/Prezel/feature/practice/impl/src/main/res/values/strings.xml index 51e85922..8302cf75 100644 --- a/Prezel/feature/practice/impl/src/main/res/values/strings.xml +++ b/Prezel/feature/practice/impl/src/main/res/values/strings.xml @@ -12,8 +12,6 @@ 녹음을 재생하지 못했습니다. 분석중 잠시만 기다려주세요 - 분석할 음성을 인식하지 못했어요. - 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 녹음해 주세요. 분석 중 문제가 발생했어요. 일시적인 오류로 분석을 완료하지 못했어요.\n잠시 후 다시 시도해 주세요. 다시 시도하기 @@ -26,4 +24,6 @@ perfect good try + 분석할 음성을 인식하지 못했어요. + 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 녹음해 주세요.