채용공고 주업무 = 웹 제품을 Electron 등 데스크톱으로 확장. 그래서 출발 질문 — Joint의 Next.js 웹을 Electron으로 이식하면 어디서 막히고 어떻게 푸는가? 로딩·알림·배포를 훑되, 가장 먼저·확실히 막히는 알림부터 실제로 검증했음. (웹 알림이 Service Worker + FCM 경로라면 Electron에서 그대로 될지가 첫 관문 — 정확한 전략은 실제 구조 확인 필요.)
이건 가상 시스템 설계가 아니라 이미 있는 웹을 데스크톱으로 옮기는 실무 그 자체. 이식 전체로 보면 답할 게 셋인데, 그중 가장 먼저·확실히 막히는 알림부터 검증했음.
loadURL)
vs 로컬 번들(loadFile). 첫 갈림길, 나머지를 규정.
"왜 Electron이냐"는 당연히 나올 질문 → 대안을 낮잡지 않고 비교. Tauri는 진지한 후보 — 2.0 stable(2024.10), 번들·메모리·보안·모바일에서 우위. "아직 불안정"이라는 인상은 낡음.
| 관점 | Tauri v2 | Electron |
|---|---|---|
| 렌더링 엔진 | OS WebView → macOS는 WebKit, 플랫폼별 차이 실재 | 번들된 Chromium → 모든 OS에서 렌더링 일관 |
| 팀 스택 정합 | 네이티브 계층은 Rust (러닝코스트) | TS/React/Next 그대로 — Rust 세금 없음 |
| 번들·메모리 | ✓ 가볍다 | 무겁다 (Chromium 동봉) |
| 성숙한 웹 제품 확장 | 그린필드엔 매력적 | 배포·서명·자동업데이트 생태계 성숙 → 리스크 낮음 |
그럼에도 지금 Joint엔 Electron — 이유 셋:
셸이 웹을 띄우는 방식 = 두 갈래. 이 선택이 SSR 가용 · 배포 횟수 · 오프라인 여부를 전부 규정 → 이식의 첫 결정.
| 기준 | 로컬 번들 · loadFile |
원격 URL · loadURL |
|---|---|---|
| 렌더링 모델 |
Next.js output:'export' (SSG)
만 가능
|
배포된 웹을 그대로 → SSR·미들웨어 살아있음 |
| SSR / API / 미들웨어 | ✕ 서버 기능 사용 불가 | ✓ 기존 웹과 동일하게 동작 |
| 배포 | 웹 배포 + 앱 재빌드·재배포 이중 관리 | 웹 배포 1회 → 데스크톱도 자동 최신화 |
| 오프라인 | ✓ 번들이 로컬에 있어 강함 | △ 흰 화면 위험 — 기존 SW 캐싱으로 완화 |
| Joint 적합성 | 제품을 export 제약 안으로 욱여넣어야 함 | 이미 PWA 서비스 + AWS 배포 존재 → 자연스러움 |
Joint 기준 — 공개된 구조(PWA 서비스 중)로 보면 사실상 정해져 있다고 판단.
output:'export')로 다시 마는
건 손해 — SSR·API 버림 +
웹 배포·데스크톱 번들 이중 관리 자초.
단, URL 로드도 공짜는 아님 — 새로 생기는 트레이드오프 (조사만 함, 대부분 미구현 · 향후 과제):
did-fail-load 감지
시 번들된 offline.html로 폴백(메인 차원, SW와 무관).
(데모엔 미설정.)
mainWindow.loadURL(APP_URL)로 웹을 띄움.APP_URL = http://localhost:3010 (Next 개발
서버)URL 셸을 만들면 곧바로 부딪히는 게 알림 — 웹에서 당연하던 푸시가 Electron 안에선 그대로 안 됨. 그래서 이식의 대표 문제로 잡고, 경로별 트레이드오프를 따져 하나 선택 + 프로토타입까지.
PushManager 구독 시도 →
push service not available.
electron-push-receiver = 브라우저 PushManager 우회,
Node가 FCM 등록·롱커넥션(MCS) 직접 구현 (안드로이드
방식 흉내).
근거 = 레퍼런스 앱의 실제 동작. 슬랙·디스코드도 트레이 상주 시만 알림, 완전 종료 시 무알림 — 결함이 아니라 의도된 설계. 모바일과 데스크톱은 전제가 다름.
| 환경 | 전제 | 알림에 필요한 것 |
|---|---|---|
| 모바일 | OS가 앱을 수시로 종료 (메모리 관리) | 앱과 독립된 OS 푸시(FCM·APNs) 필수 |
| 데스크톱 | 사용자가 Quit하기 전엔 프로세스 유지, 트레이 상주가 자연스러움 | 상시 WS 연결만으로 충분 |
즉 "앱 꺼져도 알림"용 OS 푸시는 데스크톱 현 단계엔 과한 설계. 완전 종료 무알림 = 슬랙·디코와 동일한 의도 → 표준 WebSocket 경로 선택. 표준 의존성만 쓰니 비공식 프로토콜 리스크도 없음.
| 경로 | 성격 | 판정 |
|---|---|---|
| WebSocket + IPC | 렌더러가 WS 수신 → IPC로 메인 → 네이티브 알림. 표준 의존성만 사용. | 선택 |
| 표준 Web Push (SW+FCM) | Electron에 Push Service 부재 → 경로 자체가 막힘. | 불가 |
| electron-push-receiver | Node가 FCM·MCS 직접 구현. 비공식 리버스 프로토콜, 2024.6 이후 불안정. | 배제 |
| OS 네이티브 푸시 (WNS·APNs) | "앱 꺼져도 알림"의 정석. 지금 단계엔 과함. | 향후 |
new Notification()
그대로 뜸. 웹이든 Electron이든 동작. (WS 수신은 타이머와 달리
백그라운드에서도 throttle 거의 안 됨)알림을 "전송(메시지를 어떻게 받나)"과 "표시(받은 걸 어떻게 띄우나)"로 쪼개면 판단이 명확해짐.
| 단계 | 선택 | 근거 |
|---|---|---|
| 전송 | WebSocket 강제 | Electron에서 표준 Web Push(FCM) 경로가 막힘 → 메시지를 받아올 방법이 상시 WS뿐. 선택이 아니라 결정됨. |
| 표시 | IPC → 메인 네이티브 선택 |
렌더러 웹 Notification으로도 뜨고 방 전환까지 됨.
다만 트레이에 숨긴 창 복원은 메인만 가능 →
알림 로직을 렌더러·메인으로 쪼개게 됨. 어차피 Electron 셸이라
창 제어·앱 정체성(setAppUserModelId)·클릭
라우팅이 이미 메인에 모여 있음 → 알림도 메인 네이티브로
일원화하는 게 자연스럽고, 웹 노티를 굳이 쓸 이유가
없음.
|
즉 "웹으로도 알림이 뜨는데 왜 네이티브냐"의 답은 "웹은 안 된다"가
아니라 "어차피 Electron이라 굳이 웹 노티를 쓸 이유가 없다" —
창 제어·정체성·클릭 라우팅이 이미 메인에 있으니 알림도 거기로 일원화.
그 경로가 다음 섹션의
WS → preload → 메인 → 네이티브.
실제 동작하는 데모로 구현. 셸이 웹 UI를 loadURL로 띄우고,
렌더러가 받은 WS 메시지 →
preload 브리지(contextBridge) → 메인 프로세스 →
네이티브 알림.
핵심 = 직접 구현·동작 확인한 것과 조사·설계만 한 것을 섞지 않기. 아래가 그 구분.
backgroundThrottling:false로 백그라운드 freeze 방지)
open-room
IPC)
isQuitting 플래그로 hide와 구분)
electron . 개발 실행 기준. 배포는 범위에서 의도적으로
제외.
여기까지 = 레퍼런스·공개 제약 기반의 일반론. 프로토타입으로 "이식하면 이런 문제 → 이렇게 풀림"까지는 직접 확인. 단, 정확한 전략은 Joint 실제 내부 구조 확인 후 확정 — 겸손이 아니라 실무의 순서.
loadURL로 띄우는 셸. 웹 배포
1회로 데스크톱 최신화. (동작 확인)
이 프로토타입 = 완성 제품이 아니라, 이식이라는 실무를 어떤 순서·근거로 결정하는지 보여주는 기록. 다 풀어놓기보다 무엇이 아직 열려 있는지 안다는 걸 남기려 함.