From 6751d691832c973e29ea30098d7fc0fb4e01e216 Mon Sep 17 00:00:00 2001
From: nabivi
Date: Tue, 25 Aug 2026 13:19:17 +0900
Subject: [PATCH] =?UTF-8?q?fix(fe):=20=ED=94=84=EB=A1=A0=ED=8A=B8=20?=
=?UTF-8?q?=EB=85=B9=EC=9D=8C=20=ED=99=94=EC=9E=90=20=EB=B6=84=EB=A6=AC=20?=
=?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../checklist/RecordingModals.test.js | 19 ++++++++++-
.../checklist/RecordingTextModal.vue | 34 +++++++++++++++++--
2 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/frontend/src/components/checklist/RecordingModals.test.js b/frontend/src/components/checklist/RecordingModals.test.js
index 874f63b..ec0ac47 100644
--- a/frontend/src/components/checklist/RecordingModals.test.js
+++ b/frontend/src/components/checklist/RecordingModals.test.js
@@ -46,7 +46,7 @@ describe('RecordingTextModal', () => {
[{ isLoading: true }, '녹음 내용을 불러오고 있어요.'],
[{ errorMessage: '조회 실패' }, '조회 실패'],
[{ text: '' }, '변환된 녹음 텍스트가 아직 없어요.'],
- [{ text: '첫 줄\n둘째 줄' }, '첫 줄\n둘째 줄'],
+ [{ text: '화자 정보 없는 녹음' }, '화자 정보 없는 녹음'],
])('조회 상태에 맞는 본문을 표시한다', (state, message) => {
const wrapper = mount(RecordingTextModal, {
props: { open: true, ...state },
@@ -55,6 +55,23 @@ describe('RecordingTextModal', () => {
expect(wrapper.text()).toContain(message);
});
+ test('백엔드 화자 번호를 A와 B로 바꾸고 화자마다 별도 줄로 표시한다', () => {
+ const wrapper = mount(RecordingTextModal, {
+ props: {
+ open: true,
+ text: '화자 1: 첫 번째 발화 이어지는 발화\n화자 2: 두 번째 발화\n화자 1: 다시 첫 번째 화자',
+ },
+ });
+
+ expect(
+ wrapper.findAll('.recording-text-modal__speaker-line').map((line) => line.text()),
+ ).toEqual([
+ 'A: 첫 번째 발화 이어지는 발화',
+ 'B: 두 번째 발화',
+ 'A: 다시 첫 번째 화자',
+ ]);
+ });
+
test('모달 shell 닫힘을 상위로 전달한다', async () => {
const wrapper = mount(RecordingTextModal, { props: { open: true } });
await wrapper.get('.shell-close').trigger('click');
diff --git a/frontend/src/components/checklist/RecordingTextModal.vue b/frontend/src/components/checklist/RecordingTextModal.vue
index ec5f794..a40a1aa 100644
--- a/frontend/src/components/checklist/RecordingTextModal.vue
+++ b/frontend/src/components/checklist/RecordingTextModal.vue
@@ -10,7 +10,28 @@ const props = defineProps({
errorMessage: { type: String, default: '' },
});
const emit = defineEmits(['close']);
-const hasText = computed(() => Boolean(props.text.trim()));
+const transcriptLines = computed(() => {
+ const speakerAliases = new Map();
+
+ return props.text
+ .split(/\r?\n/)
+ .map((line) => line.trim())
+ .filter(Boolean)
+ .map((line) => {
+ const speakerLine = line.match(/^화자\s+([^:]+):\s*(.*)$/);
+ if (!speakerLine) return line;
+
+ const [, speaker, content] = speakerLine;
+ if (!speakerAliases.has(speaker)) {
+ speakerAliases.set(
+ speaker,
+ String.fromCharCode(65 + speakerAliases.size),
+ );
+ }
+ return `${speakerAliases.get(speaker)}: ${content}`;
+ });
+});
+const hasText = computed(() => transcriptLines.value.length > 0);
@@ -29,7 +50,13 @@ const hasText = computed(() => Boolean(props.text.trim()));
class="recording-text-modal__error mb-0 text-center"
role="alert"
>{{ errorMessage }}
- {{ text }}
+
변환된 녹음 텍스트가 아직 없어요.
@@ -52,6 +79,9 @@ const hasText = computed(() => Boolean(props.text.trim()));
line-height: 1.75;
white-space: pre-wrap;
}
+.recording-text-modal__speaker-line + .recording-text-modal__speaker-line {
+ margin-top: 12px;
+}
.recording-text-modal__empty {
padding: 24px 0;
color: var(--black-300);