Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.

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

기술 뉴스 #118 : 19-01-15

웹개발 관련

  • React Performance Fixes on Airbnb Listing Pages : React로 만든 Airbnb의 리스트 페이지의 성능을 개선한 과정을 설명하고 있다. 서버사이드 렌더링에서 브라우저의 피처 디텍팅이 되지 않아서 성능이 저하된 부분 등 크롬 개발자 도구의 프로파일링 기능과 프레임 그래프를 이용해서 성능이 저하되는 부분을 찾아내고 고치는 과정을 설명하고 있다.(영어)
  • React와 Apollo, Parcel 기반 서비스 개발 : 네이버에서 신규 서비스의 스택을 React, Redux, webpack 대신 React, Apollo, Parcel로 가져간 과정을 설명한 글이다. 실제 서비스를 만드는 세부 내용까지는 안 나와 있고 각 기술을 선택한 이유와 어떤 장단점을 제공하는지 소개하고 있다.(한국어)
  • Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word] : 작년에 이어 Smashing Magazine에서 웹사이트의 성능을 높이기 위해 점검해 봐야 할 내용을 체크리스트로 만들어서 PDF와 Apple Pages 포맷으로 배포했다. 이 체크리스트에는 개발 전에 준비해야 할 부분, 성능 목표, 환경 구성, 빌드 최적화, 에셋 최적화, 전송 최적화, HTTP/2, 테스트와 모니터링 등으로 나누어서 좋은 성능을 위해 필요한 부분이 잘 정리되어 있다.(영어)
  • The React Handbook : React를 공부할 수 있는 220페이지 분량의 핸드북을 공개했다. 웹에서 볼 수도 있고 뉴스레터에 가입하면 PDF나 ePub 등의 포맷으로 다운로드 받을 수 있다.(영어)
  • 2018 JavaScript Rising Stars : JavaScript 관련 프로젝트들을 프레임워크, 빌드 도구, 생태계 등으로 분류해서 2018년에 GitHub 스타를 많이 받은 순서대로 보여주는 페이지이다. 누적 스타 수는 오래된 프로젝트가 많게 마련인데 이 페이지는 2018년에 증가한 스타 수만 집계하고 있어서 2018년에 주목받은 프로젝트를 살펴볼 수 있다.(영어)

