Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Electron으로 데스크톱 앱을 개발한 경험

작년 하반기 회사에서 Electron으로 데스크톱 애플리케이션을 개발했다. 그 전에 Electron을 간단하게 사용해 보거나 관련 프로젝트만 살펴봤을 뿐 실제로 최종 사용자에게 배포할 앱을 만들어 본 적은 없었다. 주로 웹 쪽 작업만 하다가 데스크톱 애플리케이션을 배포한 것은 특별한 경험이었고 그냥 공부용 토이 프로젝트가 아니었기 때문에 더욱 특별했다. 대단한 애플리케이션을 아니지만, 나중을 위해서 기록을 남겨두려고 한다. 이것도 한 달 이상 지나서 더 늦으면 못 적을 것 같아서...

왜 Electron을 사용했는가?

앱의 성격은 여기선 중요하지 않고 회사에서 필요한 서비스를 제공해야 했는데 상황상 일정이 매우 매우 급한 상황이었다. 그래서 개발팀에서 만들기로 했을 때도 일정 내에 만들어서 배포할 수 있을지 배포하더라도 어디까지 만들 수 있을지 걱정하고 있었다.

기존에는 모바일 애플리케이션으로 기획이 되는 상황에서 일정을 맞추기 위해 Electron을 사용한 데스크톱 애플리케이션으로 방향을 바꾸었다. 기존 기획을 바꾸면서까지 Electron으로 변경한 이유는 몇 가지가 있다.

  1. 웹 스택만 가지고 빨리 만들 수 있다. 이 프로젝트에 참여한 개발자(나 + 다른 개발자 한 명)가 웹 스택은 어느 정도 다룰 수 있었기에(나는 모바일 앱은 한 번도 해본 적도 없다.) 우리가 만들 때 가장 빨리 만들 수 있는 스택으로 구성되어 있다.
  2. 다양한 플랫폼을 지원한다. 하나의 애플리케이션을 만들어서 Windows, Linux, macOS에 모두 배포할 수 있으므로 더 많은 사용자가 서비스를 이용할 수 있다. 기존 계획을 변경하게 된 내부 사정이 있지만, 결과적으로는 더 많은 플랫폼을 지원할 수 있게 되었고 회사 사업관점에서도 더 많은 사용자에게 서비스를 제공하는 것은 중요한 문제였다.
  3. 크로스 브라우징을 위한 작업을 하지 않아도 된다. Electron이 Node와 Chromium 기반으로 동작하므로 여기에서 동작하도록 작업만 하면 된다. 웹 애플리케이션으로 만든다면 훨씬 더 많은 사람이 이용할 수 있겠지만 다양한 브라우저에 맞추는 작업을 하는데 너무 큰 노력이 들어가게 된다. Electron으로 만들면 Chromium 외에 다른 브라우저는 신경 쓰지 않아도 되고 창 사이즈도 제어할 수 있으므로 반응형 디자인 작업도 최소화해서 작업 시간을 크게 단축할 수 있다.

작업 과정

개발은 다른 개발자 D님과 둘이 진행했다. 백엔드 API 작업도 해야 했기에 초기에 나는 API 서버를 만들고 디자인이 나오기 전까지 D님 Electron 프로젝트 구성을 하고 디자인 없이 UI와 필요한 로직을 개발하면서 먼저 진행하고 만들어진 화면을 디자인을 붙이면서 수정하면서 진행하였다. 이런 식으로 협업해 본 것은 처음인데 경험이 꽤 나쁘지 않았다. 프로젝트 구성에 대한 설명만 좀 듣고 중간중간 막힐 때 의사소통하는 것 외에는 크게 시간 낭비 없이 코드만 주고받으면서 개발할 수 있었다. D님의 경험은 어떤지 모르겠지만 중요 로직이 대부분 작성되어 있으니 그 부분은 크게 신경 쓰지 않고 빠르게 디자인 적용을 할 수 있었다.

스택은 Vue.jsVuetify를 사용했고 프로젝트 구성은 electron-vue 보일러 플레이트를 이용했다. Vue를 선택한 건 D님이 Vue를 해본 적이 있었기 때문이고 나는 안 해 본건 Vue나 React나 마찬가지라서 이 기회에 Vue를 써보게 되었다. 일정 내에 결과물을 만들어내기에 급급했기에 사실 Vue는 거의 공부하지 못하고 개발하면서 필요한 정보만 임시방편으로 찾아서 해결하면서 개발했다. 그래서 여전히 Vue를 잘 모른다. 개발하면서도 Vue를 이해 못 해서 삽질하면서 시간을 많이 소비하기는 했지만 이건 내가 몰라서 그런거고 Vue로 만든 경험은 나쁘지 않았다.

Electron으로 만든 데스크톱 앱

