기념일이나 생일에 '진열대에서 살 수 있는 것이 아닌' 선물을 찾다 보면, 경험을 디자인하는 것이 어렵다는 점에 깨닫게 됩니다. 사진이나 메시지 카드는 간편하지만, 상대방의 이름이나 두 사람만의 에피소드를 반복해서 들을 수 있는 형태로 남기기 위해서는 생성 파이프라인 측면의 제약 정리가 필요합니다. 여기서는 공개된 일본어 설명을 바탕으로, Once Song과 같은 '질문 → 곡' 플로우를 구현할 때의 설계 메모를 정리했습니다. 코드는 모두 설명용 샘플이며, 제품의 소스 코드가 아닙니다.
일본어 버전의 흐름은 대체로 다음 4단계입니다.
- 선물 받는 사람/상황/남기고 싶은 추억을 안내에 따라 답변
- 무드(Mood)・장르(Genre)・목소리 선택
- 먼저 자신만 시청해 보기 (비공개 프리뷰)
- 풀 트랙을 언락하여 선물로 남기기
공개된 복사본에 있는 '후렴구에 이름 녹이기', '템플릿의 빈칸 채우기가 아니다', '최단 30분' 등의 내용은, 구현상 필수 슬롯(이름 등)과 자유 기술 슬롯(에피소드)을 분리하고, 후속 가사/작곡 모델로 구조화하여 전달하는 문제에 직면합니다. 이 부분을 모호하게 긴 자연어로 연결하면, 고유명사의 누락이나 톤의 흐트러짐이 증가합니다.
폼(Form)을 그대로 연결하기 전에 스키마를 고정해야 합니다. 필수 항목과 선택 항목을 타입으로 구분해 두면, 유효성 검사(Validation)와 재생성 조건 작성이 용이해집니다.
type SongBrief = {
recipientName: string; // 후렴구 필수
occasion: 'anniversary' | 'birthday' | 'wedding' | 'proposal' | 'memorial' | 'other';
...
}
핵심은 이름을 '권장 사항'이 아니라 '계약 조건'으로 만드는 것입니다. 생성 후에 후렴구 문자열을 검사하여 이름이 포함되어 있지 않으면 재생성하거나 수동 검토로 보내면, 선물 경험의 실패율을 낮출 수 있습니다. 에피소드는 '짧고 구체적인 내용'을 여러 개 전달하는 것이 긴 독백 하나를 전달하는 것보다 가사에 남기기 쉽다는 경험적 규칙도 있습니다.
프리뷰는 '나만 확인', 풀 버전은 '선물로 남기기'라는 점 자체가 공개 플로우가 곧 과금 경계(Billing Boundary)입니다. API 설계에서는 다음과 같이 상태를 분리하는 것이 자연스럽습니다.
type SongJob = {
id: string;
brief: SongBrief;
...
}
프리뷰 URL을 알아도 풀 음원을 가져갈 수 없도록, 서명된 URL(Signed URL)의 권한을 서버 측에서 분리해야 합니다. 선물 용도의 경우, 완성 전에 상대방에게 링크가 유출되지 않는 것도 중요합니다. 큐 대기 시간('최단 30분'을 내세운다면)은 작업(Job)의 SLO(Service Level Objective) 모니터링과 사용자 대상 진행 상황 표시를 세트로 설계해야 합니다. 실패했을 때는 '같은 Brief로 재시도할 수 있는지 / 크레딧을 반환할지'를 먼저 결정해 두면 지원 업무가 수월합니다.
생성형 제품에서는 모델 출력을 그대로 반환하지 않고 게이트(Gate)를 거치는 것이 정석입니다.
- 후렴구에
recipientName이 포함되는지 (표기 변동을 정규화하여 판별) - 금지 토픽이나 과도하게 민감한 내용 필터링
- 장르/무드 지정과의 대략적인 일치 여부 (태그 불일치 시 재생성)
- 가사의 언어가 지정된 언어와 일치하는지
- 생성 시간의 SLO(큐 체류 및 워커 생사)
function normalizeName(s: string) {
return s.normalize('NFKC').toLowerCase().replace(/\s+/g, '');
}
...
게이트를 통과하지 못한 경우에는, 같은 Brief를 유지한 채 '가사만 재생성', '목소리만 교체' 등 단계를 나누어 재시도할 수 있다면 비용을 낮출 수 있습니다. 전부 처음부터 다시 할 필요는 없습니다.
- 해야 할 것: Brief 입력 지원, 프리뷰 재생, 언락 후 다운로드, 상대에게 전달하기 전 최종 확인
- 하지 말아야 할 것: 브라우저만으로 풀 음원 URL 추측, 프리뷰를 SNS에 미리 공개하는 경로를 암묵적으로 남기기, 필수 슬롯 미입력 상태로 생성 API 호출
'먼저 자신만 듣는다'는 공개 문구는 구현상 프리뷰 권한과 공유 UI의 초기 상태에 그대로 반영됩니다.
가사 생성・음성 합성・마스터링을 1개의 요청으로 동기 실행하면, 타임아웃 및 재시도가 어렵습니다. 공개된 '최단 30분'을 지키려면, 최소한 다음 단계로 분리하는 것이 안전합니다.
각 단계별 실패 이유를 코드로화하고, 사용자에게 보여줄 문구와 내부 로그를 분리합니다. "다시 시도하기" 버튼이 어느 단계부터 재개할 수 있는지 UI에 표시하면, 고객 지원 문의가 줄어듭니다. 워커는 Idempotency Key(jobId + stage)를 가지고 있어, 중복 실행으로 인한 이중 결제를 방지합니다.
Once Song은 다국어 사이트로 공개되었지만, 한 곡 안에서 언어가 혼재되면 선물 경험이 깨지기 쉽습니다. Brief의 language를 가사, 음성 읽기(낭독), 이메일 문구 전체에 전파시키고, 미리보기 화면의 UI 언어와 곡의 언어가 다를 경우에는 명시합니다. 기념일, 결혼식, 추모 등 occasion 별로 금지 표현 목록을 약간씩 변경하는 것도 효과적입니다 (축하 자리에서 무거운 표현이 섞이는 사고 방지).
개인화 곡은 "좋은 가사를 한 번에 뽑아내는 것"보다, 필수 슬롯 보장・시청과 풀 권한 분리・재생성 루프가 사용자 경험을 좌우합니다. 질문 순서, 무드 선택, 비공개 미리보기 노출 방식 등은 Once Song 공개 페이지에서 확인할 수 있습니다. 이야기를 소리로 만드는 프로덕트에서는 LLM의 출력 자체보다 폼 설계와 상태 기계(State Machine)가 더 오래 효과를 발휘합니다.