Firebase Cloud Functions๋? Firebase์์ cloud functions์ ๋ํ ์ค๋ช ์ ์ฐพ์๋ณด๋ฉด ์๋์ ๊ฐ์ด ๋์จ๋ค.
Firebase์ฉย Cloud Functions๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ด๋ฒคํธ, HTTPS ์์ฒญ,ย Admin SDKย ๋๋ย Cloud Schedulerย ์์ ์ ์ํด ํธ๋ฆฌ๊ฑฐ๋ ์ด๋ฒคํธ์ ๋ํ ์๋ต์ผ๋ก ๋ฐฑ์๋ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์คํํ ์ ์๋ ์๋ฒ๋ฆฌ์ค ํ๋ ์์ํฌ์ ๋๋ค.
์ฝ๊ฒ ๋งํด์ ์๋ฒ๊ฐ ๊ธฐ๋ณธ์ ์ธ api์ ๋ํ ๋ก์ง์ ์์ฑํ ํ ์ดํ ํด์ผ ํ๋ ๋ฐฐํฌ๋ถํฐ ๊ด๋ฆฌ ๋ฌธ์ ์ ๋ฐ์ firebase์ชฝ์ ์ด๊ดํ๋ ๊ฒ์ด๋ค. ์ ๋ช ํ ์๋ฒ๋ฆฌ์ค ํ๋ ์์ํฌ๋ functions ๋ง๊ณ ๋ aws lambda ๋ฑ์ด ์๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด ์๋ฒ๋ฆฌ์ค๋ผ๋ ๊ฐ๋ ์ ์ ํํ ๋ฌด์์ ์๋ฏธํ๋ ๊ฑธ๊น?
์๋ฒ๋ฆฌ์ค ์ํคํ ์ฒ(serverless architecture)
๋จ์ด ๊ทธ๋๋ก server + less, ์๋ฒ๋ฅผ ๊ด๋ฆฌํ ํ์๊ฐ ์ ์ด์ง๋ค๋ ๋ป์ด๋ค. ๊ฐ๋ฐ์๊ฐ ์ผ์ผ์ด ์๋ฒ๋ฅผ ๊ด๋ฆฌํ ํ์ ์์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น๋ํ๊ณ ์คํํ ์ ์๋๋ก ํ๋ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ๊ฐ๋ฐ ๋ชจ๋ธ์ด๋ค. ๊ทธ๋ ๋ค๊ณ ์๋ฒ๊ฐ ์์ด์ง๋ ๊ฒ์ ์๋๋ค. ๊ทธ์ ๊ฐ๋ฐ์๊ฐ ์๋ฒ๋ฅผ ๊ฑด๋๋ฆด ํ์๊ฐ ์๋ค๋ ๋ป์ด์ง, ์๋ฒ๋ฆฌ์ค ๋ชจ๋ธ์๋ ์๋ฒ๋ ์กด์ฌํ๋ค. ํ์ง๋ง ๊ทธ๊ฑธ ๊ด๋ฆฌํ๋ ์ฃผ์ฒด๊ฐ ํด๋ผ์ฐ๋ ์ ๊ณต์ ์ฒด๋ก ๋ฐ๋๋ค๋ ์ ๊ณผ ์ด ํด๋ผ์ฐ๋ ์ ๊ณต์ ์ฒด์์ ์๋ฒ ์ธํ๋ผ์ ๋ํ ํ๋ก๋น์ ๋, ์ ์ง๊ด๋ฆฌ, ์ค์ผ์ผ๋ง ๋ฑ ์๋ฒ ๊ด๋ฆฌ์์ ํ์ํ ํ์์ ์ธ ์์๋ฅผ ๋์ ํด์ค๋ค.
์๋ฒ๋ฆฌ์ค ์ํคํ
์ฒ์ IaaS(Infrastructure-as-a-Service)๋ ํด๋ผ์ฐ๋ ์ปดํจํ
๋ชจ๋ธ์ด๋ผ๋ ์ ์์ ๊ณตํต์ ์ ๊ฐ์ง์ง๋ง, ์๋ฒ๋ฆฌ์ค๋ ์ด๋ฒคํธ ๊ด๋ฆฌ ๊ธฐ๋ฐ ๋ชจ๋ธ์ด๋ผ๋ ์ ์์ ํฐ ์ฐจ์ด์ ์ ๊ฐ์ง๋ค.

