박스히어로 Electron 개발기 — 커스텀 타이틀바 만들기

BitYoungjae
bgpworks
Published in
9 min readFeb 5, 2021

일렉트론앱에 특정 웹 페이지를 렌더링하는 방법은 아래와 같이 단순하게 구현 가능하다.

저기에 구글 주소를 입력하면, 그 자체로 구글 전용 웹 브라우저를 만들 수 있다.

웹 서비스를 띄우는 용도가 아니라 일반 데스크탑앱을 만드는 것이 목적이라면 로컬 html 파일 경로를 지정함으로써, 로컬 파일로 구성된 전용 리액트 앱을 그리면 된다.

타이틀바 역시 기본적으로 아래와 같이 호스트 환경에서 제공하는 기본적인 형태로 별도의 설정없이 띄울 수 있다.

그러나 우리는 이런 칙칙한 회색 바탕의 기본 타이틀바를 원했던 것이 아니고, 아래 Slack의 타이틀바처럼, 우리만의 디자인과 기능을 적용하고 싶었기 대문에 기본 옵션 외의 별도의 해법이 필요했다.

우선 기본 타이틀바를 없애야 한다.

BrowserWindow 생성시 사용되는 옵션 중 titleBarStyle 옵션을 hidden으로 설정하는 것으로 기본 타이틀바를 없애고 커스텀 타이틀바가 들어갈 영역을 확보할 수 있다.

titleBarStyle을 hidden으로 설정하면 기본 타이틀바를 숨겨준다.

이 부분에서 염두에 두어야할 것은 Mac의 경우 기본 타이틀바가 사라지는 것은 동일하나 문서에서 Standard window controls 혹은 traffic lights라고 칭하는 기본 창 컨트롤 버튼들은 사라지지 않고 좌상단에 그대로 유지가 된다는 것이다.

이 기본 버튼들을 제거하는 다른 방법이 있는지 모르겠으나, 커스텀 타이틀바를 사용하는 거의 모든 일렉트론 앱들은 그냥 저 영역은 그대로 두고 나머지 공간을 잘 활용한다. 기본 버튼 영역을 고려해서 디자인만 잘 하면 문제될 내용이 없다.

이제 타이틀바를 그려줄 차례이다.

사실 여타 일렉트론 앱들처럼 데스크탑 전용 앱으로써 화면 또한 일렉트론에 맞춰서 새로 만드는 것이라면, 사실 전혀 복잡할 내용이 없다.

앱 전체를 SPA로 구성하고 본문을 감싸는 레이아웃 컴포넌트 단에서 언제나 같은 위치에 같은 크기로 타이틀바 컴포넌트를 렌더링해주면 그만이다.

하지만 우리는 앞서 이야기 했다시피 현재 운영중인 웹 서비스를 최대한 변화없이 있는 그대로 일렉트론 화면에 그리고 싶었기 때문에, 조금 더 복잡한 솔루션이 필요했다.

최초에는 loadURL을 통해 당사 서비스를 불러오고, 위에 서술한 방식과 비슷하게 웹앱 내부에 타이틀바를 만들어 그려주는 방법을 고려해 보았으나, 페이지가 이동될 때마다 타이틀바가 깜빡이는 현상이 생기고 모든 화면에 타이틀바를 그려줘야 하는 문제 그리고 가장 본질적인 문제로 일렉트론 앱이 웹앱에 의존성을 갖게 된다는 문제가 생기기 때문에 금방 단념했다.

일렉트론 앱은 웹앱에 대한 단순 Wrapper로써의 기능만을 담당하고 한 번 배포한 이후에는 가급적(최대한) 손댈 일이 없도록 하는 것을 목표로 하고 있었기에 적당치 않은 방법이었다.

우리가 찾은 해결법을 간단히 설명하면 아래와 같다.

BrowserWindow 내에 타이틀바를 위한 타이틀바 영역과 웹앱을 위한 Body 영역을 별개의 것으로 분리하는 것이다.

즉, 위 화면처럼 하나의 BrowserWindow 내에 두개의 앱을 렌더링하는데, 타이틀바 영역을 포함한 전체적인 화면은 로컬 리액트 앱으로 구성하고 해당 로컬앱 내부의 타이틀바 영역 아래에 모종의 방법으로 박스히어로 웹앱을 포함시켜주는 방법이다.

우선 타이틀바를 띄울 리액트 앱을 렌더링 해야한다.

리액트 앱 프로젝트를 구성하는 방법은 다양하다. 우리는 CRA가 아닌 웹팩 + 바벨을 이용해 직접 구성하였고, 타입스크립트를 사용하였으나 이 내용들은 본 글의 취지와 맞지 않기에 자세한 내용은 생략하겠다.

아무튼 웹팩을 이용해 번들링된 (타이틀바를 포함한)로컬 리액트앱 번들의 경로는 ./out/index.html 이었고, 아래와 같이 BrowserWindow에 불러와 렌더링 하였다.

이제 박스히어로 웹 페이지를 타이틀바 아래에 띄울 차례다.

BrowserWindow의 메인 웹 컨텐츠 내부에 또다른 컨텐츠를 넣기 위해서는 세가지 방법을 고려할 수 있다. BrowserView와 webview tag 그리고 iframe이다.

세가지 방법중에서 webview tag를 선택하였는데, 사실 일렉트론 공식문서에서는 webview를 지양하고 BrowserView나 iframe을 대안으로 사용하라고 하고 있다.

