Conversation
* fix: 부정어 감지 필터 추가로 안 매운/맵지 않게 등 오분류 방지, MEDIUM 표기를 보통맛으로 수정 * fix: 영문 부정어를 단어 경계로 매칭하도록 수정, 부정어 분기 유닛 테스트 추가 * fix: 한글 부정어 매칭 확대(안/않/말고/빼고), 영문 부정어 회귀 테스트 추가 * fix: 부정어 판정을 구체적 조합으로 좁혀 안녕/안내 등 오탐 방지, 부정 입력 시 외부 호출 없음 검증 추가 * fix: 빼주세요/빼줘/빼줄래 부정 표현 추가로 매운 거 빼주세요 오분류 방지
* feat: Chickenstore 환경의 CompatibilityRule errorcode 사용자 친화 메시지 * feat: CandidateFilterResult requiresReconfirmation 필드 map으로 변경 * feat: Map 변경에 따른 서비스 수정 * feat: RuleEvaluator: 1단계 값 추출 로직 * feat: RecommendationEngineService STEP5~7 추천 로직 초기 구현 * fix: STEP2 파트 passesByCandidateId 추가 전달 * fix: SKIPPED, PASS 점수 오류 수정 * fix: WARN 없는 항목의 confidence 임계값 크로스 테스트 픽스처 보정 * fix: 뼈 타입 추천 점수에 추가 * fix: STAFF_ASSISTANCE_REASON 멘트 추가 * fix: 병합 오류 수정 * fix: v5.1.6 RC5 변경에 맞추어 뼈타입.컵옵션 추가 * feat: 혼잡 시간대(요일+시간대) 기반 포장 메뉴 가산점 추가 * fix: 뼈 타입 점수, 확신도 점수 수정. --------- Co-authored-by: parkseyoung <parkseyoung@users.noreply.github.com>
포커스 결함 3건(원인이 각각 다름), 큰 글씨 모드 가로 넘침, 맵기 매칭(서버 임베딩) 붙이기, 사용자 피드백 5건, 여자 목소리 우선 선택, 안내 언어 위치 조정, QR 페어링 확인 도구(check:pairing) 추가. 코드래빗 지적 4건 반영.
* feat: 여러 조건이 맞으면 한 줄로 말합니다
서버는 맞은 축마다 이유를 한 줄씩 따로 줍니다. 두 축이 맞으면 화면이
이렇게 됐습니다.
반영: 포장 전용 닭강정 — 선호하신 이용 방식과 일치하는 메뉴라 우선 추천드립니다.
반영: 포장 전용 닭강정 — 선호하신 맵기와 맞는 메뉴라 우선 추천드립니다.
메뉴 이름과 뒷말이 통째로 되풀이돼서, 두 줄을 다 읽어야 무엇이 다른지
알 수 있습니다. 큰 글씨로 보는 분에게는 이것만으로 화면 하나가 찹니다.
한 줄로 합칩니다.
반영: 포장 전용 닭강정 — 선호하신 포장하기, 매운맛과 맞는 메뉴라 우선 추천드립니다.
■ 축 이름 대신 고르신 값으로 부릅니다
"이용 방식" 보다 "포장하기" 가 사용자가 실제로 고른 말입니다. 짐작이
아닙니다 — 어느 축인지는 서버 문장에 적혀 있고, 그 축에 무엇을 골랐는지는
주문표에 있습니다. 둘 다 아는 것만 씁니다. 주문표에 값이 없거나
"상관없음" 이면 축 이름 그대로 둡니다.
서버가 부르는 축 이름과 주문표의 축 이름이 늘 같지는 않습니다
("뼈/순살"→"형태", "컵 옵션"→"컵"). 표로 이어 두고, 못 찾으면 축 이름으로
물러납니다.
■ 조사
"맵기과" 가 되면 안 되므로 마지막 값의 받침을 보고 과/와 를 고릅니다.
영어에는 조사가 없어 en.ts 의 두 열쇠가 같은 문장을 가리킵니다.
■ 한 곳에서만 합칩니다
OrderConfirmScreen 에서 한 번 합쳐 아래로 내려보냅니다. 화면마다 따로
합치면, 접힌 한 줄이 세는 "외 N개" 와 펼친 목록의 줄 수가 어긋납니다.
■ 안 건드리는 것
한 축만 맞은 줄은 그대로 둡니다 — 되풀이가 없고, 그 문장들은 표에 열쇠가
그대로 있습니다. 못 맞춘 조건과 제외 이유도 손대지 않습니다. 저마다 다른
말이라 합치면 뜻이 뭉개집니다.
typecheck · 436 tests · build 통과. 이유묶기 시험 11건을 더했습니다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: 같은 축이 두 번 와도 한 번만 셉니다 (리뷰 1건)
코드래빗 🟡 Minor 1건. 타당해서 고쳤습니다.
같은 메뉴에 같은 축의 '맞음' 줄이 두 번 오면 이렇게 됐습니다.
선호하신 매운맛, 매운맛과 맞는 메뉴라 우선 추천드립니다.
같은 조건을 두 번 말하는 줄이라, 합치지 않느니만 못합니다.
축으로 가려서 한 번만 셉니다. 값이 아니라 축으로 가리는 이유는, 두 축이
같은 이름을 가질 수 있기 때문입니다 — 주문표에 값이 없으면 축 이름으로
물러나는데, 그때는 서로 다른 조건이 맞은 것이므로 둘 다 세는 것이 맞습니다.
서버는 축마다 한 줄씩 주므로 겹칠 일이 없어야 하지만, 이 자리는 겹쳐 온
적이 있습니다(App.tsx 의 error.details 주석에 같은 얘기가 적혀 있습니다).
시험 2건을 더했습니다.
· 같은 축이 두 번 와도 한 번만 센다 — 축이 하나뿐이라 합치지 않는다
· 겹친 축이 있어도 서로 다른 축이 둘이면 합친다
typecheck · 438 tests · build 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: 합친 뒤 남은 줄 수까지 봅니다 (리뷰 1건)
코드래빗 🟡 Minor 1건. 시험이 헐거웠던 것이 맞습니다.
"겹친 축이 있어도 서로 다른 축이 둘이면 합친다" 가 합침[0] 만 봤습니다.
그러면 첫 문장만 맞게 만들고 겹친 줄을 둘째 항목으로 남기는 구현도
통과합니다 — 화면에는 같은 말이 한 줄 더 붙는데도요.
toHaveLength(1) 을 앞에 뒀습니다. 지금 구현은 이미 1개를 내놓으므로
코드는 안 바뀌고, 시험이 그것을 붙잡게 된 것입니다.
typecheck · 438 tests · build 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
서버(#138)가 부정어를 읽게 되어 프론트의 되거르는 겹을 제거. 운영 배포 후 실측으로 확인했다 — "안 매운 거" → confident=false, candidates=[MILD, MEDIUM]. 곁들여, 서버가 확신하지 못한 답(confident=false)은 후보가 하나여도 자동으로 고르지 않고 되묻는다. 예전에는 하나면 확정으로 삼았는데, 서버는 그때 되물을 문장까지 같이 보낸다 — 묻고 싶다는 뜻이다. 그걸 우리가 대신 고르면 맵기를 못 드시는 분의 주문이 물어본 적도 없이 넘어간다. 아니라고했나() 는 부르는 곳이 없어져 삭제. 부정어 표 자체는 말했나() 가 계속 쓴다.
취소하면 서버가 pairing 을 폐기하는데 프론트가 그 값을 계속 들고 있어서, 다른 주문표로 들어갈 때 PAIRING_NOT_FOUND 가 '연결 정보를 찾을 수 없습니다' 라는 개발자 말로 화면에 떴다. 두 겹으로 막는다. requestMapping 이 연결끝남 을 먼저 보고(승인 경로에만 있던 검사다 — 화면은 매핑부터 부르므로 사용자가 먼저 닿는 쪽이 안 막혀 있었다), 거절하면 App 이 연결 상태를 놓는다. 머리의 뒤로가기와는 갈라 뒀다 — 한 콜백으로 묶으면 그냥 나간 사람의 연결까지 끊는다. 끝난 이유에 따라 문구도 나눈다. 취소하고 나온 사람에게 '연결 시간이 만료되었습니다' 는 사실이 아니다. Closes #146
* feat: 말로 만드는 주문표에서 앞 질문으로 돌아갈 수 있습니다 한 칸씩 묻는 흐름에 앞으로 가는 길만 있었습니다 — 말하기·건너뛰기뿐이라, 두 번째 칸에 잘못 답하고 나면 되돌릴 방법이 없었습니다. 화면 뒤로가기는 흐름을 통째로 나가므로 되돌리기가 아니라 포기입니다. ■ 두 개의 문을 같이 냅니다 · '앞 질문' 단추 — 눈으로 보고 누르는 분 · "뒤로"·"이전"·"되돌려" — 손을 안 쓰는 분 뒤쪽이 이 변경의 핵심입니다. 화면을 못 보는 분에게는 말이 앞 칸을 고칠 유일한 길이라, 단추만 두면 그분들에게는 여전히 되돌리기가 없습니다. ■ 답 맞추기보다 먼저 봅니다 "뒤로" 를 답으로 읽어 볼 기회를 주면 안 됩니다. 보기와 우연히 겹칠 때 되돌아가는 대신 값이 들어갑니다. 그래서 보기 맞추기·예아니오·서버 맵기 매칭 셋보다 앞에 둡니다. ■ 답은 안 지웁니다 돌아간 칸에 고른 값이 칩으로 눌려 있어야 무엇을 골랐었는지 보고 고칠 수 있습니다. 다시 답하면 덮입니다. ■ 첫 칸에서는 단추를 아예 안 내밉니다 — 눌러도 아무 일이 없는 단추를 두면 앱이 멎은 줄 압니다. 말로 왔을 때는 "여기가 첫 질문이라 더 앞으로는 갈 수 없어요" 라고 답합니다. 조용히 무시하면 못 들은 줄로 압니다. ■ 표를 짧게 둡니다 여기 걸린 말은 답이 아니라 명령이 됩니다. 실제로 나올 법한 답이 걸리면 고르려던 값 대신 앞 칸으로 튕깁니다. 영어는 낱말째로 봅니다 — 순살의 영어 답 'boneless' 안의 글자가 걸리면 안 됩니다(#39 와 같은 이유). 시험 5건. typecheck · 457 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: 말이 끝나면 알아서 다음 질문으로 넘어갑니다 한 번 '말하기' 를 누르면 그 뒤로는 손을 안 댑니다. 다섯 칸을 답하는 데 열 번 눌러야 하던 것이 한 번이 됩니다. 화면을 못 보는 분에게는 그 열 번이 답하는 일보다 오래 걸리는 장벽이었습니다. 단추가 어디 있는지 찾는 데 걸리는 시간이 말하는 시간보다 깁니다. ■ 소리는 밖으로 안 나갑니다 (api/vad.ts) 말이 끝났는지는 기기 안에서만 봅니다. AnalyserNode 가 브라우저 안에서 파형을 재고, 우리는 그 크기 숫자만 읽습니다. 녹음이 서버로 가는 것은 지금과 똑같이 한 답에 한 번뿐이고, 이 파일은 아무것도 보내지 않습니다. (AudioWorklet 이 아니라 AnalyserNode 입니다. 둘 다 기기 안에서 돌지만 AnalyserNode 는 파일을 따로 두지 않아도 되고 50ms 마다 한 번 읽는 정도는 본 스레드에서도 가볍습니다.) ■ 기기마다 조용한 정도가 다릅니다 "이 값을 넘으면 말소리" 로 못 박으면 조용한 방과 식당 앞이 열 배 넘게 차이 나서, 한쪽은 아무 말도 못 듣고 다른 쪽은 계속 말하는 중이라고 봅니다. 처음 400ms 로 그 자리의 바탕 소음을 잡고, 거기서 얼마나 솟는지로 봅니다. ■ 다음 질문을 읽어 준 뒤에 마이크를 엽니다 안 기다리면 두 가지가 한꺼번에 어긋납니다. 듣기시작() 이 읽던 것을 끊어서 무엇을 묻는지 못 들은 채 답해야 하고, 안 끊더라도 스피커 소리를 마이크가 주워들어 인식을 망칩니다. speech.ts 에 다읽을때까지() 를 뒀습니다. ■ 헛돌지 않게 자리를 뜨면 "말이 없다 → 다시 듣기" 가 끝없이 돕니다. 잇달아 두 번 못 들으면 이어 듣기를 끄고 단추를 남깁니다. 마이크가 막힌 경우(권한없음)는 몇 번을 열어도 같으므로 바로 접습니다. ■ 못 하는 자리에서는 조용히 물러납니다 오디오를 못 다루는 브라우저에서 던지면 말로 답하는 길이 통째로 막힙니다. 그때는 vad 가 아무 일도 안 하고, 사람이 '그만 듣기' 를 누르는 길과 최대 녹음 시간(15초)이 그대로 남습니다. ■ 시험 9건 상수를 일부러 망가뜨려 헛통과가 아닌 것을 확인했습니다 — 무음 판정을 1200ms → 100ms 로 바꾸니 '숨 고르기를 끝으로 보면 안 된다' 가, 짧은 소리 거르기를 150ms → 50ms 로 바꾸니 '툭 하는 소리는 말로 안 본다' 가 각각 정확히 하나씩 깨졌습니다. typecheck · 466 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: 부르면 시작합니다 + 리뷰 5건 반영 ■ 부르면 시작합니다 (api/wake.ts) 첫 화면에서 "키오브릿지" 라고 부르면 손을 한 번도 안 대고 시작합니다. 이어 듣기로 답하는 동안은 이미 손을 안 대게 됐고, 맨 처음 한 번이 남아 있었습니다. 평소에는 아무것도 안 보냅니다. 상시 대기는 기기 안에서만 돌고(vad), 사람이 실제로 말한 뒤 조용해진 순간에만 그 한 토막을 서버로 보내 무슨 말이었는지 묻습니다. 조용한 동안에는 한 바이트도 안 나갑니다. 그러니 "웨이크워드 인식을 기기 안에서 한다" 는 말은 아닙니다. 기다리는 일이 기기 안에서 돌고, 말이 났을 때만 서버에 묻습니다. 화면 안내도 그렇게 적었습니다 — 못 하는 것을 한다고 말하지 않습니다. 켜지는 조건 셋: · 소리로 주고받기가 켜져 있다 · **이미 동의했다** — 동의는 건너뛸 수 없는 관문이라 부르는 말로 넘어가게 두지 않습니다 · 브라우저가 묻지 않고 마이크를 준다(permissions API 가 granted) 듣고 있으면 화면이 밝힙니다. 그만뒀을 때도 말합니다 — 조용히 멈추면 아직 듣는 줄 알고 계속 부릅니다. 끝없이 돌지 않게 두 그물을 칩니다: 물어본 횟수 12번, 전체 3분. ■ 리뷰 5건 🔴 자동 진행 뒤의 답이 앞 칸에 들어가던 것 이어서예약() 이 어느 한 렌더의 함수라, 거기서 건 setTimeout 콜백이 그 렌더의 듣기시작·지금축 을 그대로 쥐고 있었습니다. set칸 을 올린 뒤 700ms 에 깨어난 콜백은 **바뀌기 전 축**으로 듣기를 시작합니다. 화면은 k+1 번째를 묻는데 답은 k 번째 축에 들어갑니다. 회차 가드도 못 막습니다 — 듣기시작() 이 스스로 회차를 올리고 그 값으로 부르니까요. 이 앱이 가장 피해야 할 결함입니다(고르지 않은 조건이 주문표에 들어감). 예약은 표시만 남기고, 실제로 거는 일은 칸이 새로 그려진 뒤 useEffect 가 합니다. 정리 함수가 타이머도 걷어 갑니다. 🟠 바탕 소음을 평균으로 잡던 것 처음 400ms 안에 말을 시작하면 그 말소리가 바탕을 밀어 올려, 문턱이 실제 목소리보다 높아집니다. 그 뒤로 아무것도 말로 안 잡히고 녹음이 버려집니다. 이어 듣기에서는 흔한 자리입니다 — 안내가 끝나자마자 바로 답하는 것이 정상이니까요. 아래쪽 4분의 1 값으로 바꿨습니다. 🟠 다읽을때까지 가 '아직 안 읽음' 과 '다 읽음' 을 구분 못 하던 것 둘 다 speaking·pending 이 false 입니다. 읽기가 걸리기 전에 물어보면 곧바로 "다 읽었다" 고 답해서, 마이크가 안내보다 먼저 열립니다. 시작기다림 인자를 뒀습니다. 🟠 여러 개 고르는 칸에서 음성만으로 못 나가던 것 카페의 시럽 축은 값을 넣어도 그 자리에 머뭅니다. '건너뛰기' 는 화면 단추뿐이라, 손을 안 쓰는 분은 같은 질문만 되풀이해 듣다가 이어 듣기가 꺼집니다. "다음"·"됐어요" 를 알아듣게 했습니다. 🟡 듣는 중 안내 두 줄이 영어 화면에 우리말로 남던 것 — 표로 옮겼습니다. 시험 wake 11건 + 다음가자 5건. typecheck · 482 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: 새 문구가 표에서 빠지지 않게 지킵니다 (리뷰 1건 — 반영 대신 확인) 코드래빗 🟡 1건: "각 문자열을 t(...) 로 렌더링하세요." ■ 이 건은 안 고칩니다. 지적이 이 앱의 구조와 안 맞습니다 이 앱은 문장마다 t() 를 두르지 않습니다. 화면을 그린 뒤 DOM 을 훑어 한 번에 바꿉니다(i18n/apply.ts 머리말에 그 판단과 이유가 적혀 있습니다) — 화면에 나가는 우리말이 600줄 가까이 되고 App.tsx 한 파일에 흩어져 있어서, 손으로 두르면 빠뜨리는 자리가 생기고 그 자리는 화면을 열어 보기 전까지 아무도 모르기 때문입니다. 지적받은 세 자리와 **똑같은 꼴**이 이미 있습니다(App.tsx 1360행): 못들음 === "권한없음" ? "마이크를 쓸 수 없어요. 위에서 손으로 골라 주세요." ← t() 없음 : ... 그리고 그 문장은 표에 있습니다(en.ts 47행). 즉 이 구조에서 필요한 것은 t() 가 아니라 **표에 열쇠가 있는지**이고, 세 문장 모두 이미 넣어 뒀습니다. ■ 대신 그 규칙을 시험으로 잠급니다 말로만 "괜찮다" 고 하면 다음에 또 같은 지적이 옵니다. 사람 눈 대신 지키게 i18n/en.test.ts 를 뒀습니다. · 이번에 더한 여섯 문장이 표에 있고, 옮긴 말에 우리말이 안 남았는지 · 표 전체에 반쯤 옮긴 항목이 없는지 (자리표시자 이름은 뺍니다 — {전체}·{것들} 은 화면에 안 나가고 tf() 가 값으로 갈아 끼웁니다) · 빈 값으로 둔 항목이 없는지 (빈 값이면 그 자리가 통째로 사라져, 우리말로 남는 것보다 나쁩니다) '앞 질문' 열쇠를 일부러 빼서 시험이 실제로 잡는 것을 확인했습니다. jsdom 이 없어 DOM 훑기 자체는 시험하지 않습니다. 그건 이미 쓰이고 있는 경로라(기존 문장들이 영어 화면에서 바뀝니다) 새로 지킬 것은 표 쪽입니다. typecheck · 487 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: 동의를 말로 받고, 소리 안내를 처음부터 켭니다 이걸로 손을 한 번도 안 대고 시작하는 길이 이어집니다. 앱 열림 → (소리 안내 켜져 있음) → "이용에 동의하시겠어요?" → "네" → 동의됨 → "키오브릿지" → 시작 ■ 동의를 말로 받습니다 (api/wake.ts 의 말로예아니오받기) 동의는 건너뛸 수 없는 관문인데 그 관문이 체크박스라, 화면을 못 보는 분은 손을 안 대고는 지날 수 없었습니다. 부르는 말을 알아듣게 만들어 놓고도 그 앞에서 막혀 있던 셈입니다. 물음은 화면이 글로 띄웁니다. 소리 안내가 그 글을 읽고, 다 읽힌 뒤에 답을 듣습니다 — 물음을 소리로 따로 만들지 않는 이유는, 화면에 없는 말을 귀로만 들려주면 눈으로 보는 사람과 다른 것을 듣게 되기 때문입니다. '아니오' 는 답이 없는 것이 아니라 답입니다. 거기서 또 물으면 붙잡는 것이 되므로 다시 묻지 않습니다. 못 알아들었을 때만 다시 묻고 세 번까지입니다. ■ 답하기 전에 목소리가 한 번 나갑니다 — 숨기지 않습니다 이 앱은 녹음을 서버로 보내 인식합니다. 그래서 동의를 말로 받으려면 그 한마디가 동의 전에 서버를 다녀와야 합니다. 순서가 뒤집힙니다. 물음 문장이 그 사실을 같이 말합니다: "이용에 동의하시겠어요? 동의하시면 '네' 라고 말씀해 주세요. 대답은 알아듣기 위해 서버로 한 번 전송됩니다." 그렇게 적어 두고 사용자가 말할지 말지를 고르게 하는 것이, 조용히 받아 두는 것보다 낫습니다. 시험이 이 문구에서 '서버로' 가 빠지지 않는 것을 지킵니다. ■ 소리로 듣고 답하기를 처음부터 켭니다 이 스위치는 화면을 못 보는 분에게 가장 필요한 것인데, 켜려면 화면에서 그 스위치를 찾아 눌러야 했습니다 — 읽어 줘야 읽을 수 있는 사람은 켜러 갈 수가 없습니다. 첫 화면으로 옮겨 놓은 것도 그래서였고, 이번에 마지막 한 걸음까지 없앱니다. 맞바꾸는 것이 있습니다. 눈으로 보는 분에게는 열자마자 화면을 읽는 소리가 납니다. 대신 끄는 길은 늘 첫 화면 맨 위에 있고, 한 번 끄면 남습니다. 마이크가 저절로 열리지는 않습니다. 브라우저가 이미 허용해 둔 자리에서만 듣고, 그 전에는 아무것도 안 듣습니다. ■ 곁들여 — 기본값이 falsy 하다는 가정을 걷어냅니다 voiceGuide 를 기본 켜짐으로 바꾸니 session.ts 의 '남길 것이 있나' 가 깨졌습니다. 값이 true 인지로 '건드렸다' 를 판단하고 있어서, 아무것도 안 한 사람의 빈 이용이 전부 저장됐습니다. 같은 자리가 두 번째입니다 — language 가 들어왔을 때도 같은 원인이었고 (기본값 "ko-KR" 이 truthy), 그때는 language 만 예외로 두어 막았습니다. 예외를 하나씩 더하는 대신 기준을 바꿉니다: '건드렸다' 는 **기본과 다르다** 는 뜻이지 '켜져 있다' 는 뜻이 아닙니다. 기본 켜짐인 것을 끈 사람도 건드린 것이고, 그 선택은 남겨야 합니다. 옛 기준으로 되돌려 새 시험 둘이 실제로 깨지는 것을 확인했습니다. typecheck · 491 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: 자리표시자 검사를 조입니다 (리뷰 1건) 코드래빗 🟡 1건. 제가 만든 시험이 헐거웠던 것이 맞습니다. '옮긴 말에 우리말이 섞였나' 를 볼 때 {...} 를 **통째로** 지우고 봤습니다. 그러면 원문에 없던 이름을 옮긴 말에 적어 두어도 지나갑니다. "앞 질문": "Previous {한국어} question" ← 통과했음 tf() 는 원문에 있는 이름으로만 값을 갈아 끼우므로, 저 {한국어} 는 영영 안 채워집니다. 영어 화면에 우리말이 글자 그대로 뜨는데 시험은 초록입니다. ■ 두 가지로 나눠 봅니다 ① 자리표시자가 원문과 옮긴 말에서 같은가 (새 시험) 다른 이름이 들어 있으면 그 자리는 안 채워지고, 빠져 있으면 값이 통째로 사라집니다. 둘 다 화면에서 바로 보이는 결함입니다. ② 우리말 검사에서는 **원문에 있는 이름만** 뺍니다 없던 이름은 안 빼므로, 그 안에 우리말이 있으면 걸립니다. ■ 헛통과가 아닌 것을 확인했습니다 "앞 질문" 값에 {한국어} 를 넣어 봄 → 시험 3건 실패 "{메뉴} — {글}" 에서 {글} 을 빼 봄 → 새 시험 1건 실패 typecheck · 494 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: 마이크가 아직 허락되지 않았을 때 아무 말도 안 하던 것 노트북에서 "동의하시겠어요?" 도 "부르는 말을 기다리고 있어요" 도 안 들린다는 말을 듣고 실제 페이지를 열어 봤습니다. 원인은 제 설계였습니다. 마이크 권한: denied 동의 물음 보임: false 부르기 안내 보임: false 권한이 granted 가 아니면 **아무것도 안 하고 아무 말도 안 했습니다.** 그래서 처음 오신 분에게는 말로 시작하는 길이 있는지조차 안 보였고, 말을 걸어도 반응이 없었습니다. 화면은 멀쩡한데 아무 일도 안 일어나면 사람은 앱이 고장 났다고 여깁니다. 이 앱이 여러 곳에서 피해 온 자리를 제가 만들었습니다(listen.ts·speech.ts 의 "되는지 먼저 보고 내민다" 와 같은 얘기입니다). ■ 상태를 보고 그에 맞는 말을 합니다 됨 부르는 말을 기다린다 (지금까지와 같음) 물어봐야함 "마이크를 한 번 허락해 주세요" + [마이크 허락하기] 단추 막힘 "주소창 옆 자물쇠에서 허용하시면 됩니다" 모름 물어봐야함 과 같이 다룬다 '모름'(Permissions API 가 없는 브라우저)을 막힘으로 치지 않습니다. 모른다고 길을 닫으면, 될 수도 있는 기기에서 아무 말 없이 아무것도 안 하게 됩니다. ■ 단추가 필요한 이유 브라우저는 **사람이 누른 자리에서만** 마이크를 물어 줍니다. 우리가 화면을 그리다가 부르면 조용히 막힙니다 — 물어보지도 못하고 아무 일도 안 일어납니다. 그 한 번이 지나면 다음부터는 손을 안 대도 됩니다. 허락을 받으면 연 김에 바로 끕니다 — 여기서 하려는 일은 녹음이 아니라 허락을 받는 것뿐이고, 안 끄면 듣고 있지도 않은데 마이크 표시등이 켜진 채로 남습니다. ■ 막혔을 때도 어디서 푸는지 말합니다 "안 됩니다" 만 말하고 끝내면 사용자는 할 수 있는 일이 없습니다. 시험이 이 문구에서 '자물쇠' 가 빠지지 않는 것을 지킵니다. ■ 확인 실제 브라우저(denied 상태)에서 막힘 안내가 뜨는 것을 봤습니다. 전에는 같은 자리에서 아무것도 안 나왔습니다. '물어봐야함' 갈래는 이 창을 그 상태로 만들 수 없어 눈으로는 못 봤습니다. 배선과 타입만 확인했습니다. typecheck · 494 tests · build 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
트리는 dev 그대로입니다. 이 머지는 내용을 하나도 안 바꾸고 이력만 잇습니다 (git diff origin/dev 가 비어 있는 것으로 확인했습니다). ■ 왜 또 필요했나 같은 일이 전에도 있었고 그때 a5ac006 이 봉합했습니다. 그 커밋에 결론이 적혀 있습니다 — "릴리스 PR 은 앞으로 merge commit 으로 머지할 것." 그런데 #143 이 스쿼시로 머지됐습니다(부모 1개). 스쿼시는 dev 의 커밋들을 main 에 새 SHA 하나로 옮겨 놓으므로, git 이 보기에 두 갈래는 같은 내용을 따로 만든 셈이 됩니다. 그래서 다음 릴리스에서 같은 파일이 통째로 충돌합니다. 이번에 충돌한 넷도 전부 그것입니다 — spicy.ts·spicy.test.ts 는 add/add, voice.ts·App.tsx 는 content 충돌인데, 어느 쪽도 실제로 갈라진 작업이 아닙니다. main 에는 릴리스 말고 들어간 것이 없습니다(dev..main 확인). ■ 다음 릴리스부터 #148 은 **merge commit** 으로 머지해야 합니다. 스쿼시로 넣으면 다음 릴리스 때 이 봉합을 또 해야 합니다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
dev→main. 프론트만 바뀝니다.들어가는 것
"연결 정보를 찾을 수 없습니다"가 안 뜹니다 (#146)#145 가 여는 길
앞 질문단추와"뒤로"·"이전". 여태 앞으로 가는 길만 있어서, 두 번째 칸에 잘못 답하면 되돌릴 방법이 없었습니다AnalyserNode). 다섯 칸에 열 번 누르던 것이 한 번이 됩니다"키오브릿지"로 시작 — 상시 대기는 기기 안에서 돌고, 말이 났을 때만 그 한 토막을 서버에 묻습니다. 조용한 동안에는 한 바이트도 안 나갑니다"네"로 — 물음이 "대답은 서버로 한 번 전송됩니다" 까지 말합니다. 순서가 뒤집히는 것을 숨기지 않습니다마이크 허락하기단추를 내밉니다. 여태 이 자리에서 아무 말도 안 해서, 처음 오신 분에게는 이 길이 있는지조차 안 보였습니다백엔드는 안 바뀝니다
backend/**변경이 0건이라 Backend CD 가 안 돕니다. Vercel 프론트 배포만 나갑니다. 지난 릴리스처럼 EC2 재배포를 기다릴 필요가 없습니다.이 릴리스가 고치는 것
확신 못 한 답을 앱이 대신 고르던 것 (#142)
서버가
confident: false로 답하면서 후보를 하나만 줄 때, 앱이 그것을 확정으로 삼았습니다. 그런데 서버는 그때 되물을 문장까지 같이 보냅니다.서버가 묻고 싶다고 한 것을 앱이 대신 골랐습니다. 맵기를 못 드셔서 그렇게 말한 분의 주문이 물어본 적도 없이
상관없음으로 넘어갔습니다. 이제confident가 참이 아니면 개수와 상관없이 되묻습니다.같은 PR에서 프론트의 부정어 방어를 걷어냈습니다 — 서버(#138)가 부정을 읽게 됐고, 같은 판단이 두 곳에 있으면 어긋났을 때 어느 쪽이 옳은지 알기 어려워집니다.
취소 뒤에 개발자 오류가 뜨던 것 (#147)
취소하면 서버가 pairing 을 폐기하는데 프론트가 그 값을 계속 들고 있어서, 다른 주문표로 들어갈 때
PAIRING_NOT_FOUND가 그대로 화면에 올라왔습니다.매핑 경로에 검사를 넣고(승인 경로에만 있었습니다 — 화면은 매핑부터 부르므로 사용자가 먼저 닿는 쪽이 안 막혀 있었습니다), 거절하면 연결 상태를 놓습니다. 끝난 이유에 따라 문구도 나눴습니다 — 취소하고 나온 분께 "연결 시간이 만료되었습니다" 는 사실이 아닙니다.
배포 뒤 확인
1. 맵기 — 부정과 되묻기
node -e "fetch('https://api.hyunwoocha.site/internal/spicy-level/match',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({text:'안 매운 거'})}).then(r=>r.json()).then(console.log)"confident: false여야 합니다. 백엔드는 이번에 안 바뀌므로 지난 릴리스 상태 그대로여야 정상입니다.2. QR 페어링
네 항목 모두 ✓.
⚠는 통과가 아닙니다 — 서버가 답을 못 준 것이라 막혔는지 아직 모른다는 뜻입니다.3. 취소 흐름 (#147)
QR 연결 → 주문표A 열기 → 취소 → 다른 주문표로 주문 시도.
"연결 정보를 찾을 수 없습니다"가 뜨면 안 됩니다. "이 연결은 다 쓰셨어요" 와 QR 다시 찍기 안내로 가야 합니다.4. 음성 (#145)
자동으로 확인할 방법이 없습니다 — 마이크가 필요합니다. 휴대폰에서 직접 해 주세요.
sessionStorage는 새로고침으로 안 지워집니다)"이용에 동의하시겠어요?"가 들리면 → "네""부르는 말을 기다리고 있어요"가 들리면 → "키오브릿지"말하기를 한 번만 누르고, 말이 끝날 때마다 다음 질문으로 넘어가는지"뒤로"로 앞 질문에 돌아가는지마이크 권한이 아직 없으면 화면이
마이크 허락하기단추를 내밉니다. 그 한 번이 지나면 다음부터는 손을 안 대도 됩니다.데모 전에 알아 둘 것
#145가 들어가면 소리 안내가 기본으로 켜집니다. 앱을 열자마자 화면을 읽는 소리가 납니다 — 의도된 동작입니다. 끄는 스위치는 첫 화면 맨 위에 있습니다.
https://에서만 마이크가 열립니다.192.168.x.x같은 주소로는 음성이 안 됩니다.