회사에서 진행한 프로젝트인 나인트리의 키오스크 셀프 체크인 / 체크아웃 프로그램 개발이 거의 완료되어, 오늘 인사동 나인트리 호텔 로비에 설치를 하고 왔다.
출장이 끝나고 나인트리에서 딱 나왔는데 쌈지길이더라. 위치가 진짜 좋은 호텔인 것 같다. ㅋㅋㅋ
아무튼...
나는 키오스크의 프론트앤드 프로그램 구현을 담당했다.
사실 나는 이전에도 몇 번 키오스크 프론트앤드 프로그램 구현을 담당했었는데,
이전 키오스크 프로그램에 도입하지 않았던 여러가지 기술을 이번에 도입해 보았다.
그래서 새로 도입했던 기술들을 키오스크 프로그램에서 사용했을 때의 장단점에 대해 정리해보고자 한다.
아난티 키오스크 개발했을 때는 React의 context API로 상태 관리를 했었다. (첫 입사했을 때 회사에서는 상태관리를 Context API로만 했다.) Context API는 React에서 제공하는 기능이어서 특별히 라이브러리를 다운받을 필요가 없다는 장점이 있다. 하지만 상태 관리 라이브러리들이 괜히 더 나오는 게 아니다. ㅋㅋㅋ 코드가 복잡해지는 단점이 있다.
메종 글래드 제주의 키오스크에서는 zustand로 전부 상태관리를 했었다. zustand는 contextAPI에 비해 훨씬 사용하기 쉬웠다. 게다가 persist라는 미들웨어를 사용할 수 있다는게 엄청난 장점이었다.
persist 미들웨어를 사용하면 로컬 스토리지, 세션 스토리지, indexedDB를 아주 짧은 코드로 이용할 수 있다. (예시로 localStorage.setItem getItem 이딴거 안해도 알아서 state가 스토리지와 자동으로 연결된다. )
zustand devTool도 있는데 사실 context API도 크롬 확장프로그램으로 다운받을 수 있어서 이건 패스
react query도 사용했는데, react query는 정말 개꿀 라이브러리이다. 내사랑 react query... ㅋ
react query를 쓰면 코드가 훨~씬 깔쌈해진다.
API를 받은다음에 성공 응답을 받았을 때 state를 set해줄 필요도 없었고,
api의 응답을 계산하는 로직은 query hook의 selectFn으로 분리할 수도 있었다.
한 번 요청했던 API라면 불필요하게 다시 요청을 하지 않는다거나, n분에 한 번 API호출을 다시 하는 경우에 대해서도 너무 쉽게 처리된다.
프로젝트에서 API 요청중일 때 로딩 스크린을 띄워주는 코드도 useIsMutationg, useIsFetching 훅을 사용하면 너무 깔끔하게 구현이 된다.
키오스크가 첫 화면으로 갔을 때는 모든 서버 응답과 관련된 상태값을 초기화해야만 하는데, queryClient.removeQueris를 사용하면 한 줄의 코드로 쉽게 초기화가 가능하다.
아, 중간에 API 페이로드가 이상하게 호출이된다거나 하는 문제점이 있었는데,
query를 사용할 때 enabled 값을 추가해서 완전히 해결되었다.
예를들어 첫 화면으로 돌아가기 전에 모든 state값을 날렸는데, 해당 state값을 파라미터로 하는 쿼리에서는 다른 페이로드로 쿼리가 재요청 될 것이다. 이 경우에 해당 state값을 enabled에 반드시 설정해주어야 한다.
키오스크는 키보드가 없기 때문에 무조건 가상 키보드를 만들어줘야 한다.
처음에는 react-simple-keyboard라는 라이브러리를 사용했는데,
이 라이브러리에서 한글 입력 기능을 사용했을 때 앙 <- 이렇게 입력하면 아ㅇ <- 이렇게 입력되는 문제가 있었다.
그래서 hangul-js 라이브러리를 사용해서 이 문제를 해결해야 했는데,
이럴거면 그냥 키보드 컴포넌트를 직접 만드는게 낫겠다는 생각을 했다.
그리고 CSS도 라이브러리의 CSS를 사용하려면 정해진 클래스 네임을 찾아서 설정해줘야 하는데
이게 더 번거롭다.
키보드 컴포넌트를 직접 만드니까 프로젝트에 만들어놓은 여러가지 컴포넌트들과 같이 연동해서 사용할 수 있어서 오히려 편했고, 내가 원하는 대로 커스텀하기 오히려 쉬웠다.
이번에 작업한 키오스크 프로그램에는 추가 정보를 입력하는 화면이 있는데, 이 form을 react hook form으로 관리해보고자 했다.
react hook form을 사용했을 때 가장 편리했던 점은 유효성 검사가 아주 간편하다는 점, 컴포넌트 내에 useState와 useRef 등을 남발하지 않아도 된다는 점 등이 있고, devtool도 지원한다. (근데 이거 배포해도 남아있더라.ㅠㅠ 반드시 개발모드에서만 나타나도록 설정해주어야 한다.)
하지만 마냥 좋지도 않았다. input 태그가 focus되었을 때 키오스크에서는 반드시 키보드가 나타나야 했는데, 이를 위해서 결국 input태그에 state가 필요했다. 그래서 굳이 watch 메소드를 이용해야 했는데 아무튼 이 라이브러리를 사용하려고 공부하는 것도 일이었다.
그리고 키오스크 프로그램에서는 mutation에서 mutate 를 사용하지 않고, mutateAsync를 사용했다. 그 이유는, 예를들면 객실키를 반납했다. -> 예약자 정보를 조회한다 -> 예약자 정보의 주차 정보를 조회한다 -> 체크아웃을 한다. 이런 로직을 구현할 때, API가 순서되로 호출되어야 하는데 mutate는 이게 어렵기 때문이다.
컴포넌트 (버튼, 토글, 드롭박스, 체크박스, input, 모달, toast...)를 구현하고, storybook에 문서화를 해두었다.
컴포넌트 별고 기능을 개발하다 보면 내가 만든 컴포넌트의 props에 어떤 값을 줘야 하는지 까먹기 일쑤이다. 코드에 주석을 달아놓을 수 있지만, 스토리북을 이용하면 훨씬 편리하고, props값에 따른 컴포넌트의 UI 변화도 시각적으로 확인되어 편리하다.
이 컴포넌트로 만들 수 있는 예시에 대해서도 정리해둘 수 있다.