하지만 BrowserView 자체도 일렉트론의 실험적인 기능이고 webview는 chromium 자체에서 제공하는 feature이면서 webview tag라는 이름처럼 html 내부에 선언되어 일반 dom node를 다루듯이 스타일링 등을 할 수 있어 편의성 측면에서 선택하게 되었다.

추가) 공식문서에서는 향후 webview의 아키텍처가 변경이 될 경우(문서에서는 실제로 dramatic한 변경이 있다고 경고하고 있다)미래의 electron 버전에서는 사용하기 어려워질 수도 있다고 한다.

실제로 webview와 관련하여 버그가 발생한다는 이슈들이 간혹 있었고 일렉트론 버전이 올라가면서 하나 둘 고쳐지고 있기는 하지만 아직 보고되지 않은 어떤 버그가 또 존재할지 모르는 부분이다.

여러가지 고려할 사항들 때문에 불가피하게 webview tag 사용을 결정하였지만 어쩌면 우리 앱에게도 기술부채로 남아있는 부분이기도 하다.

webview에 지정된 몇가지 attribute들을 개략적으로 설명하면 다음과 같다.

  • preload — webview의 컨텐츠가 로딩되기 전에 선행되어 실행되는 스크립트를 지정할 수 있다.
  • enableremotemodule — remote module을 사용하지 못 하도록 한다. 기본값은 사용한다인데, 이를 허용할 경우 내부에 그려진 컨텐츠에서 electron api를 직접 컨트롤 할 수 있게 되어 보안상 문제가 생길 수 있다.
  • webPreferences — Electron BrowserWindow의 webPreferences와 그 역할이 같다. 여기서 nativeWindowOpen 옵션을 줬는데, chromium의 built-in window.open을 사용하기 위함이다. 자세한 이유는 모르겠으나 이 옵션을 설정하지 않을 경우 카카오 로그인 등이 정상 동작하지 않았다. electron은 alert()같은 기본 다이얼로그도 electron 전용의 것으로 매핑해두는데 window.open도 같은 경우여서 window.open의 내부 동작이 chrome과 다른 부분이 있기 때문인 것 같다.
  • allowpopups — 이를 설정하지 않으면 내부 컨텐츠에서 뜨는 팝업을 차단한다.

electron에서 개발자도구를 열어놓고 보면 위 화면처럼 webview 영역이 타이틀바 하단에 잘 그려진 것을 확인할 수 있다.

스타일 부분을 잘 보면, width는 100vw로 화면에 꽉 채우고 height에는 calc(100vh — 38px)를 지정했는데, 38px는 타이틀바의 높이값으로 이를 설정하지 않으면 타이틀바 높이 만큼의 하단 영역이 가려져서 보이기 때문에 의도적으로 설정한 것이다.

이번에는 렌더링 된 타이틀바에 기능을 불어넣을 순서이다.

우선 타이틀바는 draggable 이어야 할 것이다. 타이틀바를 이용해 사용자가 화면의 위치를 조정할 수 있어야 하기 때문이다.

이는 css property 중 비교적 생소한 -webkit-app-regin 속성을 drag로 설정함으로써 간단히 해결된다. 해당 property가 설정된 요소는 어디에 위치해 있든 일렉트론 앱에서 화면을 끌고 다닐 수 있는 draggable한 상태가 된다.

단, 타이틀바 전체를 draggable하게 설정하면 안 된다. 이유는 좌상단, 우상단 꼭지점을 통해서 화면 크기를 조절할 수 있어야 하는데, 이를 위한 공간이 확보가 안되기 때문이다.

그래서 위 코드와 같이 타이틀바 내부에 DraggableBackground라는 별개의 투명한 내부 영역을 만들고, absolute position과 계산된 width를 통해 좌상단, 우상단의 영역을 5px만큼 비워두는 방법으로 크기 조절을 위한 동작을 보존했다.

여기까지면 기본적인 형태와 기능(화면 끌고 다니기)을 갖춘 우리만의 타이틀바를 만들어낼 수 있다.

하지만, 조금 더 생각해보면 타이틀바에는 화면 위치 이동 이외에도 생각외로 다양한 기능들이 구현되어야 할 필요가 있다.

  • Mac 환경과 달리 Window 환경에서는 기본 컨트롤 버튼을 직접 구현해주어야 한다. 즉, 최소화 최대화 닫기 버튼의 구현이 필요하다.
  • 박스히어로 라는 웹 서비스만을 위한 미니 웹 브라우저와 같은 형태이므로 내부 웹 컨텐츠에 대한 history를 조작하는 뒤로가기, 앞으로가기, 새로고침 등의 버튼을 만들어야 한다.
  • 상기된 history control 버튼들은 현재 history의 상태를 반영해야 한다. 예를들어 더이상 뒤로가거나 앞으로 갈 수 없는 상태라면 ui 측면에서 해당 버튼을 비활성화 상태로 표현해야 할 것이다.
  • 보통 타이틀바를 더블 클릭을 하게 되면 최대화되거나 원래 크기로 돌아간다. 이것도 직접 기능을 붙여줘야 한다.
  • 부수적인 것이지만 Mac에서 전체화면 모드로 그려질 경우 다시 원상복귀 할 수 있는 버튼도 있으면 좋을 것 같다.

타이틀바를 직접 만들어보려고 생각해보지 않았다면, 이런 것들이 필요할 것이라고 처음부터 상상하기 어려웠을 수도 있다.

이 내용들을 모두 다루려면 글이 길어지므로 다음 글에서 계속 설명을 이어가도록 하겠다.

--

--