Fecit 웹 버전을 배포하기까지
모바일 앱을 웹으로 옮기면서 겪은 서버 설정, 프록시, Apple/Google 로그인 삽질기.
Fecit 웹 버전을 배포하기까지
Fecit은 원래 모바일 앱입니다. iOS와 Android. 그런데 데스크톱에서도 쓰고 싶다는 생각이 들었습니다. 그래서 웹 버전을 만들었습니다.
fecit.vaudium.net. Vite + React + TypeScript로 스캐폴딩하고, 모바일 앱의 로직을 최대한 재사용하면서 웹에 맞게 다시 짰습니다. React Router v7, Jotai, Tailwind CSS v4, i18next. 모바일에서 쓰던 shared palette과 locale 파일을 직접 import해서 디자인과 번역을 통일했습니다.
앱을 만드는 건 어렵지 않았습니다. 문제는 배포였습니다.
서버 분리와 프록시
API 서버는 이미 돌아가고 있었습니다. 웹 앱은 별도 서버에 올려야 했습니다. brew로 설치한 httpd(Apache2)를 사용했습니다.
왜 Apache냐면, 이미 서버에 설치되어 있었고, 설정이 익숙했기 때문입니다. Nginx가 더 나을 수도 있지만, 지금 단계에서는 굳이 바꿀 이유가 없었습니다.
웹 앱에서 API를 호출할 때 CORS 문제가 생깁니다. 같은 도메인이 아니니까요. 이걸 해결하기 위해 httpd에서 프록시를 설정했습니다. /api/* 요청은 API 서버로 포워딩하고, 나머지는 웹 앱의 정적 파일을 서빙하는 구조.
여기까지는 순탄했습니다.
Apple 로그인 — localhost의 함정
Apple 로그인을 구현할 때 개발 환경에서 테스트하려고 redirect URI에 localhost를 적었습니다.
안 됩니다.
Apple은 redirect URI에 localhost를 허용하지 않습니다. 이걸 몰랐습니다. 에러 메시지도 친절하지 않아서 한참을 헤맸습니다. 결국 실제 도메인으로 테스트해야 했습니다.
모바일에서는 Apple Sign In SDK가 이런 걸 다 처리해줘서 몰랐던 건데, 웹에서 OAuth를 직접 다루니까 이런 제약이 드러났습니다.
Google 로그인 — access_token vs ID Token
Google 로그인은 다른 종류의 삽질이었습니다.
처음에는 useGoogleLogin을 사용했습니다. 이걸로 받는 건 access_token입니다. 그런데 서버에서 기대하는 건 ID Token(credential)이었습니다. 모바일 앱에서는 Google Sign-In SDK가 ID Token을 바로 주는데, 웹의 useGoogleLogin은 access_token을 줍니다. 형식이 다릅니다.
그래서 GoogleLogin 컴포넌트로 바꿨습니다. 이건 credential(ID Token)을 줍니다. 문제 해결… 인 줄 알았는데, 이번에는 버튼 디자인을 커스텀할 수 없었습니다. Google이 제공하는 기본 버튼만 쓸 수 있습니다.
결국 google.accounts.id.prompt()를 직접 호출하는 방식으로 바꿨습니다. 커스텀 버튼을 유지하면서 ID Token을 받을 수 있습니다. 커밋 3개를 거쳐서야 해결했습니다.
돌아보면
웹 배포는 앱 개발보다 예상 밖의 삽질이 많았습니다. 코드를 짜는 시간보다 설정을 맞추는 시간이 더 길었습니다.
- 서버 설정: httpd 프록시 설정은 한 번 잡으면 끝이지만, 처음 잡을 때 시행착오가 있었습니다
- Apple 로그인: localhost 제한을 미리 알았으면 30분이면 끝났을 일을 반나절 헤맸습니다
- Google 로그인: access_token과 ID Token의 차이를 이해하는 데 시간이 걸렸습니다
모바일 SDK가 얼마나 많은 걸 감춰주고 있었는지 웹에서 직접 구현해보니 실감합니다. 하지만 덕분에 OAuth 플로우를 훨씬 깊이 이해하게 되었습니다.
fecit.vaudium.net은 지금 잘 돌아가고 있습니다.