tags: ['autodocs'] 로 props에 대한 설명과 예시 데이터를 정리할 수 있다.

스토리북 도입은 참 쉽지 않은 것 같다.
왜냐? 스토리북을 사용하려면 이것도 어느정도 학습이 필요한데,
고객을 위한 기능이 아니고 개발자를 위한 기능이다보니
공수가 짧고 일이 바쁘게 굴러가는 울 팀 사람들에겐 귀찮은 일일 수도 있다.
하지만 내가 이 회사에 평생 있을 게 아니고 다른 신입분들이 프로젝트를 유지보수하게 될 것을 생각하면 컴포넌트에 대해서 문서화를 해두는 것이 바람직하다고 생각한다.
뿐만 아니라 내가 유지보수 할 때도. 다른 프로젝트를 작업하다가 다시 예전에 작업했던 프로젝트로 돌아가면 아 그 컴포넌트 이 프롭에 뭐 설정해야 하더라..? 하면서 해매게 되기 때문에... 길게 보면 훨씬 유지보수 비용을 절약할 수 있는 도구라고 본다.
여태 작업했던 키오스크 프로그램은 모두 한 화면에서 30초간 사용자의 터치가 없으면 홈 화면으로 돌아가는 기능이 있어야 했다.
window에서 키오스크 모드로 실행되기 때문에 시작버튼도 없고 프로그램을 끌 수도 없다. 그래서 적어도 프로그램을 끌 수 있게끔 기능을 추가해 주어야 한다. 키오스크 화면의 구석 어딘가에 투명한 버튼을 만들어두고, 여러번 터치하면 비밀번호 입력창을 보여주고, 비밀번호를 올바르게 입력했을 때 개발 모드로 진입할 수 있게끔 구현해주면 된다.
키보드가 없는 window 환경인 키오스크 환경에서 PC프로그램 위에서 내 프로그램이 돌기 때문에 input이 focus되더라도 가상키보드가 특별히 뜨지 않는다.
키오스크 모듈을 실행하는 PC 프로그램의 함수를 실행하기 위해 웹뷰 통신을 사용한다. (C# 기반의 앱에서 Chromium 웹 엔진으로 React 웹페이지 실행)
C# 프로그램에서 WebView를 설정한다.
React 앱에서 C# 객체에 접근하도록 설정한다.
CefSharp.BindObjectAsync("webBridge"); 얘를 페이지가 처음 뜰 때 실행해야 한다. .NET 객체를 Chromium 웹 엔진에 노출시키는 역할, "webBridge" 가 JavaScript와 .NET 사이의 통신 다리 역할을 하는 객체의 이름이 된다.
WebBridge.함수명() 형식으로 PC프로그램의 함수를 실행해준다.
(아마 다음 키오스크는..?!ㅋㅋ)