ํํ ๋ฆฌ์ผ: ํธ๋ฆฌ๊ฑฐ, ํจ์ ๋ฐ ๊ฐ์ ์ฌ์ฉํ์ฌ ์๋ฒ๋ฆฌ์ค GitHub ๊ธฐ์ฌ ์ถ์ ๊ธฐ ๋น๋
์ด ํ์ด์ง์ ๋ด์ฉ
- ์ ์ ์กฐ๊ฑด
- ์ ์ฑ ๋ง๋ค๊ธฐ
- ์ GitHub ๊ธฐ์ฌ์๋ฅผ ํ์ํฉ๋๋ค.
- ์๋ํฌ์ธํธ ๋ง๋ค๊ธฐ
- ์๋ํฌ์ธํธ ํจ์์ ์ธ์ฆ ์ค์ ์ ๋ฐ์ดํธ
- GitHub Webhook์ ์๋ํฌ์ธํธ ์ฐ๊ฒฐ
- GitHub ์ก์ธ์ค ํ ํฐ ๋ฐ๊ธฐ
- ํ ํฐ์ ๊ฐ์ผ๋ก ์ ์ฅ
- GitHub API ํด๋ผ์ด์ธํธ ์ค์น
- ์๋ํฌ์ธํธ ๋ก์ง ์์ฑ
- ์๋ํฌ์ธํธ ํ ์คํธ
- ์ฃผ๊ฐ ์ปค๋ฎค๋ํฐ ๋ณด๊ณ ์ ์์ฑ
- ๋ณด๊ณ ์๋ฅผ ์์ฑํด์ผ ํ๋ ๋ฆฌํฌ์งํ ๋ฆฌ ์ง์
- ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ ํจ์ ๋ง๋ค๊ธฐ
- ๋งค์ฃผ ๋ณด๊ณ ์ ์์ฑ ๋ฐ ์ ์ฅ
- ๋ณด๊ณ ์ ์๋ฆผ ๋ณด๋ด๊ธฐ
- ๋ณด๊ณ ์ Atlas Triggers ํ ์คํธ
- ๋ค์ ๋จ๊ณ
- ๊ณ์ ๊ตฌ์ถํ๊ธฐ
- ๋ฌธ์ ํ์
- ํผ๋๋ฐฑ ๋ณด๋ด๊ธฐ
- ์ปค๋ฎค๋ํฐ ์ฐธ์ฌํ๊ธฐ
์์ ์๋ฃ ์๊ฐ: 30๋ถ
์ด ํํ ๋ฆฌ์ผ์์๋ Atlas App Services๋ฅผ ์ฌ์ฉํ์ฌ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ ๊ธฐ์ฌ๋๋ฅผ ์ถ์ ํ๋ ์๋ฒ๋ฆฌ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น๋ํฉ๋๋ค.
๋น๋ํ๋ App Services App์๋ ๋ค์๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
MongoDB Atlas ์ปฌ๋ ์ ์์ ํ๋ก์ ํธ์ ๊ธฐ์ฌํ ์ฌ์ฉ์๋ฅผ ์ถ์ ํ์ธ์.
์๋ก์ด ๊ธฐ์ฌ์๊ฐ pull ์์ฒญ์ ์ด๊ฑฐ๋ ์ด์๋ฅผ ์ ๊ธฐํ ๋ ํ์ํ๋ ๋๊ธ์ ๋จ๊ธฐ์ธ์.
๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ๊ธฐ์ฌ๋๋ฅผ ์์ฝํ๋ ์ฃผ๊ฐ ๋ณด๊ณ ์๋ฅผ ์์ฑํ์ฌ ๋ณด๋ ๋๋ค.
์ด ํํ ๋ฆฌ์ผ์ ์๋ฃํ๋ฉด ๋ค์ ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์๊ฒ ๋ฉ๋๋ค.
์ฑ์ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ์๋ฒ๋ฆฌ์ค ํจ์ ๋ฅผ ์์ฑํ๊ณ ์คํํฉ๋๋ค.
Realm ํธ๋ฆฌ๊ฑฐ๋ก ์์ฝ๋ ์์ ์ ์๋ํํ๊ณ ๋ณํํ๋ ๋ฐ์ดํฐ์ ๋์ํ์ธ์.
API ํค์ ๊ฐ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ์ ์ฅํ๊ณ ์ก์ธ์คํฉ๋๋ค.
API๋ฅผ ํตํด ์ธ๋ถ ์๋น์ค์ ์ฐ๊ฒฐ
์ ์ ์กฐ๊ฑด
์ด ํํ ๋ฆฌ์ผ์ ์์ํ๋ ค๋ฉด ๋จผ์ ๋ค์์ด ํ์ํฉ๋๋ค.
GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋๋ค. ๋น๋ํ ์ฑ์ ์ด ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ๊ธฐ์ฌ๋ฅผ ์ถ์ ํฉ๋๋ค.์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ๋๋ ๊ด๋ฆฌ์ ์ก์ธ์ค ๊ถํ์ด ์๋ ๊ธฐ์กด ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ธ์.
MongoDB Atlas ํด๋ฌ์คํฐ. ์์ง ํด๋ฌ์คํฐ๊ฐ ์๋ค๋ฉด MongoDB Atlas์ ๋ฑ๋กํ๊ณ ๋ฌด๋ฃ๋ก ํด๋ฌ์คํฐ๋ฅผ ์์ฑํ์ธ์.
๋ํ ์ด ํํ ๋ฆฌ์ผ์์๋ ๋ฐฐํฌ ์ด์ ์ํฌํ๋ก๋ฅผ ์ฌ์ฉํ์ง ์์ต๋๋ค. ๋ฐฐํฌ ์ด์์ ๋จ์ผ ๋จ์๋ก ๋ฐฐํฌํ๊ฑฐ๋ ์ญ์ ํ ์ ์๋ ์ฑ ๋ณ๊ฒฝ ์ฌํญ์ collection์ ๋๋ค. ๋ฐฐํฌ ์ด์์ ๋นํ์ฑํํ์ง ์์ ๊ฒฝ์ฐ ๋ณ๊ฒฝ ์ฌํญ์ ์ด์์ ์ ์ฅํ ๋ค์ ์๋์ผ๋ก ๋ฐฐํฌํด์ผ ํ ์ ์์ต๋๋ค.
์ ์ฑ ๋ง๋ค๊ธฐ
๋จผ์ App Services์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
์ฑ์ ๋ง๋ค๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์๋จ ํ์์์ App Services ์ ํด๋ฆญํฉ๋๋ค.
Create a New App๋ฅผ ํด๋ฆญํฉ๋๋ค.
์ฑ์ ์ด๋ฆ์
github-tracker-tutorial
์ผ๋ก ์ง์ ํฉ๋๋ค.cluster๋ฅผ ์ ํํ์ฌ ์ ์ฑ์ ์ฐ๊ฒฐํฉ๋๋ค.
Create App Service๋ฅผ ํด๋ฆญํฉ๋๋ค.
์ GitHub ๊ธฐ์ฌ์๋ฅผ ํ์ํฉ๋๋ค.
GitHub ๋ฆฌํฌ์งํ ๋ฆฌ ์นํ ์ ์ฌ์ฉํ์ฌ ์ฑ์ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ฐ๊ฒฐํฉ๋๋ค. . ์นํ ์ ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์ ์ปค๋ฐ์ด๋ pull ์์ฒญ๊ณผ ๊ฐ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค ์ฑ์ ์ด๋ฅผ ์๋ฆฝ๋๋ค. ๊ฐ ์ด๋ฒคํธ๋ ์๋ต์ผ๋ก ๋ฌด์์ธ๊ฐ๋ฅผ ์ํํ ์ ์๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ์คํํฉ๋๋ค.
์ด ํํ ๋ฆฌ์ผ์์๋ GitHub REST API๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์ฒซ ๋ฒ์งธ pull ์์ฒญ ๋๋ ์ด์๋ฅผ ์ด ๋๋ง๋ค ๊ธฐ์ฌ์์๊ฒ ํ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ ๋๋ค.
์๋ํฌ์ธํธ ๋ง๋ค๊ธฐ
์นํ ์ ์ฑ์ด ์ ์ดํ๋ URL๋ก ์ด๋ฒคํธ์ ๋ํ ์์ฒญ์ ์ ์กํ๋ ๋ฐฉ์์ผ๋ก ์๋ํฉ๋๋ค. ์นํ ์์ฒญ์ ์์ ํ๊ณ ์ฒ๋ฆฌํ๋ ค๋ฉด ์ฑ์์ ๊ณ ์ ํ URL์ด ์๋ ์ฌ์ฉ์ ์ง์ ์๋ํฌ์ธํธ๋ฅผ ๋ ธ์ถํด์ผ ํฉ๋๋ค.
์๋ํฌ์ธํธ๋ฅผ ์์ฑํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ HTTPS Endpoints ์ ํด๋ฆญํฉ๋๋ค.
Add An Endpoint๋ฅผ ํด๋ฆญํฉ๋๋ค.
์๋ํฌ์ธํธ ๊ฒฝ๋ก์ ์ด๋ฆ์
/greetNewContributors
์ผ๋ก ์ง์ ํฉ๋๋ค.HTTP ๋ฉ์๋๋ POST ๋ก ์ค์ ๋ ์ํ๋ก ๋ก๋๋ค.
๊ถํ ๋ถ์ฌ๋ฅผ ์ํด Require a Secret ๋ฅผ ์ ํํฉ๋๋ค.
์ ์ํฌ๋ฆฟ ์ด๋ฆ์ ์ ๋ ฅํ๊ณ Create ์ ํด๋ฆญํ์ฌ ์ ์ํฌ๋ฆฟ์ ์์ฑํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ํฌ๋ฆฟ ๊ฐ์ผ๋ก
tutorial
์(๋ฅผ) ์ ๋ ฅํฉ๋๋ค. ์ด๋ฅผ ์ํด์๋ ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ด ์์ฒญ URL์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์secret=tutorial
์(๋ฅผ) ํฌํจํด์ผ ํฉ๋๋ค.์๋ํฌ์ธํธ์ ๋ํ ์ Realm ํจ์ ์ ๋ง๋ค๊ณ ์ด๋ฆ์
endpoints/greetNewContributors
์ผ๋ก ์ง์ ํฉ๋๋ค.์ง๊ธ์ ๋ค๋ฅธ ์์ ์ ์ํํ์ง ์๊ณ ์์ ํธ์ถ์๋ง ์๋ตํ๋ ๊ธฐ๋ณธ ํธ๋ค๋ฌ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ค์ ์ฝ๋๋ฅผ ํจ์ ๋ณธ๋ฌธ์ ๋ณต์ฌํฉ๋๋ค.
exports = async function greetNewContributors(request, response) { return response .setStatusCode(200) .setBody("Successfully received a GitHub webhook event") } Save ์ ํด๋ฆญํ๊ณ ์๋ํฌ์ธํธ๋ฅผ ๋ฐฐํฌํฉ๋๋ค.
Operation Type ์๋์์ ์๋ํฌ์ธํธ ์ฝ๋ฐฑ URL์ ๋ณต์ฌํฉ๋๋ค. ๋์ค์ GitHub ์นํ ์ ์ค์ ํ๋ ๋ฐ ํ์ํฉ๋๋ค.
์๋ํฌ์ธํธ ํจ์์ ์ธ์ฆ ์ค์ ์ ๋ฐ์ดํธ
์ด์ ์ฑ์์ ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค์์ผ๋ฏ๋ก GitHub ์นํ ์ด ์๋ฝ๋๋๋ก ์๋ํฌ์ธํธ์ ํจ์์ ๋ํ ์ธ์ฆ ์ค์ ์ ๋ณ๊ฒฝํด์ผ ํฉ๋๋ค.
ํจ์์ ์ธ์ฆ ์ค์ ์ ์ ๋ฐ์ดํธํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Functions ์ ํด๋ฆญํฉ๋๋ค.
์๋ํฌ์ธํธ ํจ์๋ฅผ ์ฐพ์ ์ ํํฉ๋๋ค.
Settings๋ฅผ ํด๋ฆญํฉ๋๋ค.
Authentication ์์ ์ธ์ฆ ๋ฐฉ๋ฒ์ System ๋ก ๋ณ๊ฒฝํฉ๋๋ค.
ํจ์๋ฅผ ๋ฐฐํฌํ๋ ค๋ฉด Save ์ ํด๋ฆญํฉ๋๋ค.
GitHub Webhook์ ์๋ํฌ์ธํธ ์ฐ๊ฒฐ
์๋ํฌ์ธํธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ค๋น๊ฐ ๋๋ฉด GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์๋ํฌ์ธํธ๋ก ์ด๋ฒคํธ๋ฅผ ์ ์กํ๋ ์นํ ์ ์ค์ ํด์ผ ํฉ๋๋ค.
๋ฆฌํฌ์งํ ๋ฆฌ์ ์นํ ์ ๋ง๋ค๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์ธ์:
๋ฆฌํฌ์งํ ๋ฆฌ ์ค์ ์ ์ด๊ณ ์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Webhooks ์(๋ฅผ) ์ ํํฉ๋๋ค.
์ ์นํ Payload URL ์ถ๊ฐ ๊ทธ๋ฆฌ๊ณ ์(๋ฅผ) ๋ฐฉ๊ธ ์์ฑํ ์๋ํฌ์ธํธ์ URL๋ก ์ค์ ํฉ๋๋ค. URL์ ์ฑ์ ๋ฐฐํฌ ๋ชจ๋ธ์ ๋ฐ๋ผ ๋ค์ ์ค ํ๋์ ๊ฐ์ด ํ์๋ฉ๋๋ค.
์นํ ์ฝํ ์ธ ์ ํ์ application/json ์ผ๋ก ์ค์ ํฉ๋๋ค.
์๋ํฌ์ธํธ์ ๋ํด ์ ์ํ ๊ฒ๊ณผ ๋์ผํ ์ํฌ๋ฆฟ ๊ฐ์ธ Secret ์(๋ฅผ) ์ค์ ํฉ๋๋ค(
tutorial
.๊ฐ๋ณ ์ด๋ฒคํธ๋ฅผ ์ ํํ๋๋ก ์ ํํ๊ณ Issues ๋ฐ Pull requests ์ ๋ํ ์ด๋ฒคํธ๋ง ์ ์กํ๋๋ก ์นํ ์ ๊ตฌ์ฑํฉ๋๋ค.
Add webhook ์ ํด๋ฆญํ์ฌ ์ ์นํ ์ ์ ์ฅํฉ๋๋ค.
์นํ
์ด ์๋ํฌ์ธํธ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ํธ์ถํ๋์ง ํ์ธํ๋ ค๋ฉด App Services์ ์ ํ๋ฆฌ์ผ์ด์
๋ก๊ทธ ์์ ์ ํ์ด Endpoint
์ธ ํญ๋ชฉ์ ํ์ธํฉ๋๋ค. ์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Logs ์ ํด๋ฆญํ๋ฉด ํด๋น ํ์ด์ง๋ก ์ด๋ํ ์ ์์ต๋๋ค.
์นํ ์ ์์ฒญ ๋ก๊ทธ ๋ฅผ ํ์ธํ ์๋ Recent Deliveries ์์ต๋๋ค. ์นํ ์ค์ ํ์ด์ง์ ์๋ GitHub์์ ์ฑ๊ณตํ ๊ฐ ์์ฒญ ์์๋ ๋ น์ ํ์ธ ํ์๊ฐ ์์ต๋๋ค.
GitHub ์ก์ธ์ค ํ ํฐ ๋ฐ๊ธฐ
์ด์ ์นํ ์ด GitHub์์ ์๋ํฌ์ธํธ๋ก ์ด๋ฒคํธ๋ฅผ ์ ์กํ๋๋ก ์ค์ ๋์์ต๋๋ค. ํ์ง๋ง GitHub API๋ก ์๋ํฌ์ธํธ์ ์ด๋ฒคํธ์ ์๋ตํ๋ ค๋ฉด ์ก์ธ์ค ํ ํฐ์ด ํ์ํฉ๋๋ค. ์ด ํํ ๋ฆฌ์ผ์์๋ ๊ฐ์ธ ์ก์ธ์ค ํ ํฐ์ ์ฌ์ฉํ์ง๋ง, GitHub ์ฑ์ ์ค์ ํ์ฌ ํด๋น ํ ํฐ์ ๋์ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
๊ฐ์ธ ์ก์ธ์ค ํ ํฐ์ ์์ฑํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
GitHub ์ฌ์ฉ์ ์ค์ (๋ฆฌํฌ์งํ ๋ฆฌ ์ค์ ์ด ์๋)์ ์ด๊ณ ์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Developer settings ์ ์ ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Personal access tokens ์(๋ฅผ) ์ ํํ ๋ค์ Generate new token ์(๋ฅผ) ํด๋ฆญํฉ๋๋ค.
์ค๋ช ์ด ํฌํจ๋ ์ด๋ฆ๊ณผ ํฉ๋ฆฌ์ ์ธ ๋ง๋ฃ ์๊ฐ์ ์ฌ์ฉํ์ฌ ํ ํฐ์ ๊ตฌ์ฑํฉ๋๋ค. ํํ ๋ฆฌ์ผ์ด๋ฏ๋ก 7์ผ ํ์ ํ ํฐ์ ๋ง๋ฃํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
repo
๋ฒ์๋ฅผ ์ ํํฉ๋๋ค.Generate token๋ฅผ ํด๋ฆญํฉ๋๋ค.
ํ ํฐ์ ๋ค์ ์ก์ธ์คํ ์ ์๋ ์์ ํ ๊ณณ์ ๋ณต์ฌํฉ๋๋ค. ์ด ์ ์ดํ์๋ Github์์ ํ ํฐ์ ๋ค์ ํ์ํ์ง ์์ต๋๋ค.
ํ ํฐ์ ๊ฐ์ผ๋ก ์ ์ฅ
์ฑ์ผ๋ก ๋์๊ฐ์ ๋ฐฉ๊ธ ์์ฑํ ๊ฐ์ธ ์ก์ธ์ค ํ ํฐ์ ์ ์ฅํ ์ ๊ฐ์ ์ถ๊ฐํฉ๋๋ค. ํจ์์ ํ ํฐ์ ํ๋์ฝ๋ฉํ์ง ์๊ณ ๋ ์๋ํฌ์ธํธ์์ ๊ฐ์ ์ฐธ๊ณ ํ ์ ์์ต๋๋ค.
๊ฐ์ ์์ฑํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Values ์ ํด๋ฆญํฉ๋๋ค.
Create New Value๋ฅผ ํด๋ฆญํฉ๋๋ค.
๊ฐ์ ์ด๋ฆ์
GitHubAccessToken
์ผ๋ก ์ง์ ํฉ๋๋ค.์ ํ์ Value ์ผ๋ก ์ค์ ๋ ์ํ๋ก ๋ก๋๋ค.
๊ฐ์ธ ์ก์ธ์ค ํ ํฐ์ ๊ฐ์ ์ ๋ ฅ์ ๋ถ์ฌ๋ฃ์ต๋๋ค. ๊ฐ์ ์ ํจํ JSON์ด์ด์ผ ํ๋ฏ๋ก ์ฃผ์์ ๋ฐ์ดํ๋ฅผ ๋ฃ์ด์ผ ํฉ๋๋ค.
Save๋ฅผ ํด๋ฆญํฉ๋๋ค.
GitHub API ํด๋ผ์ด์ธํธ ์ค์น
์๋ํฌ์ธํธ๋ GitHub์ REST API์ ์ํธ ์์ฉํ์ฌ ๋๊ธ์ ๋จ๊น๋๋ค. ๋ด์ฅ๋ ํด๋ผ์ด์ธํธ ๋๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ API์ ์ง์ HTTP ์์ฒญ์ ์์ฑํ๊ณ ๋ณด๋ผ ์ context.http
์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ํํ ๋ฆฌ์ผ์์๋ Octokit ์ด๋ผ๋ GitHub์ ๊ณต์ Node.js ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค. API๋ฅผ ๋ํํฉ๋๋ค. ์ค์น๊ฐ ์๋ฃ๋๋ฉด ์ฑ์ ๋ชจ๋ ํจ์์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
์ฑ์ ์ฅํ ํท ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์ธ์:
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Functions ์ ํด๋ฆญํฉ๋๋ค.
0}Dependencies ํญ์ ์ ํํฉ๋๋ค.
Add Dependency๋ฅผ ํด๋ฆญํฉ๋๋ค.
ํจํค์ง ์ด๋ฆ ์ ๋ ฅ:
@octokit/request
.Add๋ฅผ ํด๋ฆญํฉ๋๋ค.
App Services์์ ํจํค์ง๋ฅผ ์ค์นํ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค. ์ค์น๋ ๋ช ์ด ์์ ์๋ฃ๋์ง๋ง ์ต๋ 1๋ถ ์ ๋ ๊ฑธ๋ฆด ์ ์์ต๋๋ค.
์๋ํฌ์ธํธ ๋ก์ง ์์ฑ
์ด์ ์ก์ธ์ค ํ ํฐ์ด ์๊ณ ์ฅํ ํท์ ์ค์นํ์ผ๋ฏ๋ก ์ด๋ฒคํธ๋ฅผ ์์ ํ ๋ ์ค์ ๋ก ์ด๋ค ์์ ์ ์ํํ๋๋ก ์๋ํฌ์ธํธ ํจ์๋ฅผ ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก, ํจ์๋ ๋ค์์ ์ํํด์ผ ํฉ๋๋ค.
์์ ์นํ ์ด๋ฒคํธ ๊ตฌ๋ฌธ ๋ถ์
MongoDB์ ๊ธฐ์ฌ๋ ๊ธฐ๋ก
GitHub API๋ฅผ ํตํด ๋๊ธ ์ถ๊ฐ
GitHub์ ์ ์ฉํ ์๋ต์ ๋ค์ ๋ณด๋ด๊ธฐ
ํจ์๋ฅผ ์ ๋ฐ์ดํธํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Functions ์ ํด๋ฆญํฉ๋๋ค.
endpoints/greetNewContributors ์(๋ฅผ) ํด๋ฆญํ์ฌ ์๋ํฌ์ธํธ ํจ์ ํธ์ง๊ธฐ๋ฅผ ์ฝ๋๋ค.
๊ธฐ๋ณธ ํจ์๋ฅผ ๋ค์ ์ฝ๋๋ก ๋ฐ๊ฟ๋๋ค.
functions/endpoints/greetNewContributors.jsexports = async function greetNewContributors(request, response) { // Parse the webhook event from the incoming request. const event = JSON.parse(request.body.text()); // Don't do anything unless this is a new issue or pull request if (event.action !== "opened") { return response.setStatusCode(200); } // Get data from the GitHub webhook event. // Based on the webhook configuration the event will be one of the following: // - issues: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#issues // - pull_request: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request const sender = event.sender; const repo = event.repository; const contribution = event.issue || event.pull_request; const contribution_url = event.issue ? event.issue.url : event.pull_request.issue_url; const issue_number = contribution.number; // Record this contribution in the user's contributor document. // If this user hasn't contributed to the repo before, create a document for them. const atlas = context.services.get("mongodb-atlas"); const contributors = atlas.db("community").collection("contributors"); const contributor = await contributors.findOneAndUpdate( // Look up the user by their GitHub login { login: sender.login }, // Add this issue or pull request to their list of contributions { $push: { contributions: { date: new Date(), type: event.issue ? "issue" : "pull_request", url: contribution_url, }, }, }, // If they haven't contributed before, add them to the database { upsert: true, returnNewDocument: true } ); // Send a welcome message to first time contributors on their issue or pull request const isFirstTimeContributor = contributor.contributions.length === 1; if (isFirstTimeContributor) { const octokit = require("@octokit/request"); await octokit.request( "POST /repos/{owner}/{repo}/issues/{issue_number}/comments", { headers: { authorization: `token ${context.values.get("GitHubAccessToken")}`, }, owner: repo.owner.login, repo: repo.name, issue_number: issue_number, body: `Hi there ${sender.login} ๐ Thanks for your first contribution!`, } ); } // Configure the HTTP response sent back to GitHub return response .setStatusCode(200) .setHeader("Content-Type", "application/json") .setBody( isFirstTimeContributor ? `This is ${sender.login}'s first contribution!` : `${sender.login} has contributed before.` ); }; Save ์ ํด๋ฆญํ๊ณ ์๋ํฌ์ธํธ๋ฅผ ๋ฐฐํฌํฉ๋๋ค.
์๋ํฌ์ธํธ ํ ์คํธ
์ด์ ํ์ ๋ฉ์์ง ์๋ํฌ์ธํธ๊ฐ ์์ ํ ์ค์ ๋์ด์ผ ํฉ๋๋ค. ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋์ง ํ ์คํธํ๋ ค๋ฉด ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์ ์ด์ ๋๋ pull ์์ฒญ์ ์ฝ๋๋ค. ์ด ์์ ์ ์ฒ์ ์ํํ ๋๋ ์๋ํฌ์ธํธ๊ฐ ์ค๋ ๋์ ์ ๋๊ธ์ ์ถ๊ฐํ์ง๋ง ์ดํ ์๋์์๋ ํ์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ์ง ์์ต๋๋ค.
GitHub๋ ๋ฆฌํฌ์งํ ๋ฆฌ ์นํ ์์ฒญ์ ๊ธฐ๋กํ๋ฏ๋ก GitHub์์ ๋ก๊ทธ ํญ๋ชฉ์ ํ์ธํ์ฌ ๋ชจ๋ ๊ฒ์ด ์ ๋๋ก ์๋ํ๋์ง ํ์ธํ ์๋ ์์ต๋๋ค. ๊ฐ ์์ฒญ ๋ก๊ทธ์๋ ์๋ํฌ์ธํธ์ ์๋ต ๋ฉ์์ง๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
ํ
ํ
์คํธ๋ฅผ ์ฌ์ค์ ํ๋ ค๋ฉด community.contributions
์์ GitHub ์ฌ์ฉ์ ์ด๋ฆ์ด ํฌํจ๋ ๋ฌธ์๋ฅผ ์ญ์ ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ฑ์ด ์ด์ ์ ๊ธฐ์ฌํ ๋ด์ฉ์ '์๊ณ ' ๋ค์๋ฒ ๊ธฐ์ฌ์์ ํ์ํฉ๋๋ค.
์ฃผ๊ฐ ์ปค๋ฎค๋ํฐ ๋ณด๊ณ ์ ์์ฑ
์ฑ์ด GitHub์ ์ฐ๊ฒฐ๋๊ณ , ๊ธฐ์ฌ์ ๋ํ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ , ์๋ก์ด ๊ธฐ์ฌ์๋ฅผ ํ์ํฉ๋๋ค. ์ด์ ์ด ๊ธฐ๋ฅ์ ํ์ฅํ์ฌ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ๋ณด๊ณ ์๋ฅผ ์๋์ผ๋ก ๋ถ์ํ๊ณ ์์ฑํ๊ฒ ์ต๋๋ค.
๋ณด๊ณ ์๋ฅผ ์์ฑํด์ผ ํ๋ ๋ฆฌํฌ์งํ ๋ฆฌ ์ง์
์ฑ์๋ ๋งค์ฃผ ์ด๋ค ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ์์ฑํด์ผ ํ๋์ง ์ ์ ์๋ ๋ฐฉ๋ฒ์ด ํ์ํฉ๋๋ค. ์ด ํํ ๋ฆฌ์ผ์์๋ ๋ชฉ๋ก์ Value.
๊ฐ์ฒด ๋ฐฐ์ด์ ํฌํจํ๋ GitHubProjects
์ด๋ผ๋ ์ ๊ฐ์ ๋ง๋ญ๋๋ค. ๊ฐ ๊ฐ์ฒด๋ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ owner
๋ฐ repo
์ด๋ฆ์ ์ง์ ํฉ๋๋ค. ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ํญ๋ชฉ์ ํฌํจํด์ผ ํฉ๋๋ค.
[ { "owner": "<GitHub Username>", "repo": "<Repository Name>" } ]
๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ ํจ์ ๋ง๋ค๊ธฐ
๋ณด๊ณ ์๋ ์ผ์ ๊ธฐ๊ฐ ๋์ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๊ธฐ์ฌํ ๋ด์ฉ์ ์์ฝํ ๋ฌธ์์ ๋๋ค. ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ์จ๋๋งจ๋ ๋ณด๊ณ ์๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
generateCommunityReport
์ด๋ผ๋ ์ ํจ์๋ฅผ ๋ง๋ค๊ณ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
exports = async function generateCommunityReport({ owner, repo, startDate }) { // Look up issues and pull requests that had activity const octokit = require("@octokit/request"); const { data: issuesWithActivity } = await octokit.request( "GET /repos/{owner}/{repo}/issues", { headers: { authorization: `token ${context.values.get("GitHubAccessToken")}`, }, owner: owner, repo: repo, since: startDate, } ); // Look up users that contributed to the repo const atlas = context.services.get("mongodb-atlas"); const contributors = atlas.db("community").collection("contributors"); const allContributors = await contributors .find({ contributions: { $elemMatch: { date: { $gt: new Date(startDate) }, owner: owner, repo: repo, }, }, }) .toArray(); // Keep track of users who made their first contribution const newContributors = allContributors.filter((c) => { new Date(c.contributions[0].date) > new Date(startDate); }); // Return a report with the data return { owner, repo, startDate, issuesWithActivity, allContributors, newContributors, }; };
๋งค์ฃผ ๋ณด๊ณ ์ ์์ฑ ๋ฐ ์ ์ฅ
๋ฐฉ๊ธ ์์ฑํ ํจ์๋ ์จ๋๋งจ๋ ๋ฐฉ์์ผ๋ก ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด ์ ์์ ์๋ฌด๊ฒ๋ ํจ์๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉฐ ์์ฑ๋ ๋ณด๊ณ ์๋ ์ด๋์๋ ์ ์ฅ๋์ง ์์ต๋๋ค. ์ค์ ๋ก ์ฌ์ฉํ๊ธฐ ์ํด ๋งค์ฃผ ํ ๋ฒ ํจ์๋ฅผ ํธ์ถํ๊ณ ์์ฑ๋ ๋ณด๊ณ ์๋ฅผ ์ฐ๊ฒฐ๋ ํด๋ฌ์คํฐ์ ์ ์ฅํ๋ ์์ฝ๋ Atlas Trigger๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
ํธ๋ฆฌ๊ฑฐ๋ฅผ ์์ฑํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Triggers ์ ํด๋ฆญํฉ๋๋ค.
Add a Trigger๋ฅผ ํด๋ฆญํฉ๋๋ค.
trigger ์ ํ์ผ๋ก Scheduled ๋ฅผ ์ ํํฉ๋๋ค.
trigger ์ด๋ฆ ์ง์
generateAndSaveCommunityReports
Advanced ์ผ์ ์ ํ ์ ํ
๋ค์ ํฌ๋ก ์ผ์ ์ ์ ๋ ฅํ์ฌ ์ผ์ฃผ์ผ์ ํ ๋ฒ ์์์ผ ์ค์ 5์(UTC)์ ์คํํฉ๋๋ค.
0 5 * * 1 ํธ๋ฆฌ๊ฑฐ์ ๋ํ ์ ํจ์๋ฅผ ๋ง๋ค๊ณ ์ด๋ฆ์
triggers/generateAndSaveCommunityReports
์ผ๋ก ์ง์ ํฉ๋๋ค.Add Dependency ์ ํด๋ฆญํ๊ณ
moment
์ ์ค์นํฉ๋๋ค.๋ค์ ์ฝ๋๋ฅผ ํจ์ ๋ณธ๋ฌธ์ ๋ณต์ฌํฉ๋๋ค.
functions/Atlas Triggers/generateAndSaveCommunityReports.jsexports = async function generateAndSaveCommunityReports() { const projects = context.values.get("GitHubProjects"); const lastMonday = getLastMonday(); // e.g. "2022-02-21T05:00:00.000Z" // Generate a report for every tracked repo const reportsForLastWeek = await Promise.all( // Call the `generateCommunityReport` function for each project projects.map(async (project) => { return context.functions.execute("generateCommunityReport", { owner: project.owner, repo: project.repo, startDate: lastMonday, }); }) ); // Save the generated reports in Atlas const atlas = context.services.get("mongodb-atlas"); const reports = atlas.db("community").collection("reports"); return await reports.insertMany(reportsForLastWeek); }; // Get an ISO string for last Monday at 5AM UTC function getLastMonday() { const moment = require("moment"); return moment(new Date().setUTCHours(5, 0, 0, 0)) .utc() .day(1 - 7) .toISOString(); } Save๋ฅผ ํด๋ฆญํฉ๋๋ค.
์ด ํํ ๋ฆฌ์ผ ์๋ถ๋ถ์ ์ ์๋ํฌ์ธํธ์ ํจ์์ ์ผ์นํ๋๋ก ์ ํจ์์ ์ธ์ฆ ์ค์ ์ ์ ๋ฐ์ดํธํฉ๋๋ค.
๋ณด๊ณ ์ ์๋ฆผ ๋ณด๋ด๊ธฐ
์ด์ ์ฑ์ด ๋งค์ฃผ ์๋์ผ๋ก ๋ณด๊ณ ์๋ฅผ ์์ฑํ๊ณ ์ ์ฅํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ณด๊ณ ์๋ฅผ ๋ณด๋ ์ฌ๋์ด ์๋ค๋ฉด ๋ณด๊ณ ์๋ ๊ทธ๋ค์ง ์ ์ฉํ์ง ์์ต๋๋ค. ์ ๋ณด๊ณ ์๋ฅผ ์์ ํ๊ณ ์ต์ข ์ฌ์ฉ์์๊ฒ ๋ณด๋ผ ์ ์๋ ํ์์ ๋ฉ์์ง๋ฅผ ์์ฑํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋ฆฌ๊ฑฐ๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
๋ฉ์์ง๋ฅผ ์ค์ ํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ผ์ชฝ ํ์ ๋ฉ๋ด์์ Triggers ์ ํด๋ฆญํฉ๋๋ค.
Add a Trigger๋ฅผ ํด๋ฆญํฉ๋๋ค.
trigger ์ ํ์ Database ๋ก ์ค์ ๋ ์ํ๋ก ๋ก๋๋ค.
ํธ๋ฆฌ๊ฑฐ์ ์ด๋ฆ์
sendCommunityReport
๋ก ์ง์ ํฉ๋๋ค.trigger๋ฅผ
community.reports
collection์ ์ถ๊ฐํ๊ณ Insert ์ด๋ฒคํธ๋ฅผ ์์ ๋๊ธฐํฉ๋๋ค.ํธ๋ฆฌ๊ฑฐ ํจ์์ ์ ๋ฌ๋ ๋ณ๊ฒฝ ์ด๋ฒคํธ์ ๊ฐ๊ฐ์ ์ ๋ณด๊ณ ์ ๋ฌธ์๋ฅผ ํฌํจํ๋ ค๋ฉด Full Document๋ฅผ ํ์ฑํํฉ๋๋ค.
ํธ๋ฆฌ๊ฑฐ์ ๋ํ ์ ํจ์๋ฅผ ๋ง๋ค๊ณ ์ด๋ฆ์
triggers/sendCommunityReport
์ผ๋ก ์ง์ ํฉ๋๋ค.๋ค์ ์ฝ๋๋ฅผ ํจ์ ๋ณธ๋ฌธ์ ๋ณต์ฌํฉ๋๋ค.
functions/Atlas Triggers/sendCommunityReport.jsexports = async function sendCommunityReport(changeEvent) { // Pull out the report document from the database change event const report = changeEvent.fullDocument; // Format values from the report to include in the message const projectName = `${report.owner}/${report.repo}`; const moment = require("moment"); const formattedDate = moment(report.startDate).utc().format("MMMM Do, YYYY"); const numIssuesWithActivity = report.issuesWithActivity.length; const numContributors = report.allContributors.length; const numNewContributors = report.newContributors.length; // Create a message string that describes the data in the report const message = [ `# Community contributions to ${projectName} since ${formattedDate}`, `Last week, there was activity on ${numIssuesWithActivity} issues and pull requests.`, `We had ${numContributors} people contribute, including ${numNewContributors} first time contributors.`, ].join("\n"); // For this tutorial we'll just log the message, but you could use a // service to send it as an email or push notification instead. console.log(message); }; Save ์ ํด๋ฆญํ๊ณ trigger๋ฅผ ๋ฐฐํฌํฉ๋๋ค.
๋ณด๊ณ ์ Atlas Triggers ํ ์คํธ
์ฑ์ด ๋งค์ฃผ ๋ณด๊ณ ์๋ฅผ ์๋์ผ๋ก ์์ฑ, ์ ์ฅํ๊ณ ์ ์กํ๋๋ก ์ค์ ๋์ด ์์ต๋๋ค. ๋ชจ๋ ๊ฒ์ด ์ ๋๋ก ์๋ํ๋์ง ํ์ธํ๋ ค๋ฉด ์ด ๋ณด๊ณ ์ ํ๋ฆ์ ์๋์ผ๋ก ์คํํ๋ฉด ๋ฉ๋๋ค.
์์ฝ๋ ํธ๋ฆฌ๊ฑฐ triggers/generateAndSaveCommunityReports
์ ๋ํ ํจ์ ํธ์ง๊ธฐ๋ฅผ ์ฐ ๋ค์ Run ๋ฒํผ์ ํด๋ฆญํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด GitHubProjects
๊ฐ์ ๋์ดํ ๋ชจ๋ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ํ ์จ๋๋งจ๋ ๋ณด๊ณ ์๊ฐ ์์ฑ๋๊ณ ์ ์ฅ๋ฉ๋๋ค.
ํ์ธํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค.
์ ๋ณด๊ณ ์ ๋ฌธ์๋
community.reports
์(๋ฅผ) ํ์ธํ์ธ์.์ฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค trigger ๋ก๊ทธ๋ฅผ ํ์ธํ์ฌ ๊ฐ ๋ณด๊ณ ์์ ๋ํด ํ์์ด ์ง์ ๋ ๋ฉ์์ง๋ฅผ ์ฐพ์ต๋๋ค.
๋ค์ ๋จ๊ณ
์ถํํฉ๋๋ค! ์๋ฒ๋ฆฌ์ค GitHub ๊ธฐ์ฌ ์ถ์ ๊ธฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ค์ ํ๊ณ ์ด ํํ ๋ฆฌ์ผ์ ์๋ฃํ์ต๋๋ค.
๊ณ์ ๊ตฌ์ถํ๊ธฐ
๊ณ์ ๊ฐ๋ฐํ๊ณ ์ถ๋ค๋ฉด ํธ๋์ปค์ ๋ช ๊ฐ์ง ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํด ๋ณผ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
issue_comment ์ ๊ฐ์๋ ๋ง์ ์นํ ์ด๋ฒคํธ ์ ํ์ ์ฒ๋ฆฌํ๋๋ก ์๋ํฌ์ธํธ๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ๋๋ pull_request_review.
GitHub API์ ๋ ๋ง์ ์ ๋ณด๋ฅผ ํฌํจํ๋๋ก ์ฃผ๊ฐ ๋ณด๊ณ ์๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค.
Twilio ์ ๊ฐ์ ์ธ๋ถ ์๋น์ค์ ์ฐ๊ฒฐ ๋๋ SendGrid ๊ธฐ๋กํ๋ ๋์ ์ด๋ฉ์ผ์ด๋ SMS๋ฅผ ํตํด ์ค์ ๋ก ๋ณด๊ณ ์๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค.
๋ฌธ์ ํ์
App Services์๋ ์ฑ์ ๊ฐํํ ์ ์๋ ๋ง์ ์๋น์ค๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ์ด๋ฌํ ์๋น์ค์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด ๋๋จธ์ง ์ค๋ช ์๋ฅผ ํ์ธํ์ธ์.
ํผ๋๋ฐฑ ๋ณด๋ด๊ธฐ
์ฐ๋ฆฌ๋ ํญ์ ๋ฌธ์์ ํํ ๋ฆฌ์ผ์ ๊ฐ์ ํ๊ธฐ ์ํด ๋ ธ๋ ฅํ๊ณ ์์ต๋๋ค. ์ด ํํ ๋ฆฌ์ผ๊ณผ ๊ด๋ จํ์ฌ ์ ์ ์ฌํญ์ด ์๊ฑฐ๋ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ ์ด ํ์ด์ง ํ๋จ์ Give Feedback ๋ฅผ ํด๋ฆญํ์ฌ ํํ ๋ฆฌ์ผ์ ํ๊ฐํ๊ณ ๋๊ธ์ ๋ณด๋ด์ฃผ์ธ์.
์ปค๋ฎค๋ํฐ ์ฐธ์ฌํ๊ธฐ
๊ณต์ MongoDB Community ํฌ๋ผ ์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ฅผ ๋ง๋๊ณ ์ง๋ฌธํ๊ณ ๋ต๋ณํ๊ณ ์ต์ App Services ๊ธฐ๋ฅ ๋ฐ ๋ฆด๋ฆฌ์ค์ ๋ํ ์ต์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ ์ข์ ์ฅ์์ ๋๋ค.