Iaas(Infrastructure-as-a-Service) PaaS(Platform-as-a-Service) SaaS(Software-as-a-Sercvice) ์ ํจ๊ป 3๋ ํด๋ผ์ฐ๋ ์๋น์ค ๋ชจ๋ธ ์ค ํ๋์ด๋ฉฐ, ์คํ ๋ฆฌ์ง, ๋คํธ์ํฌ, ์๋ฒ ๋ฑ์ ๋ชจ๋ ์ ๊ณตํ๋ฉด์ ์๋ฒ ๊ด๋ จ ์ธํ๋ผ๋ฅผ ๋ชจ๋ ์ ์ดํด์ค๋ค
Iaas๋ ์ฌ์ฉ์๊ฐ ์ธํ๋ผ๋ฅผ ์ง์ ๊ด๋ฆฌํ๋ ๊ฒ์ด์ง๋ง ์๋ฒ ์ค์ , ๋คํธ์ํน, OS ์ค์น ๋ฑ์ ์ธํ ๊ณผ ๊ฐ์ ์ผ๋ จ์ ๊ณผ์ ์ ํด๋ผ์ฐ๋ ์์์ ํ๋ ๊ฒ์ด๋ค. ๋ฐ๋ฉด ์๋ฒ๋ฆฌ์ค์ ๊ฒฝ์ฐ์๋ ์๋ฒ ์ธํ๋ผ์ ์ฒ์๋ถํฐ ๋๊น์ง ํด๋ผ์ฐ๋๋ฅผ ํตํด ์๋น์ค๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ์๋ก ๊ด์ฌํ๋ ์ ๋๊ฐ ๋ค๋ฅด๋ค.
๋ํ ๊ฐ์ฅ ์ค์ํ ์ ์ Iaas๋ ์๋ฒ๋ฅผ ๊ณ์ ๋๋ฆฌ๊ณ ์๋ ์ํ์ฌ์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ ์๊ฐ์ด๋ ์ฉ๋ ๋ฑ์ ๋ํด์ ๋น์ฉ์ด ์ฑ ์ ๋๋ ๋ฐ๋ฉด, ์๋ฒ๋ฆฌ์ค๋ ๋ด๊ฐ ์ค์ ํด๋์ ํน์ ํจ์๊ฐ ์คํ๋์์ ๊ฒฝ์ฐ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์์ผ ๋ฆฌ์์ค๋ฅผ ๋์ ์ผ๋ก ํ ๋นํ๋ค(๊ทธ๋์ ์ด๋ฆ์ lambda๋ functions๋ก ํ๋๋ฏ). ๋ฐ๋ผ์ ์๋ฌด๋ฆฌ ํธ๋ํฝ์ด ๋ชฐ๋ฆฐ๋ค ํ๋๋ผ๋ ๋์ ํ ๋น์ผ๋ก ๊ฒฌ๋ ์ ์๋ค. ํ์ง๋ง ์ด ๋งํผ ๋์ ๋์จ๋ค.(on-demand)
์๋ฌดํผ ์ด๋ฌํ ์๋ฒ๋ฆฌ์ค ์ํคํ ์ณ๋ FaaS(Function-as-a-Service) ์ Baas(Backend-as-a-Service) ๋ก ๋๋๋๋ฐ, ์ฐ๋ฆฌ๊ฐ ์๋ฒ๋ฆฌ์ค๋ฅผ ์ง์นญํ ๋๋ ์ฃผ๋ก FaaS๋ฅผ ๋ํ๋ธ๋ค๊ณ ๋ณด๋ฉด ๋๋ค. Faas๋ ํน์ ํจ์ ๋จ์๋ก ์ฝ๋๋ฅผ ์คํํ ์ ์๋ ์๋น์ค๋ก, ํด๋ผ์ฐ๋ ์๋น์ค ์ ๊ณต์๊ฐ ํน์ ์์ฒญ์ ๋ํด ๋ฐ์ํ๋ ํจ์๋ฅผ ์คํํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํ๋๋ก ํ์ฌ ์ฌ์ฉ์๋ค์ ์์ฒญ์ ํจ์๊ฐ ์คํ๋ ์ ์๋๋ก ํ๋ค.
BaaS(Backend-as-a-Service) BaaS๋ ํด๋ผ์ฐ๋ ์ ๊ณต์๊ฐ ๋ฐฑ์๋(์๋ฒ)์์ ํ์ํ ๋ชจ๋ ๊ฒ๋ค์ ์ ๊ณตํ๋ค(์ธ์ฆ, ์คํ ๋ฆฌ์ง, db ๋ฑ). Firebase๋ AWS Amplify๊ฐ ๋ํ์ ์ด๋ฉฐ, ์ด๋ฅผ ํตํด remote ํธ์ ์๋ฆผ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์๋ค.
ํน์ง
- Statelessํ๋ค
- ํจ์๊ฐ ์คํ๋๋ ๋์๋ง ๊ด๋ จ๋ ๋ฆฌ์์ค๋ง์ ํ ๋นํ๊ณ ๋๋๋ฉด ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์์์ ์ผ๋ก ์๊ฐํด๋ statelessํ ์๋ฐ์ ์๋ค. ๋ฐ๋ผ์ ์ง์๋๋ ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด์๋ DB๋ฅผ ๋ฐ๋ก ์ด์ฉํด์ผ ํ ํ์์ฑ์ด ์๋ค.
- Ephemeralํ๋ค(์ผ์์ ์)
- stateless์ ๋น์ทํ ๊ฒฐ์ด๊ธด ํ๋ค. ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๊ฒฝ์ฐ ์ปจํ ์ด๋๋ก์ ๋ฐฐํฌ๊ฐ ๋๊ณ ํ์๋๋ ๊ณผ์ ์ด ๋ฐ๋ณต๋๊ธฐ ๋๋ฌธ์ ์ผ์์ ์ผ๋ก ๋ฐฐํฌ๋๋ค๊ณ ๋ณผ ์ ์๋ค.
์ฅ๋จ์
์ฅ์ ์ ๋ฌด์๋ณด๋ค ์๋ฒ๋ฅผ ๊ทธ๋๋ก ๋งก๊ธฐ๊ธฐ ๋๋ฌธ์ ์๋ฒ์ ๋ํ ๋ฌธ์ ๋ฅผ ์ ๊ฒฝ์ฐ์ง ์์ ์ ์๋ค๋ ์ ์ด๋ค. ๋๊ฐ์ ํ๋ก ํธ๊ฐ ์์ ์ ํ๋ฆฌ์ผ์ด์ ํ๋ ๋ง๋ค๋ ค๋ฉด ์๋ฒ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ์๋ฒ ์ฝ๋ ์ฐ๊ณ aws ์ธ์คํด์ค๋ฅผ ํ๊ณ ๋ฐฐํฌํ๋ด.. ํ๋ก์ ์ค์ ํ๋ด.. pm2 ์ค์ ํ๋ด.. crontab ์ค์ ํ๋ดโฆ ํ ๊ฒ ๋ง์๋ฐ ์ ๋๋ก ํ ์ค๋ ๋ชฐ๋ผ์ ํน๋ฐ๊ฒ ์๊ฐ์ ๋ง์ด ์ก์๋จน๋๋ค. ํ์ง๋ง ์ด ๊ท์ฐฎ์ ์์ ๋ค์ ์์์ ์ฒ๋ฆฌํด์ค๋ค๊ณ ํ๋ ์ฐ๋ฆฌ์ผ ๋กํ๋ค. ๊ฒ๋ค๊ฐ ๋๋ถ๋ถ ํธ์ถ 100๋ง๊ฑด์ ๋๋ ๋ฌด๋ฃ๋ก ์ ๊ณตํ๋ ํ์์ด๋, ๋ฏธ๋ ํ๋ก์ ํธ์์๋ ์ด๋งํผ ์์ฑ๋ง์ถค์ธ ์๋ฒ๊ฐ ์๋ค. ๋ํ ๊ฐ์๊ธฐ ํธ๋ํฝ์ด ๋ชฐ๋ฆฐ๋ค๊ณ ํด๋ ๋ค์ด๋๋ฏนํ๊ฒ ์์์ ์ค์ผ์ผ์ ์ ํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ ๊ฒฝ์ธ ํ์๊ฐ ์๋ค(์ฌ์ค ์ ๊ฒฝ์ฐ๊ธด ํด์ผํจ).
ํ์ง๋ง ์ฅ์ ์ด ๋๋ ทํ ๋งํผ ๊ทธ์ ๋ฐ๋ฅธ ๋จ์ ๋ํ ๋ช ํํ๋ค. ์๋ฒ์ ๋ํ ์์ธ ์ค์ ์ด๋ ์ธํ๋ผ ์ ์ด ๋ฑ์ ๋ํด์๋ ๋ชจ์กฐ๋ฆฌ ์๋น์ค ์ ๊ณตํ๋ ๊ณณ์์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ์ค์ ํ ์ ์๋ ๋ฒ์๊ฐ ์ ํ๋๋ค๋ ์ ์ด ์์ ๋๋ฅผ ๋ฌถ๋๋ค. ๋ํ ์๋ฒ๋ฆฌ์ค ํจ์์ ๊ฒฝ์ฐ์๋ ๋ ๋ํ ์ด๋ฒ์ ํ๋ฉด์ ๋๋ ํ์์ผ๋ก, ์ฝ๋ ์คํํธ(Cold Start) ๋ผ๋ ํ์์ธ๋ฐ ์ฒ์ ํธ์ถ ์์ ์๊ฐ ์ง์ฐ์ด ์กฐ๊ธ ๋ฐ์ํ๋ค.
Cold Start๋?
์์์ ๋งํ๋ฏ์ด ์๋ฒ๋ฆฌ์ค๋ ํจ์๊ฐ ์คํ๋ ๋๋ง๋ค ๋ฆฌ์์ค(์์)์ ๋์ ์ผ๋ก ํ ๋นํ๋ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ ํจ์๊ฐ ์คํ๋๋ ๋์๋ง ๊ด๋ จ๋ ์์์ด ํ ๋น๋๊ณ , ์คํ์ด ๋๋๊ณ ์๋ต์ด ๋์์๋ค๋ฉด ํจ์๋ ํ ๋น๋ ๋ฆฌ์์ค๋ฅผ ๋ฐํํ๋ค.
์ฌ๋ฌ ์์ฒญ์ด ๋์์ ์ค๋ ๊ฒฝ์ฐ ๊ธฐ๋ฅ ๋ณต์ฌ๋ณธ์ ์์ฑํ์ฌ ์ฌํ์ฉํ๊ฑฐ๋ ํ๋ฒ ์์ฒญ์ด ๊ฐ์๋ ์ ์งํ๋ ์ ํด ์ํ์์ ๋ฒ์ด๋๊ฒ ๋๋ฉด ๋ค์ ์์ ๊ฐ์ ๋ผ์ดํ์ฌ์ดํด์ ๋์์ผ ํ๊ฒ ๋๋, ๋ค์๊ธ ์๋๊ฐ ๋๋ ค์ง๊ฒ ๋๋ ํ์์ด ์๋ค.
์์ ์ IEEE์์ AWS Lambda์ Microsoft Azure Functions์์ ๋ฒค์น๋งํฌ๋ฅผ ์ํํ ๊ฒฐ๊ณผ๊ฐ ์๋๋ฐ, 300ms์์ 24์ด๊น์ง ์๊ฐ์ด ๊ฑธ๋ ธ๋ค๊ณ ํ๋ ์ด ์ ์ด ์ฌ์ฉ์์๊ฒ ์๋น์ค ์ ๊ณต ๊ณผ์ ์ ์์ด์ ์น๋ช
์ ์ธ ๋จ์ ์ผ๋ก ์์ฉํ๋ค.
๊ทธ๋์ firebase์ cloud functions์ ๊ฐ์ ๊ณณ์์๋ ์ต์ ์ธ์คํด์ค ์๋ฅผ ์ค์ ํ์ฌ ์ปจํ
์ด๋๋ฅผ ์ ์งํจ์ผ๋ก์จ ์ต์ํ ๋ค์ด์ฌ ์ ์๋ ์ฌ์ฉ์์ ๋ํด ๋ฐ๋ก ์๋ตํ ์ ์๋๋ก ํ๋ ๋ฑ์ ๋๋น์ฑ
์ ํตํด ์๋๋ฅผ ์ค์ด๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค. ์ถ๊ฐ์ ์ผ๋ก ์ข
์์ฑ ์ค์ด๊ธฐ, ์บ์ฑ ํค๋ ์ฌ์ฉ, ์ฌ๋ฐ๋ฅธ ์ง์ญ ์ ํ ๋ฑ์ ํตํด ์๋๋ฅผ ์ค์ผ ์ ์๋ค๊ณ ๋ ํ์ง๋ง ํจ๊ณผ์ ์ผ๋ก ์ค์ด๊ธฐ ์ํ ํ์คํ ๋ฐฉ๋ฒ์ ์ต์ ์ธ์คํด์ค ์๋ฅผ ์ ์งํ๋ ๊ฒ์ด๋ค. ์ด๋ฅผ ์ํด ์ค์ผ์ค๋ง์ ๋ฐ๋ก ์์ผ ๊ณ์ํด์ ์ธ์คํด์ค๋ฅผ ์ ์งํ๋๋ก ํ๋ ๋ฑ์ ๋ฐฉ๋ฒ์ ์ฐ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
firebase Functions๋ฅผ ์ฌ์ฉํด์ ์๋ฒ๋ฆฌ์ค ๋ฐฐํฌํ๊ธฐ(node.js)
์๋ฒ๋ฆฌ์ค(FaaS)์ ๋ํด ์์๋ณด์์ผ๋ ์ฌ์ฉํ๋ฉด์ ์ง์ ์์๊ฐ๋ณด์. ๋๋ FaaS๋ค ์ค์ firebase Functions๋ฅผ ์ฌ์ฉํ๋ค. ์ด์ ๋ ์ด ์ ์ firebase๋ฅผ ์ฌ์ฉํ cloud messaging ๊ธฐ์ ์ ๋ด ํ๋ก์ ํธ ์ฑ์ ์ฌ์ฉํ๋ ๊ฒฝํ์ด ์๋๋ฐ, firebase์ cloud messaging ๋ง๊ณ ๋ ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค์ด ์ ๋ง ๋ง์ ๊ณ์ ํ๋ฒ์ฏค์ ์ฌ์ฉํด๋ณด๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์๊ธฐ ๋๋ฌธ์ด๋ค. ๋๋ ๊ธฐ์กด functions๋ฅผ ํตํด ๋ฐํ์์ ๋ฐฐํฌํ๋ ๊ฒ์ ๋์ด db ๋ํ ๋ฐ๋ก ๋ฐฐํฌํ๊ธฐ ๊ท์ฐฎ์๊ธฐ ๋๋ฌธ์ firebase์์ ์ ๊ณตํ๋ cloud db์ธ firestore๊น์ง ์ด์ฉํ ๊ฒ์ด๋ค.
ํด๋น ๊ธ์ node.js ๊ธฐ์ค์ผ๋ก ์์ฑ๋์์ต๋๋ค.
ํ๋ก์ ํธ initialize
ํ๋ก์ ํธ ์ถ๊ฐํ๊ธฐ
Firebaseย Console์์ ํ๋ก์ ํธ ์ถ๊ฐํ๊ธฐ
ํ๋ก์ ํธ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ์ฌ์ดํธ์์ ์ถ๊ฐํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ์ด๋ ค์ด ๊ฒ์ ์๋ค. ๋ฑ๋ก์ ๋๋ด๊ณ ๋๋ฉด ์ด๋ฐ ์์ผ๋ก ์ฝ์์ด ๋ฌ๋ค.
Firebase CLI ์ค์น ๋ฐ ์ด๊ธฐํ
npm install -g firebase-toolsfirebase functions๋ฅผ ๋ฐํ์์ ๋ฐฐํฌํ๊ธฐ ์ํด์๋ Firebase CLI๊ฐ ํ์ํ๋ค. npm์ ํตํด ๋ค์ด๋ก๋ ๋ฐ์์ฃผ์ ๋ค์ด๋ก๋ ๋ฐ์ผ๋ฉด Firebase๋ฅผ ์คํํ์ฌ ๋ก๊ทธ์ธ ๋ฐ ๊ธฐํ ์ด๊ธฐํ ์์ ์ ํ ์ ์๋ค.
// ์ ์ฐ๋ฉด ๋ก๊ทธ์ธ์ฐฝ ๋ธ
firebase login
// ๋ด๊ฐ ํ๋ก์ ํธ ์์ฑํ ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd ๋๋ ํ ๋ฆฌ
// firestore init
firebase init firestore
// functions init
firebase init functions์ด๋ ๊ฒ ์ธํ ํ๋ค๋ณด๋ฉด ์ด๋ค ์ธ์ด ์ธ๊ฑฐ๋๋ ์ง๋ฌธ์ด ๋์ฌ๊ฑด๋ฐ ๋๋ js๋ก ์ ํํด์ฃผ์๋ค
myproject
+- .firebaserc # `firebase use` ๋ช
๋ น์ด๋ฅผ ํตํด ํ๋ก์ ํธ ๊ฐ ์ ํ์ ๋น ๋ฅด๊ฒ ๋์์ฃผ๋ ์จ๊น ํ์ผ
|
+- firebase.json # ํ๋ก์ ํธ ์์ฑ์ ์ค๋ช
ํ๋ ํ์ผ
|
+- functions/ # ๋ชจ๋ ํจ์ ์ฝ๋๊ฐ ํฌํจ๋ ๋๋ ํฐ๋ฆฌ
|
+- .eslintrc.json # ์๋ฐ์คํฌ๋ฆฝํธ ๋ฆฐํ
๊ท์น์ ํฌํจํ ์ ํ์ ํ์ผ
|
+- package.json # Cloud Functions ์ฝ๋๋ฅผ ์ค๋ช
ํ๋ npm ํจํค์ง ํ์ผ
|
+- index.js # Cloud Functions ์ฝ๋์ ๋ฉ์ธ ์์ค ํ์ผ
|
+- node_modules/ # package.json์ ์ ์ธ๋ ์์กด์ฑ๋ค์ด ์ค์น๋๋ ๋๋ ํฐ๋ฆฌ
๊ทธ๋ผ ์ด๋ฐ ์์ผ๋ก ์ฒ์ ์ธํ ์ด ์๋ฃ๋๋ค
//package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"cors": "^2.8.5",
"firebase-admin": "^12.1.0",
"firebase-functions": "^5.0.0"
},
"devDependencies": {
"eslint": "^8.15.0",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.1.0"
},
"private": true
}package.json์ ์ด์ด๋ณด๋ฉด ์ด๋ฐ ์์ผ๋ก scripts๊ฐ ์ง์ฌ ์์ด์ ์ฐ๊ธฐ ํธํ๋ค
๊ธฐ์ตํ๋ฉด ์ข์ ๊ฒ์
- engines : ๋ด๊ฐ ์ธ node ๋ฒ์ ์ค์
- scripts
- serve : emulator๋ฅผ ํตํด์ ๋ก์ปฌ์์ ์๋ฒ๋ฅผ ํฌ ์ ์๋ค. ui๋ ์ ๊ณตํ๋ ๋ณด๊ณ ํ ์คํธํ๊ธฐ ๋๊ฟ์ด๋ค
- deploy: functions๋ง ๋ฐฐํฌ ์์งํ ๋ ์ด์ ๋๋ง ์ผ๋ค
// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {logger} = require("firebase-functions");
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
// The Firebase Admin SDK to access Firestore.
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");
initializeApp();์ฒ์์ index.js๊ฐ๋ฉด ์ก๋คํ ํ
์คํธ์ฉ๋ค์ด ์ฃผ์์ฒ๋ฆฌ ๋์ด ์๊ณ ์ด๋ฐ ์์ผ๋ก ํด๋ณด๋ผ๊ณ ํ๋ค.
initializeApp()์ ํตํด Firebase ์๋น์ค์ ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ์ด๊ธฐ ์ค์ ์ ํ ์ ์์ผ๋ฉฐ ์ธ์ฆ์ฒ๋ฆฌ ๋ฑ์ ๊ณผ์ ์ด ์ด๋ฃจ์ด์ง๋ฉด์ ์ฐ๊ฒฐ๋๋ค.
http ์์ฒญ ๋ฐ๊ธฐ
์์ฒญ ํธ๋ค๋ฌ๋ฅผ ์ฌ์ฉํ๋ฉด HTTP ์์ฒญ์ ํตํด ํจ์๋ฅผ ํธ๋ฆฌ๊ฑฐ์ํค๊ณ , ํด๋น ํจ์๋ฅผ ํตํด ์๋ต์ ๋ฐํ์ํฌ ์ ์๋ค.
| ์ต์ | ์ค๋ช |
|---|---|
region | HTTP ํจ์๋ ๋จ์ผ ๋ฆฌ์ ๊ณผ ์ฌ๋ฌ ๋ฆฌ์ ์ ์ง์ ํ ์ ์์ต๋๋ค. ์ฌ๋ฌ ๋ฆฌ์ ์ด ์ง์ ๋๋ฉด ๋ฆฌ์ ๋ณ๋ก ๋ณ๋์ ํจ์ ์ธ์คํด์ค๊ฐ ๋ฐฐํฌ๋ฉ๋๋ค. |
timeoutSeconds(Python์ ๊ฒฝ์ฐย timeout_sec) | HTTP ํจ์์์ ์ต๋ 1์๊ฐ์ ์ ํ ์๊ฐ์ ์ง์ ํ ์ ์์ต๋๋ค. |
cors | HTTP ํจ์๊ฐ CORS ์ ์ฑ
์ ์ง์ ํ ์ ์์ต๋๋ค.ย true๋ก ์ค์ ํ๋ฉด ๋ชจ๋ ์ถ์ฒ๊ฐ ํ์ฉ๋๊ณ ,ย string,ย regex,ย array๋ฅผ ์ค์ ํ๋ฉด ํ์ฉ๋ ์ถ์ฒ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค. ๋ช
์์ ์ผ๋ก ์ค์ ํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ false/CORS ์ ์ฑ
์์์
๋๋ค. |
| ์์ฒญ์ ๋ํ ์ต์ ์ ์ด๋ ๊ฒ ์ง์ ํด์ค ์ ์๋ค. |
const { onRequest } = require("firebase-functions/v2/https");
exports.sayHello = onRequest(
{ cors: [/firebase\.com$/, "flutter.com"] },
(req, res) => {
res.status(200).send("Hello world!");
}
);์๋ฐ ์์ผ๋ก cors์ ๊ฒฝ์ฐ ๋ด๊ฐ ๊ฐ์ง๊ณ ์๋ ๋๋ฉ์ธ๋ค์ ์ค์ ํ ์ ์์ผ๋ฉฐ, ์ผ๋ฐ express ์ฌ์ฉํ๋ฏ์ด ์์ฒญ๊ณผ ์๋ต์ ๋ํ ์ฝ๋ฐฑํจ์๋ฅผ onRequest๋ฅผ ํตํด ๋ณด๋ผ ์ ์๋ค. ์ฐธ๊ณ ๋ก exports.๋ค์ ์ด๋ฆ์ ๊ธฐ๋ณธ url ๋ค์ uri๋ก ๋ ๋ถ๊ฒ๋๋ค
const functions = require("firebase-functions");
exports.api = functions.region("asia-northeast3").https.onRequest();์ด๋ ๊ฒ region ์ต์ ๋ ์ง์ ํด์ฃผ์ด ๋ฆฌ์ ์ค์ ํด์ ์๋๋ฅผ ์กฐ๊ธ์ด๋ผ๋ ์ฌ๋ฆด ์ ์๋ค. ๋ํ https ํ๋กํผํฐ๋ฅผ ํตํด onRequest ๋ฉ์๋๋ฅผ ์คํํ๋ฉด ๋ฐฐํฌํ ๋ https ๋ก ๋ฐฐํฌ๋๋ค.
express + firebase functions๋ก ์์ฒญ๊ณผ ์๋ต ๊ด๋ฆฌํ๊ธฐ
๊ธฐ์กด์ ์น ์๋ฒ ํ๋ ์์ํฌ๋ฅผ initializeํ app์ ์์ฒญ ํธ๋ค๋ฌ์ ์ธ์๋ก ๋ฃ์ผ๋ฉด ์ ์ฒด ์ฑ์ HTTP ํจ์์ ์ ๋ฌํ ์ ์๋ค. ๋ฐ๋ผ์ ์์ฒญ๊ณผ ์๋ต์ ์น ์๋ฒ ํ๋ ์์ํฌ๋ฅผ ํตํด ๊ด๋ฆฌํ ์ ์๋ ๊ฒ์ด๋ค.
๋๋ express๋ฅผ ์ฌ์ฉํด์ ์์ฒญ๊ณผ ์๋ต์ ์ฒ๋ฆฌํด๋ณด๊ณ , ํด๋ผ์ด์ธํธ์ ์๋ฒ ๋ชจ๋ ํด๋น ์๋ฒ์์ ๋๋ฆด ์๊ฐ์ด์๋ค.
const functions = require("firebase-functions");
const path = require("path");
const { initializeApp } = require("firebase-admin/app");
const express = require("express");
const { routeManager } = require("./controller/index.js");
const cors = require("cors");
initializeApp();
const app = express();
app.use(cors());
app.use(express.static(path.join(__dirname, "dist")));
app.use(express.json());
routeManager(app);
app.get("/", (req, res, next) => {
res.send("index");
});
exports.api = functions.region("asia-northeast3").https.onRequest(app);์ด๋ ๊ฒ onRequest์ initializeํ app์ ์ธ์๋ก ๋ฃ์ด์ฃผ์๋ค. ๊ทธ๋ ๊ฒ ๋๋ฉด ํด๋น functions๋ก ์ค๋ http ์์ฒญ๋ค์ด ๋ชจ๋ app์ ํตํ๊ฒ ๋๋ค.
๋๋ ๊ฐ๋จํ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค ์์ ์ด์๊ธฐ ๋๋ฌธ์ ๋ฏธ๋ค์จ์ด๋ก cors์ ๋ฒ๋ค๋งํ ์ ์ ํ์ผ๋ค์ ๋ํ ์ค์ , json ํ์ฑ ๋ฑ์ ๋ํ ์ค์ ๋ง ํด์ฃผ์๋ค.
ํด๋ผ์ด์ธํธ๋ esbuild๋ฅผ ํตํด ๋ฒ๋ค๋งํ์ฌ dist๋ผ๋ ๋๋ ํ ๋ฆฌ์ ๋ฃ์ด๋์๊ธฐ ๋๋ฌธ์ ์ ๋ ๊ฒ ์ค์ ํด์ฃผ์๊ณ , ๊ธฐ๋ณธ url์ ๋ํด์๋ index.htmlํ์ผ๋ง์ ๋ณด๋ด๋๋ก ํ๋ค. ์ด์ฐจํผ SPA๋ก ๋ง๋ ํ๋ก์ ํธ๊ธฐ ๋๋ฌธ์ ์ ๊ฑฐ ํ๋๋ง ์ค์ ํด๋์๋ค.
api uri์ ํด๋ผ์ด์ธํธ๊น์ง ๋๋ ค๋ฐ์ ์ด์ ๋ ๊ท์ฐฎ์์๋ค
์๋ฌดํผ ๋ค๋ฅธ ์ถ๊ฐ์ ์ธ uri๊ฐ ๋ถ๋ ๊ฒ์ ๋ํด์๋ ๋ฐ๋ก functions๋ฅผ ๋๋์ง ์๊ณ express์ Router๋ฅผ ์ด์ฉํด์ ๊ฐ ์์ฒญ๊ณผ ์๋ต์ ์ฒ๋ฆฌํด์ฃผ์๋ค. functions๋ฅผ ๋๋ฉ์ธ๋ณ๋ก ๋๋๋ ๊ฒ๋ ์ข๊ณ ๊น๋ํ๊ฒ ์ข์ผ๋ฉด express์์ route๋ง ๋ณ๋ ํ์ผ๋ก๋ ๊ด๋ฆฌํ ์ ์์ด์ ์ทจํฅ์ฐจ์ด์ธ ๊ฒ ๊ฐ๋ค.
์ํผ ์ด์ ๋ ์ธํ
์ ๊ฐ๋จํ๊ฒ๋ง ํด์ฃผ๊ณ deploy โ๋ธ๊นโ ํ๋ฉด ๋ฐฐํฌ๋ ์ฒ์ฒ ์๋๋ค.
์ฐธ๊ณ ๋ก ๋ฐฐํฌํ๋ ค๋ฉด ๋ฌด๋ฃ ์๊ธ์ (spark) ์๊ธ์ ์์ Blaze ์๊ธ์ ๋ก ๋ฐ๊ฟ์ผ ํ๋ค.
๊ทธ๋๋ ์ต๊ฐํ ๋งํผ์ ๊ณต์ง๋ก ์ธ ์ ์์ผ๋ ์ผ๋ฌด์ง๊ฒ ์จ๋จน์

