전체 프로젝트

Product case study

NeoStudio2 — 스마트펜 하이브리드 문서 앱

React와 .NET MAUI를 함께 사용하는 스마트펜 연동 문서 앱입니다. WebView의 React 화면과 네이티브 기능을 연결해 문서 작성, 로컬 저장과 동기화, Canvas와 PDF 처리 기능을 개발했습니다.

2022.12 — 현재Frontend / Hybrid App Engineer

01

React와 .NET MAUI 사이의 데이터 전달

Context

기존 네이티브 앱을 하이브리드 앱으로 전환하면서 WebView의 React 화면과 C# 코드가 로컬 데이터와 플랫폼 기능을 주고받아야 했습니다. 큰 데이터를 한 번에 전달하거나 브리지 초기화 순서가 어긋나면 호출이 끊기기도 했습니다.

Goal

React 화면에서 네이티브 저장소와 기능을 쓰면서도, 큰 데이터를 보내거나 실행 환경이 바뀔 때 브리지 호출이 끊기지 않는 것이 조건이었습니다.

What I changed

  • React에서 C#의 로컬 DB, 인쇄와 파일 공유 기능을 호출하는 WebView JS 브리지를 구현했습니다.
  • 큰 PDF는 여러 조각으로 나눠 보냈습니다.
  • 브리지가 응답하지 않을 때는 스마트펜에서 받은 스트로크를 네이티브 DB에 먼저 저장해 필기를 지켰습니다.
  • .NET 런타임을 바꾼 뒤 브리지 채널이 두 번 만들어져 호출이 막히는 문제를 해결했고, 기기의 언어 설정에 따라 소수점 구분자를 다르게 해석하던 부분은 문화권 설정을 고정해 바로잡았습니다.

Outcome

React 화면에서 네이티브 데이터와 기능을 그대로 쓸 수 있게 됐고, 큰 파일을 보내거나 브리지 응답이 없는 상황에서도 작업이 끊기지 않습니다.

02

기존 사용자 데이터 가져오기

Context

처음에는 Google Drive에서 기존 사용자 데이터를 가져오도록 구현했습니다. 하지만 기존 방식으로는 파일을 직접 읽기 어려워졌습니다.

Goal

기존 문서와 태그, 스트로크를 새 앱으로 가져오되, Drive API를 직접 호출하지 않는 경로가 필요했습니다.

What I changed

  • 앱 환경에서 Google 인증을 처리하고 Drive의 기존 데이터를 읽어 오는 기능을 구현했습니다.
  • Drive API에서 파일을 직접 읽는 대신 자체 서버 저장소에서 기존 사용자 데이터를 받아오도록 이전 경로를 바꿨습니다.
  • 가져오기가 중단되면 이어서 진행할 수 있도록 상태를 저장하고, 페이지를 구분하는 값이 겹치거나 일부 스트로크가 빠지는 문제를 잡았습니다.

Outcome

기존 사용자가 데이터를 옮겨올 수 있게 됐고, 중단된 지점부터 다시 가져올 때 생기던 중복과 누락도 사라졌습니다.

03

오프라인 저장과 서버 동기화

Context

앱은 사용자가 작성한 노트, 페이지, 스트로크, 태그와 첨부 데이터를 로컬 DB에 먼저 저장한 뒤 서버와 동기화합니다. 앱 종료, 백그라운드 전환, 네트워크 실패와 임시 ID 교체가 이 과정에 겹치면 아직 서버에 올라가지 않은 변경이 빠지거나 같은 노트와 페이지가 중복될 수 있었습니다.

Goal

로컬에서 발생한 생성, 수정, 삭제 내역을 올바른 대상에 반영하고, 동기화가 중간에 멈춰도 남은 작업을 다시 이어가야 했습니다.

What I changed

  • 노트, 페이지, 스트로크, 태그와 첨부 데이터를 로컬 DB에서 업로드 대기 상태와 서버 반영이 끝난 상태로 나눠 관리하고, 생성, 수정, 삭제 내역을 항목별로 처리했습니다.
  • React에서 처리하던 업로드와 서버 변경분 다운로드 로직을 .NET MAUI의 C# 코드로 단계적으로 옮겼습니다.
  • 오프라인에서 만든 노트와 페이지가 서버 ID를 받은 뒤에도 로컬 DB와 화면에서 같은 대상을 가리키도록 ID와 관련 참조를 함께 바꿨습니다. 임시 ID와 서버 ID가 함께 남아 생기던 중복도 정리했습니다.
  • 앱 종료 전에는 아직 DB에 반영되지 않은 스트로크를 저장했습니다. 백그라운드 전환이나 네트워크 실패 시에는 동기화를 대기하거나 재시도하고, DB 복구와 다운로드 실패가 잘못된 노트와 페이지 정리로 이어지지 않게 막았습니다.