그 밖의 프로그래밍 관련

  • 주니어 개발자가 처음 풀 리퀘스트 보내본 썰.txt : 업무에서 사용하던 라이브러리에서 버그를 발견해서 수정하고 풀 리퀘시트를 보낸 과정을 설명한 글이다. 처음이라고 하시는 데 풀 리퀘스트을 보내는 과정이 깔끔하게 정리되어 있어서 아직 안 보내본 사람이 참고하기 좋은 글이다.(한국어)
  • 천만 명의 사용자에게 1분 내로 알림 보내기 (병렬프로세스의 최적화) : 방송이 시작하면 구독하는 사용자에게 푸쉬 알림을 보내는데 천만 명의 사용자에게 보내는데 최대 11분이 걸리던 상황을 1분 이내로 최적화한 과정을 설명한 글이다. 3단계로 진행하면서 병렬처리 및 Redis 파티셔닝 등 어떤 부분을 개선했는지 설명하면서 단계별로 얼마나 개선되었는지까지 보여주고 있는데 세세한 개선 과정이 나와 있어서 아주 유용하고 비슷한 환경에서 참고하기 좋은 글이다.(한국어)
  • Github Organization as a Code : GitHub 조직의 저장소와 권한 관리를 Terraform으로 하는 방법을 설명하는 글이다. 조직의 성격이나 규모에 따라 다르고 저장소까지 관리하면 새로운 저장소를 만들 때 약간의 불편함이 될 수도 있지만 멤버 혹은 권한 관리나 히스토리를 제대로 알기 어려운 문제 때문에 개인적으로 좋아하는 방식이다.(영어)
  • AWS 람다 커스텀 런타임 만들기(feat. 루비 2.6.0) : 기본에는 AWS Lambda에서 지원하는 런타임만 사용할 수 있었는데 지난 re:invent에서 커스텀 런타임 지원을 발표했다. 이미 Ruby를 지원하고 있지만, 최신 버전인 Ruby 2.6.0을 커스텀 런타임으로 만들고 이를 Lambda 레이어로 올려서 사용하는 방법을 설명하고 있다. Lambda에서 지원하지 않는 언어나 최신 버전이 필요하다면 이 방법을 이용해야 한다.(한국어)
  • 왜 굳이 도커(컨테이너)를 써야 하나요? : 도커가 인기를 얻은 지 오래되었지만 도커 컨테이너를 사용했을 때 서버의 환경을 통일할 수 있고 미리 빌드 실패를 경험해 볼 수 있으면서 격리된 부분은 변경할 수 없게 되어 있는 등의 장점을 설명하고 있다.(한국어)
  • Github Actions 맛보기 : 아직 클로즈 베타로 일부 사용자에게 조금씩 열리고 있는 GitHub Actions로 개인 프로젝트의 빌드, 테스트, Lint 등의 CI 작업을 작성한 경험을 공유한 글이다.(한국어)
  • 이미지만으로 내 중고물품의 카테고리를 자동으로 분류해준다면? (feat. Keras) : 당근마켓에서 사용자가 올린 이미지를 Keras로 학습시켜서 카테고리를 자동으로 분리할 수 있도록 시도한 과정을 설명하고 있다. 사용했던 코드와 학습 결과 및 잘못 분류된 사진들의 예시 등이 잘 나와 있는데 딥러닝은 잘 모르지만 얼마 전 당근마켓에 놀러 가서 내부에서 딥러닝을 사용하는 예시를 보기도 해서 더 재미있게 읽었다.(한국어)
  • Understanding npm-link : npm에서 개발 중이거나 디버깅 목적으로 로컬에 있는 다른 모듈을 설치해서 개발하는 npm-link를 어떻게 사용하는지 설명하는 글이다. npm-link를 사용하면 패키지를 배포하거나 Git 저장소에 올리지 않고도 개발할 수 있다.(영어)
  • Using worker_threads in Node.js : Node.js 10.5.0에 실험 기능으로 추가된 worker_threads로 CPU 인텐시브한 작업을 병렬로 실행하는 방법을 소개하고 있다.(영어)

블록체인 관련

볼만한 링크

  • 구글 면접 후기 : 최근 구글 코리아에 면접을 본 경험을 바탕으로 면접을 위해서 알고리즘 공부를 어떻게 했는지 정리한 글이다. 여러 달 동안 다양한 사이트와 책을 통해서 알고리즘 문제를 풀어보면서 연습을 하고 면접 후에 준비가 아쉬웠던 부분과 좋았던 부분이 나와 있어서 외국계 회사에 면접을 준비한다면 읽어보면 좋다.(한국어)
  • 블로그 글 1편을 쓰기까지의 과정 : 매주 최소 한편씩 블로그에 글을 쓰면서 그 과정을 정리한 글이다. 소재를 선택하고 개요를 적은 후 1차, 2차에 나누어서 초안을 작성한 뒤 발행하는데 체계적으로 잘 정리되어 있다. 나는 이렇게 체계적인 단계를 글을 쓰는 편은 아닌데 다른 분이 글을 쓰는 방식을 보고 배울 수 있는 부분도 많아서 유용한 글이었다.(한국어)

IT 업계 뉴스

프로젝트

  • unified : 문서를 문법 트리(syntax tree)로 만들어서 변환해주는 인터페이스.
  • 한글 에디터 산 1.x : 1993년 고등학생 때 만든 DOS 용 한글 에디터 산의 소스를 공개한 저장소.
  • rrweb : 웹사이트에서 사용자가 동작을 녹화한 뒤 재생할 수 있게 하는 JavaScript 라이브러리.
  • FBT : Facebook에서 만든 JavaScript 국제화 라이브러리.
  • hexyl : 터미널용 hex 뷰어.

버전 업데이트

2019/01/15 23:35 2019/01/15 23:35