์๋ฌดํผ โ๋ธ๊นโ๋ง์ผ๋ก ๊ด๋ฆฌํ ํ์๊ฐ ์์ด์ง๋ ์๋ฒ๋ฆฌ์ค๋ ํ๋ก ํธ์ธ ๋์๊ฒ ๊ทธ์ ๋นโฆ
firestore ์ค์ ๊ณผ ์๋ฉด ์ข์ ๊ฒ๋ค
์ฒ์ ์ด๊ธฐ ์ธํ
ํ๋ ๊ณผ์ ์์ functions ์ธํ
์ ํ๊ณ firestore ์ธํ
๋ ๋ฐ๋ก ํ ์ ์๋ค.
http ์์ฒญ์ ๋ฐ๋ฅธ CRUD๋ express์์ firestore์ ์ ๊ทผํ์ฌ ์กฐ์ํ๋ฉด ๋๋ค.
const admin = require("firebase-admin");
async function getUserList() {
try {
const data = await admin.firestore().collection("todo").get();
return data.docs.map((doc) => new User(doc.data());
} catch (error) {
throw new Error(error);
}
}admin์ firestore ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ํ๋ก์ ํธ์ ์ฐ๊ฒฐ๋์ด ์๋ firestore์ ์ ๊ทผํ์ฌ CRUD๋ฅผ ์ํํ ์ ์๋ค.
const cityRef = db.collection('cities').doc('SF');
const doc = await cityRef.get();
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
}์ค์ํ ์ ์ ๊ฐ๋ณ ๋ฐ์ดํฐ ๊ฐ์ฒด์ ๋ํด์ data() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผ๋ง ๋ด๊ฐ ์ํ๋ ๋ฐ์ดํฐ๊ฐ ๋์จ๋ค
functions ์์ด firestore๋ง ์ฌ์ฉํ๊ธฐ
๋ฐ๋ก functions ์์ด firestore์๋ง ์ ๊ทผํ๊ณ ์ถ๋ค๋ฉด firebase SDK๋ฅผ ๋ฐ๋ก ์ค์ ํ ๋ค์ ์๋ฒ๋ฅผ ํค๋ฉด์ ์ด๊ธฐํ ์์ผ์ฃผ๋ฉด ๋๋ค.
firebase ์ฝ์ โ ํ๋ก์ ํธ ์ค์ โ ์๋ ๋ด ์ฑ โ SDK ์ค์ ๋ฐ ๊ตฌ์ฑ์์ ๋ด firebase config๋ฅผ ๋ณผ ์ ์๋ค.
npm install firebase
firebase ๋ฐ๋ก ์ค์นํ๊ณ
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "APIํค",
authDomain: "๋๋ฉ์ธ",
projectId: "ํ๋ก์ ํธID",
storageBucket: "์คํ ๋ฆฌ์ง๋ฒํท",
messagingSenderId: "๋ฉ์์ง ์ผ๋ID",
appId: "์ฑ์์ด๋",
measurementId: "์ธก์ ID"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);์ด๋ ๊ฒ InitializeAppํ๊ณ ์ฌ์ฉํ๋ฉด ๋๋ค.
์ฟผ๋ฆฌ
where๋ฌธ์ ์ฌ์ฉํด์ ๋จ์ผ ํน์ ์ฌ๋ฌ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
// Create a reference to the cities collection
const citiesRef = db.collection('cities');
// Create a query against the collection
const queryRef = citiesRef.where('state', '==', 'CA');
const citiesRef = db.collection('cities');
const snapshot = await citiesRef.where('capital', '==', true).get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});where๋ฌธ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋๊ณ , get()์ ํตํด ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
๊ฐ์ ธ์จ ๊ฐ์ฒด๋ QueryDocumentSnapshot๊ฐ์ฒด๋ก, ์ฌ๋ฌ ๊ฐ์ ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋ data() ๋ฉ์๋๋ฅผ ํตํด ๋ฐํ๋ ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํด์ผ ํ๋ค.
where()ย ๋ฉ์๋๋ ํํฐ๋งํ (ํ๋, ๋น๊ต ์ฐ์ฐ์, ๊ฐ)์ 3๊ฐ์ง ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ๋ค.
์ฐ์ฐ์๋
<ย : ๋ฏธ๋ง<=ย : ์๊ฑฐ๋ ๊ฐ์==ย : ๊ฐ์>:ย ๋ณด๋ค ํผ>=:ย ์ด์!=ย : ๊ฐ์ง ์์array-contains: ํฌํจํ๋ ๋ฐฐ์ดarray-contains-any: ๋ฐฐ์ด ์์ ๊ฒ๋ค๊ณผ ์ผ์นํ๋ ๋ชจ๋ ๋ ์ฝ๋in: ์ง์ ๋ ํ๋๊ฐ ๋น๊ต๊ฐ๊ณผ ์ผ์นํ๋ ๋ ์ฝ๋not-in: ๋์ผํ ํ๋์์ ๊ฐ์ง ์์ ์์ฝ๋
const stateQueryRes = await citiesRef.where('state', '==', 'CA').get();
const populationQueryRes = await citiesRef.where('population', '<', 1000000).get();
const nameQueryRes = await citiesRef.where('name', '>=', 'San Francisco').get();๋ณตํฉ์ฟผ๋ฆฌ(And)
==๋ array-contains๋ฅผ ์ฐ๊ฒฐํ์ฌ ์ ์ฝ ์กฐ๊ฑด์ AND์ ๊ฒฐํฉ
citiesRef.where('state', '==', 'CO').where('name', '==', 'Denver');
citiesRef.where('state', '==', 'CA').where('population', '<', 1000000);OR ์ฟผ๋ฆฌ ๋ง๋ค๊ธฐ
์ ์ฝ ์กฐ๊ฑด์ ๋ ผ๋ฆฌ์ OR๊ณผ ๊ฒฐํฉ
const bigCities = await citiesRef
.where(
Filter.or(
Filter.where('capital', '==', true),
Filter.where('population', '>=', 1000000)
)
)
.get();orderBy ๋ฐ ์กด์ฌ ์ฌ๋ถ
db.collection("cities").whereEqualTo("country", โUSAโ).orderBy(โpopulationโ);ํด๋น ์ฟผ๋ฆฌ์ ๋ํด์ ์ ๋ ฌ ๊ธฐ์ค์ ์ก๊ณ ์ ๋ ฌ orderBy ์์ ๋ค์ด์๋ ํ๋๊ฐ ์์ผ๋ฉด ๋ฐํํ์ง ์์
Firestore CRUD
Create
function addTodo(req) {
const id = crypto.randomUUID();
const created_at = new Date().toISOString();
return admin.firestore().collection("todo").add({
id,
detail: req.body.detail,
created_at,
});
}๋ฃ์ผ๋ ค๋ ๋๋ฉ์ธ์ ๋ํด์ add๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ๋ฃ์ด์ค ์ ์๋ค. NOSQL์ธ ๋งํผ ๋ฐ์ดํฐ๋ฅผ ๋ณํ๋์ง ์๋๋ก ์ ๊ฒฝ์จ์ผ ํ ๊ฒ ๊ฐ๋ค.
Read
const citiesRef = db.collection('cities');
const snapshot = await citiesRef.where('capital', '==', true).get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});์๊น ๋ดค๋ ์์์ฒ๋ผ get()์ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๊ฐ๋ณ ๋ฐ์ดํฐ์ ๋ํด์ data()๋ฅผ ํตํด ๋ด๊ฐ ์ํ๋ ๋ฐ์ดํฐ๋ง ๋ฐํ๋ฐ์
Update
async function updateTodo(req) {
try {
console.log(req.body);
const id = req.body.id;
const detailToUpdate = req.body.detail;
const todoToUpdate = await admin
.firestore()
.collection("todo")
.where("id", "==", id)
.limit(1)
.get();
if (todoToUpdate.empty) {
throw new Error("updateํ todo๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.");
}
const todo = new Todo(cardToUpdate.docs[0].data());
todo.update(detailToUpdate);
return cardToUpdate.docs[0].ref.update(todo);
} catch (error) {
throw new Error("์์์น ๋ชปํ Error๊ฐ ๋ฐ์ํ์ต๋๋ค.");
}
}๋ฐ์ดํฐ๋ฅผ ์ฐพ์์์, ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํ ๋ค์ update()๋ฉ์๋๋ฅผ ํตํด ์๋กญ๊ฒ ๊ต์ฒดํ ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ํต์ผ๋ก ๋ฐ๋๋ค
Delete
async function deleteTodo(req) {
try {
const id = req.params.id;
const cardToDelete = await admin
.firestore()
.collection("todo")
.where("id", "==", id)
.limit(1)
.get();
console.log("???", cardToDelete.docs[0]);
if (cardToDelete.empty) {
throw new Error("์ญ์ ํ todo๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.");
}
return cardToDelete.docs[0].ref.delete();
} catch (error) {
throw new Error(error);
}
}์ํ๋ ๋ฐ์ดํฐ์ ๋ํด์ ์ฐพ๊ณ ref property์ delete() ๋ฉ์๋๋ฅผ ํตํด ์ญ์ ํ ์ ์๋ค.