Loading the catalog…
Loading the catalog…
1편에서 기존 React + Vite 프로젝트에 Capacitor를 연결하고 iOS / Android 프로젝트를 생성하는 과정까지 정리했다. 처음에는 기존 React 코드를 거의 그대로 사용할 수 있으니 웹에서 잘 동작하던 기능도 앱에서 그대로 동작할 것이라고 생각했다. 실제로 UI 대부분은 큰 수정 없이 실행할 수 있었다. 하지만 API 요청이나 실행 환경처럼 브라우저에서는 당연하게 사용하고 있던 부분들이 Capacitor 앱에서는 조금 다르게 동작했다. 이번 글에서는 환승여행을 PWA에서 Capacitor 앱으로 확장하면서 웹과 앱 환경의 차이 때문에 따로 처리했던 부분들 을 정리해보려고 한다. 1. 웹에서 사용하던 /api 요청이 앱에서는 그대로 동작하지 않았다 환승여행 웹 프로젝트에서는 API를 호출할 때 /api/... 형태의 상대 경로를 사용하고 있었다. 개발 환경에서는 Vite의 proxy 설정을 통해 /api 요청을 실제 백엔드 서버로 전달하는 구조였다. server: { proxy: { "/api": { target: apiProxyTarget, changeOrigin: true, secure: false, }, }, }, 실제 프로젝트에서도 VITE_API_BASE_URL 을 proxy target으로 읽어 /api 요청을 백엔드로 전달하도록 구성되어 있다. 웹 개발 환경에서는 문제가 없었다. 브라우저 → Vite 개발 서버 → Proxy → Backend 라는 중간 단계가 존재했기 때문이다. 하지만 Capacitor로 앱을 빌드하고 나니 상황이 달라졌다. Capacitor 앱에서는 Vite 개발 서버가 중간에 존재하지 않기 때문에 웹에서 사용하던 proxy에 그대로 의존할 수 없었다. 즉, /api/... 라고 요청한다고 해서 앱이 알아서 기존 백엔드 서버를 찾아가는 것이 아니었다. 해결 방법 웹과 Capacitor의 API 환경을 분리하기로 했다. Capacitor 전용 환경 파일을 만들고 .env.capacitor 여기에 앱에서 사용할 백엔드 주소를 따로 지정했다. 현재 프로젝트도 Capacitor 전용 VITE_API_BASE_URL 을 별도로 사용하고 있다. 그리고 앱 빌드 시에는 vite build --mode capacitor 를 사용하도록 구성했다. 일반 웹 빌드와 Capacitor 빌드의 환경을 분리하면서 같은 React 코드를 사용하더라도 실행되는 환경에 따라 다른 API 설정을 사용할 수 있게 되었다. 이번 과정에서 처음 알게 된 점은 proxy는 내 React 코드의 기능이 아니라 개발 서버가 제공해주던 기능이었다는 것 이었다. 웹에서 너무 자연스럽게 사용하고 있어서 앱으로 옮기기 전까지는 크게 의식하지 못했던 부분이었다. 2. Capacitor 전용 build mode 분리하기 API 환경을 분리하면서 빌드 과정도 웹과 앱을 따로 관리할 필요가 생겼다. 기존에는 단순히 npm run build 를 사용하면 됐지만, Capacitor에서는 앱용 환경 변수를 사용해야 했다. 그래서 package.json 에 별도의 script를 추가했다. "build:ios": "tsc -b && vite build --mode capacitor", "build:app": "tsc -b && vite build --mode capacitor && cap sync" 현재 환승여행 프로젝트에서도 웹 빌드와 Capacitor 빌드를 이렇게 분리하고 있다. 이제 앱에서 확인하고 싶을 때는 npm run build:app 을 실행하면 TypeScript Build ↓ Vite Build (capacitor mode) ↓ dist 생성 ↓ Capacitor Sync ↓ iOS / Android 프로젝트 반영 흐름을 한 번에 실행할 수 있다. 처음에는 웹 코드를 수정하고 Xcode에서 다시 Run만 누르면 당연히 변경 사항이 반영될 것이라고 생각했다. 하지만 Capacitor는 Vite의 빌드 결과물을 네이티브 프로젝트에서 사용하는 구조이기 때문에 웹 소스를 수정했다면 새로운 dist 를 만들고 다시 native 프로젝트에 sync해야 했다. 이 과정을 script로 묶어두니 수정할 때마다 명령어 여러 개를 반복해서 입력하지 않아도 돼서 훨씬 편했다. 3. HTTP 요청도 웹과 완전히 같지는 않았다 Capacitor를 연결하면서 HTTP 요청 역시 브라우저에서 보내는 요청과 동일하게만 생각하면 안 된다는 것을 알게 됐다. 현재 프로젝트의 Capacitor 설정에는 CapacitorHttp 가 활성화되어 있다. plugins: { CapacitorHttp: { enabled: true, }, }, 처음에는 “React에서 원래 사용하던 요청을 그대로 보내면 되는 것 아닌가?”라고 생각했는데, 앱으로 실행하면 WebView와 네이티브 앱의 네트워크 환경까지 같이 고려해야 했다. 특히 브라우저 환경에서 사용하던 proxy나 origin 구조가 앱에서는 동일하게 존재하지 않는다. 결국 중요한 건 기존 API 코드 자체를 전부 새로 만드는 것이 아니라 어디에서 실행되고 있는지를 기준으로 요청 환경을 분리하는 것 이었다. Capacitor를 쓰면서 가장 많이 느낀 것도 이 부분이었다. 화면만 보면 똑같은 React 앱이지만, 그 React 앱을 실행하고 있는 환경은 더 이상 일반 브라우저가 아니었다. 4. 웹앱을 그대로 넣었다고 끝은 아니었다 Capacitor 연결 자체는 생각보다 빨랐다. 기존 웹 프로젝트를 빌드하고 iOS와 Android 프로젝트를 생성하면 React 화면 자체는 꽤 빠르게 앱에서 확인할 수 있었다. 하지만 실제 모바일 앱처럼 사용하기 시작하면 웹에서는 크게 의식하지 않았던 문제들이 보이기 시작했다. 환승여행은 이전부터 PWA를 기준으로 개발하면서 모바일 Safe Area Bottom Navigation 위치 화면 높이 앱 재실행 시 인증 상태 등을 한 번씩 대응했던 상태였다. 그래서 Capacitor에서도 기존 모바일 UI를 대부분 활용할 수 있었지만, 웹에서 한 번 해결했던 문제라고 해서 네이티브 환경에서도 당연히 동일하다고 가정하면 안 됐다. 특히 실제 iPhone / Android 환경에서 실행해보면서 화면과 기능을 다시 확인하는 과정이 필요했다. 이 부분은 PWA를 개발했을 때 느낀 점과도 비슷했다. Chrome 개발자 도구에서 모바일 화면으로 잘 보이는 것과 실제 휴대폰에서 앱으로 실행했을 때 잘 동작하는 것은 별개의 문제였다. 5. 웹 코드 재사용 의 의미를 잘못 생각하고 있었다 Capacitor를 선택한 가장 큰 이유는 기존 React 코드를 재사용할 수 있다는 점이었다. 실제로 대부분의 UI와 서비스 로직은 그대로 사용할 수 있었다. 그런데 처음에는 이 말을 “React 코드가 웹에서 동작하면 앱에서도 똑같이 동작한다.” 정도로 이해했던 것 같다. 직접 사용하고 나니 조금 달랐다. 정확히는 웹 애플리케이션의 구조와 코드를 재사용할 수 있지만, 실행 환경의 차이는 별도로 처리해야 한다. 에 가까웠다. UI 컴포넌트나 상태 관리, 페이지 로직은 대부분 그대로 사용할 수 있지만 API 주소 네트워크 요청 환경변수 플랫폼 설정 네이티브 기능 처럼 브라우저 바깥의 영역과 연결되기 시작하면 iOS / Android 환경을 따로 생각해야 한다. 오히려 이 차이를 이해하고 나니 Capacitor가 왜 웹을 앱으로 변환하는 도구 라기보다 웹과 네이티브 사이를 연결하는 도구 에 가까운지도 조금 이해할 수 있었다. 💡 이번 작업에서 배운 점 이번 작업에서 가장 크게 느낀 것은 “웹에서 잘 된다”라는 말에는 생각보다 많은 전제가 숨어있다는 것 이었다. 웹에서는 브라우저와 개발 서버가 proxy를 처리하고 origin을 가지고 있고 환경변수를 읽어 빌드하고 네트워크 요청을 처리해준다. 평소에는 이런 부분을 크게 의식하지 않아도 됐지만 Capacitor로 실행 환경이 바뀌자 그동안 당연하게 사용하던 것들이 하나씩 눈에 들어오기 시작했다. 특히 proxy 문제를 확인하면서 단순히 설정을 고치는 것보다 “이 기능은 누가 제공하고 있었던 거지?” 를 먼저 생각하는 것이 문제를 이해하는 데 도움이 됐다. Capacitor를 사용하면서 네이티브 개발을 깊게 배웠다고 하기는 어렵지만, 적어도 내가 작성한 웹 애플리케이션이 어떤 환경에 의존하고 있었는지는 이전보다 많이 이해하게 된 것 같다. 마무리 Capacitor를 연결하는 것 자체는 어렵지 않았다. 오히려 그다음부터가 시작이었다. 같은 React 코드를 사용하고 있어도 브라우저, PWA, iOS, Android는 각각 다른 실행 환경이고 API 요청이나 플랫폼 기능에서는 그 차이를 고려해야 했다. 이번에는 우선 웹과 앱의 API 환경 분리 Capacitor 전용 Build Mode 네이티브 환경에서의 HTTP 요청 실제 기기에서의 재확인 과정을 정리해봤다. 현재는 Apple Login 기능까지 추가해 앱스토어 배포를 목표로 심사에 넣은 상태이다. Apple Login은 단순히 기존 로그인 버튼을 앱에 옮기는 것으로 끝나는 것이 아니라 Apple Developer 설정과 네이티브 환경의 인증 흐름까지 함께 다뤄야 해서 별도의 글로 정리해보려고 한다.
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
Capacitor 트러블슈팅 | 웹에서는 되는데 앱에서는 왜 안 될까?. 1편에서 기존 React + Vite 프로젝트에 Capacitor를 연결하고 iOS / Android 프로젝트를 생성하는 과정까지 정리했다. 처음에는 기존 React 코드를 거의 그대로 사용할 수 있으니 웹에서 잘 동작하던 기능도 앱에서 그대로 동작할 것이라고 생각했다. 실제로 UI 대부분은 큰 수정 없이 실행할 수 있었다. 하지만 API 요청이나 실행 환경처럼 브라우저에서는 당연하게 사용하고 있던 부분들이 Capacitor 앱에서는 조금 다르게 동작했다. 이번 글에서는 환승여행을 PWA에서 Capacitor 앱으로 확장하면서 웹과 앱 환경의 차이 때문에 따로 처리했던 부분들 을 정리해보려고 한다. 1. 웹에서 사용하던 /api…
Open source