Outcome

앱 종료 직전의 스트로크 유실, 동기화 중 노트와 페이지 중복, 복구 과정에서 잘못된 노트가 만들어지는 문제를 바로잡았습니다. 동기화가 중단돼도 남은 작업은 다음 실행에서 이어지도록 했습니다.

04

서식 텍스트의 Canvas와 PDF 렌더링

Context

편집기에서 입력한 서식 있는 텍스트가 Canvas와 썸네일, PDF에서 서로 다르게 보였습니다. 한글 입력과 화면 배율에 따라 글자 위치, 줄바꿈과 색상이 달라지기도 했습니다.

Goal

입력 화면에서 지정한 텍스트 서식과 배치가 Canvas와 출력 결과에서도 같은 형태로 유지되어야 했습니다.

What I changed

  • 텍스트의 크기, 색상, 굵기 같은 HTML 서식을 Canvas에 그릴 수 있도록 변환하고, 입력 위치와 화면 배율에 맞춰 좌표를 계산했습니다.
  • 한글을 조합해 입력하거나 줄바꿈을 연속으로 넣을 때, CJK 폰트와 이모지가 제대로 그려지지 않던 문제를 잡았습니다.
  • 기존 Web Worker 렌더링 경로와 PDF, PNG 출력에도 같은 텍스트 스타일이 반영되게 했습니다.

Outcome

편집 화면에서 작성한 텍스트가 Canvas, 썸네일, PDF 어디에서나 같은 서식과 위치로 나옵니다.

05

웹과 앱의 미디어 자동 재생

Context

이 앱은 Android와 iOS, 웹 브라우저에서 함께 서비스됩니다. 문서에 연결된 영상을 모든 환경에서 자동으로 재생해야 했지만, 브라우저와 앱 WebView는 자동 재생 정책이 달랐고, YouTube와 Google Drive처럼 영상을 제공하는 방식에 따라 재생 주소와 로딩 과정도 제각각이었습니다.

Goal

웹과 앱 모두에서 영상을 자동으로 재생하고, 외부 화면으로 이동하지 않은 채 현재 문서에서 재생을 제어하는 것이 목표였습니다.

What I changed

  • 웹에서는 YouTube와 Vimeo 영상이 자동으로 재생되도록 React 플레이어를 구성했습니다. 앱에서는 Android와 iOS의 WebView 설정을 조정해 사용자 입력 없이 미디어가 재생되게 했습니다.
  • Google Drive와 Dropbox 영상은 웹과 앱에서 재생할 수 있는 주소로 변환했습니다. 앱에서는 .NET MAUI의 네이티브 플레이어를 연결하고, 페이지에 포함된 영상을 미리 내려받아 캐시된 파일부터 재생했습니다.
  • 앱에서는 YouTube 영상을 눌러도 외부 화면으로 넘어가지 않도록 영상 위에 재생 컨트롤을 직접 구현했습니다.
  • 페이지를 이동하거나 재생에 실패하면 재생 상태를 초기화하고, 미디어 재생과 녹음이 동시에 시작되지 않도록 제어했습니다.

Outcome

웹과 앱 어디에서나 문서에 연결된 미디어가 자동으로 재생됩니다. YouTube, Vimeo, Google Drive와 Dropbox 영상도 현재 문서 안에서 재생되고, 페이지를 옮긴 뒤 재생이 계속되거나 녹음과 겹치던 문제도 사라졌습니다.

06

BLE 스마트펜 연결과 펜별 데이터 처리

Context

BLE 스캔과 자동 연결 요청이 겹치거나 앱이 백그라운드로 전환되면 연결 작업이 계속될 수 있었습니다. 여러 펜을 쓸 때는 일부 펜의 오프라인 스트로크만 표시되거나 조회가 끝나지 않기도 했습니다.

Goal

앱이 백그라운드로 전환되거나 다시 활성화될 때 BLE 연결을 정리하고, 연결 상태와 수신 데이터를 펜별로 구분해야 했습니다.

What I changed

  • BLE 스캔과 자동 연결을 취소한 뒤 다시 시작할 수 있도록 바꾸고, 같은 장치가 중복으로 등록되지 않게 막았습니다.
  • 앱이 백그라운드로 전환되면 연결을 종료하고, 다시 활성화됐을 때 재연결 이벤트가 다른 펜 상태를 참조하지 않도록 처리했습니다.
  • 여러 펜의 오프라인 스트로크를 장치별로 요청해 화면에 표시했습니다.

Outcome

여러 펜을 연결하거나 다시 연결한 뒤에도 연결 상태와 오프라인 스트로크가 펜별로 정확히 구분됩니다.