0%

Unity

하나의 대형 터치 테이블에서 팸플릿 뷰어, 음성 AI 챗봇, 시리얼 센서 제어, 외부 실행 파일까지 자유롭게 넘나들 수 있다면 어떨까요?

전*원··수정됨 2026.07.21

하나의 대형 터치 테이블에서 팸플릿 뷰어, 음성 AI 챗봇, 시리얼 센서 제어, 외부 실행 파일까지 자유롭게 넘나들 수 있다면 어떨까요?

관람객은 단순히 버튼 하나를 누를 뿐인데, 뒤에서는 Win32 API가 Unity 창의 투명도를 조절하고 외부 프로그램의 창 모드를 강제하며, 종료 후에는 다시 홈 화면으로 자연스럽게 복귀한다면요. 이번 포스팅에서는 대구 성서도서관 '스마트에듀테이블' 멀티 콘텐츠 런처의 기술적인 구현 과정을 공유합니다.

아키텍처_01_전체구조.png

1. 시스템 아키텍처 개요

본 프로젝트는 크게 세 가지 단계로 구성됩니다.

허브 런처: 홈 화면의 버튼 입력을 프로그램 실행 / 다음 패널 / 씬 이동 세 가지 분기로 라우팅

외부 프로세스 제어: Windows API로 외부 .exe의 창 모드(Windowed·Borderless)를 강제하고, Unity 창에는 TOPMOST·색 키 투명화 적용

멀티모달 콘텐츠: 팸플릿 이미지 뷰어, 음성 기반 AI 챗봇, 시리얼 포트로 연결된 외부 하드웨어 장비를 단일 앱 안에 통합

이 모든 과정은 Unity 2022 LTS 계열을 메인 플랫폼으로, ProgramManagerPanelManagerWindowManager 세 싱글톤(Singleton)이 서로 이벤트(Event)로 통신하며 상태를 조율합니다.

2. 주요 기술적 구현

2.1 외부 프로그램 런칭과 창 모드 강제 (ProgramManager.cs)

관람객이 버튼을 누르는 순간 뒤에서 실행되는 일은 꽤 복잡합니다. OpenProgram(versionNumber)는 먼저 KillBothProcesses()로 기존 프로세스를 정리하고, ResolveProgramPath()가 바탕화면 기준 상대 경로·절대 경로.lnk 바로가기까지 모두 해석해 Process.Start로 실행합니다. 이어서 CoOpenProgram() 코루틴이 대상 프로그램의 메인 윈도우 핸들hWnd)을 최대 10초간 폴링해 획득한 뒤, SetWindowLongPtr / SetWindowPos 호출로 창 스타일을 지정된 Windowed 또는 Borderless 모드로 강제 전환합니다. 네트워크 지연이나 초기화 타이밍 차이로 한 번에 적용되지 않는 경우를 대비해 reapplyTries 회만큼 재적용하는 안전 장치도 포함했습니다.

2.2 색 키 투명화 + TOPMOST 오버레이 (WindowManager.cs)

외부 프로그램을 띄우는 동시에 Unity UI를 그 위에 "유령처럼" 올려 두기 위해, Win32 레이어드 윈도우(Layered Window) 기법을 사용했습니다. ApplyOverlay_TransparentByColorKey()가 지정된 색상(예: 초록 #00FF00)의 픽셀을 완전 투명으로 처리하고, SetClickThrough()가 해당 영역의 클릭 이벤트를 뒤쪽 창으로 통과시킵니다. 여기에 MakeUnityTopMost()로 Unity 창을 항상 최상단에 고정하되, 다른 프로그램이 SetForegroundWindow로 가로챌 가능성에 대비해 StartTopMostKeeper()가 일정 주기로 TOPMOST 속성을 재적용하는 유지 코루틴을 상시 구동합니다.

2.3 음성 → STT → LLM 멀티스텝 파이프라인 (MicrophoneManager.cs + ChatBotManager.cs)

챗봇 씬은 관람객의 말 한마디를 여러 단계로 처리합니다. MicrophoneManager.StartRecording()Microphone.devices[0]으로 44,100Hz 녹음을 시작하고, 종료 시 SaveWav()가 16-bit PCM RIFF 포맷으로 로컬 저장합니다. 저장된 WAV는 STTBotManager.SendSound()multipart/form-data로 STT 서버에 업로드해 텍스트로 변환되고, 그 결과가 다시 ChatBotManager.SendPrompt()를 통해 LLM 서버로 JSON POST 되어 답변을 받아 옵니다. 전 과정이 ContentsManager.SetQuestion()SetAnswer() 체인으로 이어져, 관람객 입장에서는 "말하고 → 듣는" 단순한 경험이 됩니다.

3. 사용자 경험(UX) 최적화

배포 친화적 경로 해석 : ProgramManager는 바탕화면 기준 상대 경로와 .lnk 바로가기를 모두 지원해, 현장 담당자가 운영 PC에 프로그램만 갖다 놓으면 별도 빌드 없이 경로 갱신이 가능하도록 했습니다.

자동 복귀 이벤트 체인 : 외부 프로그램이 종료되면 CoWaitForProgramExit()OnProgramClosed 이벤트를 발화하고, 구독 중인 ButtonManager가 로딩 팝업을 닫은 뒤 ReturnToHome()으로 홈 패널을 자동 복원해 관람객이 한 번도 "뭐가 잘못된 거지?" 상태에 빠지지 않도록 했습니다.

스마트한 시리얼 포트 선택 : SerialPortManager.FindSerialPort()StreamingAssets/UnusedPorts.txt에 나열된 제외 포트를 필터링해 사용 가능한 첫 포트를 자동 선택함으로써, 블루투스·가상 COM 포트가 많은 환경에서도 엉뚱한 장비를 잡는 사고를 방지했습니다.

페이지 스냅 팸플릿 뷰어 : TouchManagerScrollRect.horizontalNormalizedPositionLerp로 부드럽게 보간하고, OnEndDrag 시점에 가장 가까운 페이지로 자동 스냅되어 관람객이 "딱 맞게 멈추는" 페이지 전환을 체감합니다.

4. 마치며

이 프로젝트는 Unity 한 프로젝트 안에 멀티 콘텐츠 허브, 외부 프로세스 런처, 음성 AI 인터페이스, 시리얼 하드웨어 브리지 라는 네 가지 역할을 한꺼번에 담은 작업이었습니다. 특히 Win32 API를 활용해 외부 프로그램과 Unity 창을 마치 한 몸처럼 보이게 만든 오버레이 구조는 기술적으로도 흥미로웠지만, 관람객이 "테이블 위에 모든 것이 자연스럽게 얹혀 있다" 는 일관된 체감을 가지도록 만든 점에서 큰 의미가 있었습니다.

향후에는 외부 프로그램의 준비 완료를 고정 대기가 아닌 이벤트 기반 핸들 감지로 개선하고, 팸플릿 뷰어의 해상도 처리를 동적 계산 방식으로 확장하며, 음성 챗봇에 실시간 스트리밍 STT 를 도입해 응답 체감 속도를 한층 끌어올릴 예정입니다.

읽기 도구

6분 읽기

이 글이 도움이 되었나요?

다음으로 읽기