하이브리드 웹뷰 앱을 만들면서 백엔드를 Firebase Cloud Functions로 처음 짰다. 서버리스는 써본 적이 없어서, functions/ 폴더에 함수를 짜놓고도 이걸 어떻게 웹앱이 쓰는건지 감이 안 왔다. 처음엔 '웹앱에서 부르면 되겠지' 했는데 그게 아니었다. 정리하면서 서버리스 구조를 좀 더 알게되었다.
1. functions/ 와 webapp/ 는 완전히 다른 배포 단위
같은 레포에 있어도, 이 둘은 서로 별개의 프로그램이다.
functions 안에는 서버 환경변수, 파일시스템 접근처럼 브라우저에서 돌리면 런타임 에러가 나는 코드가 있다. 게다가 admin SDK는 보안 규칙을 우회하는 관리자 권한을 쓰는데, 그 권한을 여는 비밀키가 브라우저 번들에 실려 나가면 누구나 개발자 도구로 훔쳐본다. 그래서 이 코드들은 서버에만 있어야 하고, webapp 과는 별개로 배포되어야 한다.
functions/
- Google Cloud (Cloud Functions 런타임)에 배포
- 구글 서버에서 실행, 요청 올 때만 동작
webapp/
- firebase hosting에 배포
- 사용자 브라우저에서 실행
즉 함수는 구글 서버에 있고, 웹앱은 네트워크로 해당 함수들을 요청한다.
2. onCall, onRequest, onSchedule
firebase 에서 제공하는 주요 트리거에는 onCall, onRequest, onSchedule 이 있다. 서버리스 함수는 프로세스가 없기 때문에 호출자가 없고 그래서 스스로 실행되지 않는다. 트리거는 호출자 역할을 하며 해당 트리거에 걸어둔 이벤트가 발생하면 firebase가 인스턴스를 띄워 함수를 호출하는 구조다.
onCall(handler) // 클라이언트가 firebase SDK로만 호출 가능 (HTTPS)
onRequest(handler) // curl, 외부에서 호출 가능 (HTTPS)
onSchedule("every 24 hours", handler) // cron => 자동으로 호출
- onCall vs onRequest: onCall은 firebase가 CORS, 인증, 토큰, 에러 코드 등을 자동으로 준다. 다만 firebase SDK를 통해서만 호출이 가능하다. 반면에 onRequest는 CORS, 인증, 토큰, 에러 코드 등을 직접 다뤄야 하고 httpsCallable로 호출도 불가능하다. 대신 fetch, axios 등 외부에서 호출 가능하다.
- onSchedule: 스케줄에 저장된 계획에 맞춰 자동으로 호출이 된다.
3. 배포가 실제로 하는 일
firebase deploy --only functions 이 실행되면
- predeploy 빌드: TS -> JS로 컴파일된다.
- JS + package.json만 가지고 firebase Storage에 업로드한다. 이 때 package.json 만 올리는 이유는 로컬과 구글 리눅스의 환경이 다르기 때문이다.
- firebase에선 JS와 package.json을 가지고
npm install로 해당 환경에 맞는 의존성이 설치된 리눅스 이미지를 만든다. - 완성된 이미지를 가지고 서버리스 런타임에 올린 뒤 트리거를 연결한다.
4. 클라이언트에서 호출: httpsCallable과 onCall의 규약
웹앱에서는 이렇게 부른다.
const call = httpsCallable(functions, "registerUser");
const res = await call({ uid, fcmToken, filters });
httpsCallable은 아직 네트워크를 타지 않고 원격 함수를 부를 준비에 불과하고, await call()에서 실제 네트워크 요청이 나간다.
보낸 객체는 { "data": { }}로 감싸서 전송되고, 서버 onCall 핸들러가 request.data로 받는다.
서버의 리턴값은 { "result": }로 감싸져 오고, 클라이언트는 res.data로 받는다.
만약 서버에서 에러가 터져 throw new HttpsError("not-found", ) 하면, 클라이언트는 catch 에서 err.code === "functions/not-found"로 잡을 수 있다. 마지막으로 CORS, JSON 직렬화 등은 SDK(httpsCallable, call)이 알아서 다 해준다.