그래도 시작할 때와 나중에 Vue에 대한 지식 차이가 생겨서 처음에 만든 화면과 나중에 만든 화면의 처리 방식이 완전히 다르다. 계속 유지보수를 하려면 코드가 일관되게 한번 정리해야 하는데 언제 할지는 모르겠다.

보일러 플레이트가 잘 되어 있어서 개발환경에서는 HMR(Hot Module Replacement)도 잘 지원되었다. 개발 환경도 편했지만, 일반 웹 프론트엔드 작업과 거의 다르지 않아서 딱히 Electron을 의식하면서 개발하지 않아도 되었다.

배포

어느 정도 예상은 했지만, 배포를 준비할 때 꽤 많은 시간이 소요되었다.

프로덕션 빌드

보일러 플레이트에서 electron-builder 설정이 잘 되어 있어서 릴리스용 빌드는 비교적 쉽게 했다. 빌드에 필요한 설정이나 도구 선택도 많이 찾아봐야 하는데 보일러 플레이트에 CI 설정까지 잘 안내되어 있어서 필요한 설정만 하면서 테스트용 빌드를 만들 수 있었다.

config

설정관리로 node-config를 쓰고 있었는데 개발 과정에서는 문제가 되지 않았지만, 프로덕션용으로 빌드를 하고 나니 Electron이 인식한 프로젝트의 루트 위치가 달라져서 node-config를 사용할 수 없었다. 이 문제에 대한 이슈가 올라와 있긴 한데 결국 node-config로 머지되지는 못하고 electron-node-config라는 프로젝트로 빠져나왔다. electron-node-config을 사용하려고 했으나 개발 환경에서는 또 node-config를 사용해야 했기에 webpack 설정을 이용해서 개발 빌드와 프로덕션 빌드에서 config 모듈을 따로 처리하도록 설정했다.

Sentry

앱을 배포하고 나면 버그를 파악해야 하므로 Sentry를 이용하려고 했다. 데스크톱 애플리케이션은 Electron이 아니더라도 한 번도 배포해 본 적이 없으므로 문제를 파악하기 위해서는 필수라는 생각이 들었다. Sentry는 이전에는 백엔드에서만 사용해 봤는데 막상 붙이려고 하니 Vue 통합도 존재하고 아직 베타이지만 Electron SDK도 존재했다. Electron에서 Vue.js를 사용하고 있으면 어떤 SDK를 사용해야 하는지 고민되었지만, 그냥 Electron SDK를 사용했다.

Electron SDK는 설정이 꽤 복잡하고 이해하지 못한 sentry-symbols.js도 있었지만 약간의 삽질 끝에 Sentry를 통합했다. Electron이 메인 프로세스와 렌더러 프로세스가 나누어져 있으므로 Sentry도 양쪽에 모두 설정해야 한다. 처음에는 잘 동작하는 줄 알았으나 테스트를 하다 보니 렌더러 쪽에서 sentry 때문에 화면이 특정 액션에서 날라가버리는 문제가 발생했다. 이 문제를 디버깅하는 데도 시간이 많이 소비했지만, sentry를 디버깅할 수는 없었으므로 결국 렌더러 쪽에서는 Sentry를 제거해버렸다. 이건 sentry SDK가 베타라서 그럴 수도 있는데 내가 작성한 Vue 코드가 잘 작성되었다고 할 수는 없지만, sentry 같은 SDK가 애플리케이션 동작에 문제를 발생시키는 건 큰 문제라서 이건 나중에 더 살펴봐야 할 것 같다.

자동 업데이트

스토어를 통해서 배포할 수 있는 상황이 아니었으므로 자동 업데이트는 필수 기능이었다. Zeit에서 만든 Hazel 서버도 있지만 보일러 플레이트에 electron-updater자동 업데이트가 설정되어 있어서 이를 그대로 사용했다.

여기서도 고생을 많이 했는데 먼저 macOS에 대해서만 설정했다. 여기서 고생한 문제는 자동 업데이트를 이용하려면 코드 사이닝이 필요하다고 나와 있는데 macOS 앱의 코드 사이닝에 대해서 전혀 모르고 있고 Electron의 자동 업데이트에 대해서도 전혀 모르고 있었다. 한번 테스트하려면 패키징해서 새 버전을 배포하고 자동 업데이트가 동작하는지 확인해야 했으므로 시간이 오래 걸렸다. 자동 업데이트 테스트하면서 베타버전을 엄청나게 릴리스했다.

electron-builder에서 배포는 GitHub의 릴리스 기능을 이용할 수 있게 되어 있는데 잘 만들어져 있어서 GitHub 엑세스 토큰이 설정되어 있으면 빌드 명령어만으로 GitHub 릴리스 페이지에 빌드한 파일을 자동으로 올려준다. 이때 마친 공개저장소로 전환했기 때문에 쉽게 Travis CIAppVeyor를 설정해서 새로운 태그를 올리면 자동으로 빌드되어 릴리스가 생성되도록 설정했다.(AppVeyor은 윈도우 빌드를 위해서 사용한다.) 편하기는 한데 내부에 너무 자동화가 되어 있어서 어떻게 동작하는지 파악하는데 시간이 꽤 걸렸다.

