본문 바로가기

TIL

[TIL 2025. 06. 05] 음성인식으로 장바구니 업데이트하기

elder_menu.html에서 음성인식 버튼을 누르면 각각 음성만으로 "메뉴 재추천", "결제하기"로 넘어가는 것은 이미 구현이 끝났다. 문제는 "장바구니"기능과 관련해서 발생했었는데, 이를 해결하기 위해 장바구니 기능에 대해서 Redis를 적용하기로 한 것이었다.

 

장바구니 기능을 Redis로 저장하고 관리하게끔 세팅했으니, 이제 다시 본궤도로 돌아와서 음성인식으로 "장바구니"기능을 사용할 수 있도록 해볼 차례다.

 

애초에 장바구니 기능과 관련해서 Redis가 필요하겠다 생각하게 되었던 배경은 다음과 같다.

  1. cart_ai()를 테스트하다 보니 전반적으로는 response의 결과값이 생각보다 정확했는데, delete action에 있어서는 확실히 정확도가 떨어지는 이슈가 있었음_ ex. "아메리카노 1잔으로 줄여줘", "아메리카노 1잔만 줄여줘"의 구분
  2. delete에 대한 response의 정확도를 높이기 위해서는 "현재 장바구니 상태"를 gpt에게 매개변수로 넘겨줘야 한다고 판단함
  3. 당시 상황은 js내의 로직으로만 장바구니가 구현된 상태였으므로, 장바구니 기능에만 한정해서 Redis를 사용하기로 함 (DB에 저장하는 방식을 선택하지 않은 이유는 앞선 포스팅에서 설명한 바 있음)

 

따라서 이제는 Redis에 저장된 "현재 장바구니 상태"정보를 cart_ai()에서 사용할 수 있도록 해당 코드를 수정해줄 차례다.

 


html에서 "음성인식 재시작"버튼을 누르는 것부터 진행되어야 할 흐름을 정리해보면 다음과 같다.

  1. "음성인식 재시작" 버튼을 클릭하고, 장바구니 메뉴에 대한 추가, 수정, 삭제 요청(=inputText)을 음성으로 한다
  2. request_type()를 실행하고, 결과로 types가 "cart"로 반환한다 (인자로 받은 recommended_menu도 반환함)
  3. cart_ai()를 실행하고, 결과로 "calculate", "menu", "recommended_menu"를 반환한다
  4. cart_ai()의 반환값을 사용해서, current_cart_get의 "name"과 "quantity"를 update해준다
  5. update해준 장바구니 현황을 다시 Redis에 저장해준다 (-> Redis에는 항상 최신의 장바구니 상황만이 담기며, 전체적인 value값을 update마다 hset해주는 것으로 로직을 구성함 = Cart객체를 만들고 add_to_cart() 호출)
  6. html상에서 해당 장바구니 update내용이 반영되도록 axios get요청으로 Redis에서 저장정보를 받아오고, 이를 response로 돌려줌 (선: get_cart(), 후: view_cart())
  7. updateCartDisplay()를 통해 받아온 장바구니 정보를 html상에 반영해줌

 

이 과정에서 6+7 과정이 removeItem()시에도 이미 똑같이 쓰인 바가 있고, 다른 api들에서도 자주 사용될 것아서 해당 과정들을 묶어서 별도로 "refreshCart()"라는 함수를 만들어줬다.

 

즉, updateCartDisplay()는 "currentTotal"을 포함해서, html상의 row를 붙여주는 역할을 하므로, elder_menu.html이 로드되거나 새로고침될 때 사용된다. 반면에 refreshCart()는 장바구니 상태를 저장하는 Redis의 상태가 변경될 때마다 이를 즉각적으로 "새로고침 없이" 반영될 수 있도록 axios get요청을 포함하는 함수이다.

 

그리고 수많은 print문과 console.log를 찍어가며 코드를 작성하고, 들어오는 data형식들을 확인한 끝에 음성인식으로 장바구니를 udpate하는 기능을 구현하는 것에 성공했다..ㅎㅎ!!

 


 

사실 위의 과정에서 가장 까다로웠던 것은 cart_ai()의 프롬프트를 적절하게 수정해서 우리가 원하는 형태로 답변을 받아오는 동시에 대답의 정확도를 올리는 것이었다.

 

프롬프트에 적어준다고 하여 항상 그에 맞는 gpt의 response가 반환되는 것이 아니므로, 어떻게 하면 3번 날 오류를 1번 정도까지 줄일 수 있을지에 대한 다양한 시도가 있었다.

 

원래 처음 cart_ai()를 작성할 때는 response로 "mene", "quantity", "action"의 세 가지 항목을 받아오도록 했었다.

하지만 아무리 action을 "add"와 "delete"로만 프롬프트에 지정해줘도 inputText의 표현에 "action"항목의 response가 너무 많은 영향을 받아 정확도가 떨어지는 문제가 심각했다..

 

그래서 다음에 시도한 방식은 response를 "calculate", "menu" 두 가지로 수정하는 것이었다.

특히, calculate는 add나 delete를 포함하지 않고, +와 -의 기호를 포함해서 response를 반환하도록 프롬프트를 작성했다.

이렇게 수정하자 action이 inputText에 지대한 영향을 받던 것은 수정되고, 이전보다는 정확도가 개선되었다.

 

하지만 그럼에도 "~로 바꿔줘"와 같은 inputText(=음성인식)에 대해서는 +와 -와 뒤에 붙는 quantity까지 정확하지는 못함을 발견했다. (부호와 quantity는 공백으로 split해서 사용했다_프롬프트 자체에 둘 사이에 공백을 넣어서 결과값을 반환해달라고 직접적으로 글로 적는 것보다 -> 프롬프트에 제시해준 예시에서 부호와 quantity사이에 공백이 있도록 작성하는 것이 더 잘 반영됨)

 

cart.js


와중에 cart_ai()에 매개변수로 전해주고 있는 current_cart_get의 type을 print문으로 찍어봤다가 해당 데이터가 객체(obj)로 넘어가고 있음을 발견했다. 그래서 gpt가 해당 장바구니 상황을 정확히 반영하지 못한 것인가 해서 이를 str형식으로 바꿔서 전달하는 동시에 프롬프트를 수정해서 해당 current_cart_get을 고려해서 calculate항목에 대해서 나름의 결과값을 반환하도록 했다.

 

다시 말해 이전에는 현재 장바구니에 아이스 아메리카노가 1잔 담겨있는 상태에서 "아이스 아메리카노 3잔으로 늘려줘"에 대한 response에서 calculate의 값으로 "+ 2"을 기대했다면, 이제 수정된 프롬프트에서는 calculate값으로 "3"을 기대하는 것이다.

 

이렇게 calculate에 대한 반환값을 action(=+,-)와 quantity로 구분해서 지시하는 것보다 gpt에 판단을 일임하는 것이 오히려 정확도가 높았다. 물론 gpt에 대한 의존도가 더 높아진다는 문제점이 있기는 하나, 정확도 면에서 보자면 확실히 좋은 선택이었다.

 

실제로 "아이스 아메리카노를 3잔으로 늘려줘"나 "아이스 아메리카노를 3잔 줄여줘"에 대한 calculate값이 각기 다르게 잘 구분되어서 반환되는 것을 여러 번의 test로 확인할 수 있었다.