본문 바로가기

remote-craft

RemoteCraft 09 - 클라이언트 GUI (feat. Javascript + HTML + CSS)

한마디로 표현하자면 스트레스의 연속이었다. 기회가 된다면 좀 더 진득하게 프론트엔드 프레임워크에 대해 알아볼 수 있다면 좋을 것 같다. 단순한 1화면 1UI 프론트엔드이기 때문에 프레임워크를 사용하는 것은 불필요한 복잡도 및 꼭 필요 하지 않은 기능들을 덧붙이게 되는 Overkill이라고 생각하였고 내가 필요한 Javscript + HTML + CSS만 이용하여 '최대한 단순하고 깔끔하게 만들자'를 목표로 시작하였다.

 

HTML + CSS

GUI 구상

 

단순 할 수록 사용하기 편하다! 라는 아이디어를 실현 시키기 위해 (사실 복잡해지면 개발하기도 복잡한 것 같다..), 위와 같은 구성으로 SPA, 정말 SPA를 구현해 보았다. CSS의 grid display를 이용하면 윈도우를 커다란 엑셀시트처럼 사용할 수 있었다. 가로 세로로 원하는 크기로 칸을 만들고 각각 의 선마다 이름을 지정하여 쓸 수 있는 매우매우 편리한 도구 였다.

 

TMI)  처음에는 flex박스를 사용하려고 했지만 윈도우 크기에 따라, 또는 박스의 크기에 따라 UI가 전혀 의도치 않은 방식으로 재조정되는 것을 보고 바로 CSS파일을 전부 엎어버렸다.

TODO

- 터치 버튼!

- GUI 사이즈가 현재 하드코딩 되어있음. 디스플레이 상태(윈도우 크기, 모바일 vs 데스크탑)에 맞게 재조정하도록 env 관리 클래스를 만들기

 

Javascript

컴포넌트 별로 .js 파일을 따로 만들었고 자바스크립트를 객체지향적으로 사용 하기 위해서 선택 한 방식은 IIFE를 이용해 오브젝트를

생성 -> 해당 객체의 메써드를 return 하여 외부에서 사용 할 수 있도록 하였다. 

Javascript 잘 활용하기!
의도는 그저 객체를 리턴받아 밖에서 쓰고 싶었던 거였지만... 자바스크립트에 대해 정보를 찾아보다가, 프로젝트에서 사용한 함수 선언 방식이 closure를 활용한 자바스크립트의 encapsulation 방식 이라는 것을 찾았다!..

 

  1. websocket: 웹소켓 연결 및 웹소켓 메세지 핸들링 
  2. rtc: RTCPeerConnection 객체를 통해 ICE Candidate 찾기, SDP Offer 및 Answer 생성 및 저장, MediaStream(비디오 오디오 스트림) 관리
  3. controller: 키보드입력 이벤트 핸들러 및 Libretro를 위한 keyMap 정의
  4. input: 컨트롤러 상태에 대한 polling, 여러 종류의 입력을 LibretroAPI 에 맞도록 bitmap으로 인코딩 (현재는 키보드 뿐이지만, 조이패드, 터치패드 등 여러가지 입력 디바이스 추가 확장을 위해서 컨트롤러와 분리 되어있다.)

 

컴포넌트 사이의 커뮤니케이션은 간이 PubSub 클래스를 이용 하여 개발했다.

컨트롤러를 예로...

// controller.js

// KEY_PRESSED 이벤트에 등록되어 있는 콜백 함수
// PubSub에 의해 Key_PRESSED가 어딘가에서 publish 될때마다 실행된다!
const onKeyPress = (data) => {
    input.setKeyState(data.key, true);
}
// KEY_PRESSED 이벤트에 onKeyPress 함수를 PubSub에 등록
PubSub.subscribe(KEY_PRESSED, onKeyPress);


const Keyboard = (() => {
    
    ...
    
    const onKeyChange = (code, callback) => {
        if (code in keyMap) {
            const key = keyMap[code]
            // key => PubSub.publish(KEY_PRESSED, {key: key}) 함수가 실행되며
            // KEY_PRESSED 이벤트 발생
            callback(key);
        }
    };
    
    const init = () => {
        // 브라우저 키보드 이벤트 핸들러 등록
        document.body.addEventListener('keydown',
            event => onKeyChange(event.code,
                key => PubSub.publish(KEY_PRESSED, {key: key})
            )
        );
        document.body.addEventListener('keyup',
            event => onKeyChange(event.code,
                key => PubSub.publish(KEY_RELEASED, {key: key})
            )
        );

        ....
        
        console.log('[input] keyboard init');
    }
    
    return {
        init,
    }
}()

Keyboard.init()

 

개발 할때는 subscribe와 publish를 한쌍으로 언제나 유지하도록 노력하여 중간에 실수가 일어나는 일은 거의 없었으나 이벤트 기반으로 개발을 하다보니 subscribe와 publish가 코드 여기지거에 흩뿌려져 있어 확실히 가독성이 떨어지는 것을 볼 수 있었다. 이벤트를 가독성있게 구조화하려면 어떻게 해야할지는 좀 더 고민해봐야겠다...

 

프론트엔드 로직 구조

 

 

완성품 공개! (사실 이거 자랑하려고..)

RemoteCraft 프론트엔드

 

to be continued...

코드 설명도 하고 싶지만... 아직 자바스크립트가 무르익지 않았다.. 조만간 코드도 정리하고 깔끔하게 올려보도록 하자..!