GitHub 릴리스 페이지에 올라간 빌드 파일

코드 사이닝은 애플 개발자 계정에서 사인용 인증서를 발급받아서 이를 통해서 서명했다. 이 구조에서 자동업데이트를 하려면 코드 사이닝이 제대로 되고 GitHub 릴리스에 배포가 제대로 되고 Electron 앱에서 자동 업데이트 설정이 잘 되어 있어야 동작하므로 자동 업데이트가 안 되면 어디서 안되는 건지 찾느라고 고생을 정말 많이 했다. iOS든 macOS 이든 애플 계정에서 앱을 배포해 본 적이 없어서 인증서 받는데도 시간이 오래 걸렸는데 문서를 찾아보면서 어떻게 개발을 했다. 그리고 앱에 서명이 되어 있어야 macOS에서 설치할 때 "확인 안 된 개발자"라고 나오지 않는다.

예제로 나온 자동 업데이트 기본 코드를 이용하면 잠수함 패치로 앱을 업데이트한다. 앱을 새로 켜거나 일정 주기로 GitHub을 확인해서 새 버전이 있으면 뒤에서 몰래 다운받고 다운이 완료되면 앱을 리스타트하면서 새 버전으로 올려버린다. 테스트하면서 이걸 몰라서 이미 설정이 다 되었는데도 업데이트 안 된다고 답답해하면서 나갔다 왔더니 새 버전으로 올라가 있었다. Electron의 자동 업데이트 API로 제어하면 보통 앱이 그렇듯이 업데이트할 지 물어보고 다운로드 과정 보여줄 수 있을 것으로 보이지만 일단은 급해서 잠수함 자동 업데이트로 적용해 놨다.

문서에는 자동 업데이트에 코드 사이닝이 필요하다고 나와 있었는데 Windows 용 빌드에서는 코드 사이닝 없이도 자동 업데이트가 잘 되었다. Windows에서 또 고생할 시간도 없었는데 한 번에 되어서 너무 다행이었다. 물론 서명을 하지 않았으므로 Windows에서도 설치 시에 확인 안 된 개발자가 배포한 앱이라는 경고가 나온다..

에필로그

보통 프로젝트에서 설명하는 것과 달리 실제로 서비스를 만들거나 배포하면 예상처럼 동작하지 않는 경우가 많은데 Electron은 그래도 역사가 오래되어서인지 꽤 자연스레 진행되었다. 실제로 macOS와 Windows 개발을 위해서 특별히 처리한 것은 거의 많지 않다. macOS에서 메뉴 처리를 약간 다르게 하는 부분이나 macOS에서는 메뉴가 상단 상태 바에 나타나지만 윈도우에서는 앱 위에 나타나기 때문에 앱의 height가 달라지는 문제 외에는 쉽게 두 플랫폼에서 애플리케이션을 제공할 수 있었다. Linux 빌드는 배포하지 않았는데 이는 Electron 문제는 아니고 내부에 테스트할 여력이 없어서 제외했는데 곧 추가로 배포하려고 하고 있다.

앱을 만들고 배포해보고 나니까 웹페이지를 만들 수 있으면 Electron으로 앱을 쉽게 만들 수 있지만 배포할 플랫폼에 대한 지식이 부족한 게 문제가 되었다. 간단히 "윈도용 앱은 32bit 64bit 모두 나오나요?"라는 기획자의 질문에도 나는 대답을 할 수 없었다. 윈도우를 주로 안 쓴 지가 오래되어서 요즘도 32bit를 지원해야 하는지 앱을 만들 때 두 가지 버전을 따로 배포해야 하는데 따로 만들거나 같이 만들었을 때의 장단점이 무엇인지를 전혀 알지 못했다. 그리고 배포 이후에 특정 윈도우 버전에서 앱이 크래시난다는 보고를 받았는데 어떻게 처리해야 하는 건지 아무런 지식이 없었다. 같은 버전의 윈도우에서 테스트해보면 잘 되는 걸 보면 다른 앱과의 충돌이거나 또 다른 문제가 원인 인 것으로 보이는데 데스크톱 앱을 만들어본 경험이 없으므로 당연히 예상할 수 있는 문제도 전혀 없어서 대응할 수가 없었다. 윈도우에도 앱 스토어가 있다는 것도 이번에 처음 알았다.

어쨌든 데스크톱 앱을 배포해 본 경험은 재미난 경험이어서 다음에도 필요하면 고려를 해볼 것 같다.

2019/01/27 20:02 2019/01/27 20:02