Vercel ๋ฐ Netlify๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก ํธ์๋์์ ์๋ฒ๋ฆฌ์ค ํจ์์ ๊ฐ๋ ฅํจ์ ํ์ฉํ์ธ์. ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝ๊ฒ ๊ตฌ์ถ, ๋ฐฐํฌ, ํ์ฅํ์ธ์.
ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์: Vercel ๋ฐ Netlify๋ฅผ ํ์ฉํ ์ค์ฉ ๊ฐ์ด๋
์ค๋๋ ์ญ๋์ ์ธ ์น ๊ฐ๋ฐ ํ๊ฒฝ์์ JAMstack ์ํคํ ์ฒ๋ ์์ฒญ๋ ์ธ๊ธฐ๋ฅผ ์ป์ผ๋ฉฐ ๊ฐ๋ฐ์๋ค์ด ๋ ๋น ๋ฅด๊ณ ์์ ํ๋ฉฐ ํ์ฅ ๊ฐ๋ฅํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋๋ก ์ง์ํ๊ณ ์์ต๋๋ค. JAMstack์ ํต์ฌ ๊ตฌ์ฑ ์์๋ ์๋ฒ๋ฆฌ์ค ํจ์์ ์ฌ์ฉ์ด๋ฉฐ, ์ด๋ฅผ ํตํด ์๋ฒ๋ฅผ ๊ด๋ฆฌํ์ง ์๊ณ ๋ ํ๋ก ํธ์๋์์ ์ง์ ๋ฐฑ์๋ ์ฝ๋๋ฅผ ์คํํ ์ ์์ต๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ๊ฐ๋ฐ์ ๋จ์ํํ๊ณ ์ด์ ์ค๋ฒํค๋๋ฅผ ์ค์ด๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค.
์ด ๊ฐ์ด๋๋ Vercel๊ณผ Netlify๋ผ๋ ๋ ๊ฐ์ง ์ ๋์ ์ธ ํ๋ซํผ์ ์ด์ ์ ๋ง์ถฐ ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์์ ๋ํ ํฌ๊ด์ ์ธ ๊ฐ์๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋ฒ๋ฆฌ์ค ํจ์ ์ฌ์ฉ์ ์ด์ ์ ์ดํด๋ณด๊ณ , Vercel ๋ฐ Netlify๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ์ดํด๋ณด๊ณ , ๊ฐ๋ ฅํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ถ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ ผ์ํ ๊ฒ์ ๋๋ค.
ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ ๋ฌด์์ธ๊ฐ์?
ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์(์๋ฒ๋ฆฌ์ค API ํจ์ ๋๋ ํด๋ผ์ฐ๋ ํจ์๋ผ๊ณ ๋ ํจ)๋ ์๋ฒ๋ฆฌ์ค ํ๊ฒฝ์์ ์คํ๋๋ ๋ ๋ฆฝ์ ์ด๊ณ ๋จ์ผ ๋ชฉ์ ์ ํจ์์ ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก JavaScript ๋๋ ํ๋ซํผ์์ ์ง์ํ๋ ๋ค๋ฅธ ์ธ์ด(์: Python, Go)๋ก ์์ฑ๋๋ฉฐ HTTP ์์ฒญ ๋๋ ๊ธฐํ ์ด๋ฒคํธ์ ์ํด ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค. ๊ธฐ์กด ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ฌ๋ฆฌ ์๋ฒ๋ฆฌ์ค ํจ์๋ ์์ฒญ์ ๋ฐ๋ผ ๊ณต๊ธ์๊ฐ ์๋์ผ๋ก ํ์ฅ๋๋ฏ๋ก ์ต์ ์ ์ฑ๋ฅ๊ณผ ๋น์ฉ ํจ์จ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
์์ ๊ท๋ชจ์ ๋ ๋ฆฝ์ ์ธ ๋ฐฑ์๋ ๋ก์ง ๋จ์๋ก ์๊ฐํ๊ณ ์ด๋ฅผ ์ฃ์ง์ ์ง์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์์ ์ ์ถ: ์ ์ฉ ๋ฐฑ์๋ ์๋ฒ ์์ด ์ฐ๋ฝ์ฒ ์์ ๋๋ ๊ฐ์ ์์์ ์ฒ๋ฆฌํฉ๋๋ค.
- ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ: ์ธ๋ถ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ํ๋ก ํธ์๋์ ์ ๊ณตํฉ๋๋ค.
- ์ธ์ฆ: ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ด๋ฏธ์ง ์ฒ๋ฆฌ: ์ด๋ฏธ์ง๋ฅผ ์ฆ์์์ ํฌ๊ธฐ ์กฐ์ ํ๊ฑฐ๋ ์ต์ ํํฉ๋๋ค.
- ์๋ฒ ์ธก ๋ ๋๋ง(SSR): SEO ๋ฐ ์ฑ๋ฅ ํฅ์์ ์ํด ์ฝํ ์ธ ๋ฅผ ๋์ ์ผ๋ก ๋ ๋๋งํฉ๋๋ค.
- A/B ํ ์คํธ: A/B ํ ์คํธ ์คํ์ ๊ตฌํํฉ๋๋ค.
- ๊ฐ์ธํ: ๊ฐ๋ณ ์ ํธ๋์ ๋ฐ๋ผ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง์ถค ์ค์ ํฉ๋๋ค.
์๋ฒ๋ฆฌ์ค ํจ์ ์ฌ์ฉ์ ์ด์
ํ๋ก ํธ์๋ ๊ฐ๋ฐ ์ํฌํ๋ก์ฐ์ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ์ฑํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ์ด์ ์ด ์์ต๋๋ค.
- ๊ฐ์ํ๋ ๊ฐ๋ฐ: ์๋ฒ ๊ด๋ฆฌ, ์ธํ๋ผ ํ๋ก๋น์ ๋ ๋๋ ํ์ฅ ๊ฑฑ์ ์์ด ์ฝ๋ ์์ฑ์ ์ง์คํฉ๋๋ค.
- ์ด์ ์ค๋ฒํค๋ ๊ฐ์: ์๋ฒ๋ฆฌ์ค ํ๋ซํผ์ด ๋ชจ๋ ์ด์ ์ธก๋ฉด์ ์ฒ๋ฆฌํ๋ฏ๋ก ๊ธฐ๋ฅ ๊ตฌ์ถ์ ์ง์คํ ์ ์์ต๋๋ค.
- ํฅ์๋ ํ์ฅ์ฑ: ์๋ฒ๋ฆฌ์ค ํจ์๋ ์์ฒญ์ ๋ฐ๋ผ ์๋์ผ๋ก ํ์ฅ๋์ด ์ต๋ ํธ๋ํฝ ์ค์๋ ์ต์ ์ ์ฑ๋ฅ์ ๋ณด์ฅํฉ๋๋ค.
- ๋น์ฉ ํจ์จ์ฑ: ํจ์ ์คํ ์ค์ ์๋น๋ ๋ฆฌ์์ค์ ๋ํด์๋ง ๋น์ฉ์ ์ง๋ถํ๋ฏ๋ก ๋ง์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฉ ํจ์จ์ ์ธ ์๋ฃจ์ ์ ๋๋ค.
- ํฅ์๋ ๋ณด์: ์๋ฒ๋ฆฌ์ค ํ๋ซํผ์ ๋ด์ฅ๋ ๋ณด์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ๋ณด์ ํจ์น๋ฅผ ์๋์ผ๋ก ์ ์ฉํ์ฌ ์ทจ์ฝ์ฑ ์ํ์ ์ค์ ๋๋ค.
- ๋ ๋น ๋ฅธ ๋ฐฐํฌ: ์๋ฒ๋ฆฌ์ค ํจ์๋ ๋น ๋ฅด๊ณ ์ฝ๊ฒ ๋ฐฐํฌํ ์ ์์ผ๋ฏ๋ก ๋ฐ๋ณต ์ฃผ๊ธฐ๋ฅผ ๋จ์ถํ ์ ์์ต๋๋ค.
Vercel ๋ฐ Netlify: ์ ๋์ ์ธ ์๋ฒ๋ฆฌ์ค ํ๋ซํผ
Vercel๊ณผ Netlify๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ํ์ฉํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํฌํจํ์ฌ ์ต์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ๊ณ ํธ์คํ ํ๋ ๋ฐ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋ ํ๋ซํผ์ ๋๋ค. ๋ ํ๋ซํผ ๋ชจ๋ ์ํํ ๊ฐ๋ฐ์ ๊ฒฝํ, ์๋ ๋ฐฐํฌ ๋ฐ ๋ด์ฅ CDN ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
Vercel
Vercel(์ด์ Zeit)์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ฅผ ์ํด ํน๋ณํ ์ค๊ณ๋ ํด๋ผ์ฐ๋ ํ๋ซํผ์ ๋๋ค. ์๋, ๋จ์์ฑ ๋ฐ ํ์ ์ ๊ฐ์กฐํฉ๋๋ค. Vercel์ React, Vue.js ๋ฐ Angular์ ๊ฐ์ ์ธ๊ธฐ ์๋ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ์ ์ํํ๊ฒ ํตํฉ๋๋ฉฐ ๋ฎ์ ์ง์ฐ ์๊ฐ์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ๊ณตํ๊ธฐ ์ํ ๊ธ๋ก๋ฒ ์ฃ์ง ๋คํธ์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
Netlify
Netlify๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ณ ๋ฐฐํฌํ๋ ๋ ๋ค๋ฅธ ์ ๋์ ์ธ ํ๋ซํผ์ ๋๋ค. ์ฐ์ ๋ฐฐํฌ, ์๋ฒ๋ฆฌ์ค ํจ์ ๋ฐ ์ฃ์ง ์ปดํจํ ์ ํฌํจํ ํฌ๊ด์ ์ธ ๊ธฐ๋ฅ ์ ํ๊ตฐ์ ์ ๊ณตํฉ๋๋ค. Netlify์ ์ฌ์ฉ์ ์นํ์ ์ธ ์ธํฐํ์ด์ค์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ธํธ๋ ๋ชจ๋ ๊ธฐ์ ์์ค์ ๊ฐ๋ฐ์๋ค์๊ฒ ์ธ๊ธฐ ์๋ ์ ํ์ด ๋๊ณ ์์ต๋๋ค.
Vercel์ ์ฌ์ฉํ ์๋ฒ๋ฆฌ์ค ํจ์ ๊ตฌํ
Vercel๋ก ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋ค๋ ค๋ฉด ์ผ๋ฐ์ ์ผ๋ก ํ๋ก์ ํธ์ `api` ๋๋ ํฐ๋ฆฌ์ ํ์ผ์ ๋ง๋ญ๋๋ค. Vercel์ ์ด๋ฌํ ํ์ผ์ ์๋ฒ๋ฆฌ์ค ํจ์๋ก ์๋ ์ธ์ํ๊ณ ๊ทธ์ ๋ฐ๋ผ ๋ฐฐํฌํฉ๋๋ค. ํ์ผ์ `req`(์์ฒญ ๊ฐ์ฒด)์ `res`(์๋ต ๊ฐ์ฒด)์ ๋ ๊ฐ์ง ์ธ์๋ฅผ ๋ฐ๋ ํจ์๋ฅผ ๋ด๋ณด๋ด์ผ ํฉ๋๋ค.
์์ : ๊ฐ๋จํ "Hello World" ํจ์
๋ค์ ๋ด์ฉ์ผ๋ก `api/hello.js`๋ผ๋ ํ์ผ์ ๋ง๋ญ๋๋ค.
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, world!' });
}
ํ๋ก์ ํธ๋ฅผ Vercel์ ๋ฐฐํฌํฉ๋๋ค. ๋ฐฐํฌ ํ `/api/hello` ์๋ํฌ์ธํธ(์: `https://your-project-name.vercel.app/api/hello`)์์ ์ด ํจ์์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
์์ : ์์ ์ ์ถ ์ฒ๋ฆฌ
์์ ์ ์ถ์ ์ฒ๋ฆฌํ๋ ํจ์๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. ์น์ฌ์ดํธ์ ์ฐ๋ฝ์ฒ ์์์ด ์์ด ์ด ํจ์๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
๋ค์ ๋ด์ฉ์ผ๋ก `api/contact.js`๋ผ๋ ํ์ผ์ ๋ง๋ญ๋๋ค.
export default async function handler(req, res) {
if (req.method === 'POST') {
const { name, email, message } = req.body;
// TODO: ์ด๋ฉ์ผ ๋ณด๋ด๊ธฐ ๋๋ ๋ฐ์ดํฐ ์ ์ฅ ๋ก์ง์ ์ฌ๊ธฐ์ ๊ตฌํํ์ธ์.
// SendGrid์ ๊ฐ์ ์ด๋ฉ์ผ ์๋น์ค๋ฅผ ์ฌ์ฉํ๊ฑฐ๋
// ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ ์ ์์ต๋๋ค.
// ์์ฐ ๋ชฉ์ ์ผ๋ก ์ฝ์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๊น
ํฉ๋๋ค.
console.log('Name:', name);
console.log('Email:', email);
console.log('Message:', message);
res.status(200).json({ message: 'Form submitted successfully!' });
} else {
res.status(405).json({ message: 'Method Not Allowed' });
}
}
์ด ์์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์์ฒญ ๋ฉ์๋๊ฐ `POST`์ธ์ง ํ์ธํฉ๋๋ค.
- ์์ฒญ ๋ณธ๋ฌธ(`req.body`)์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํฉ๋๋ค.
- ์ธ๋ถ ์๋น์ค ๋๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํตํฉ์ด ์ด๋ฃจ์ด์ง๋ ๊ณณ์์ ์๊ธฐ์ํค๊ธฐ ์ํด ํ๋ ์ด์คํ๋ ์ฃผ์ `// TODO: Implement your logic here...`๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- ์ํ ์ฝ๋ 200์ผ๋ก ์ฑ๊ณต ์๋ต์ ๋ณด๋ ๋๋ค.
- ์์ฒญ ๋ฉ์๋๊ฐ POST๊ฐ ์๋ ๊ฒฝ์ฐ ์ํ ์ฝ๋ 405(ํ์ฉ๋์ง ์๋ ๋ฉ์๋)๋ก ์ค๋ฅ ์๋ต์ ๋ณด๋ ๋๋ค.
ํจ์์์ ์ค๋ฅ๋ฅผ ์ ์ ํ๊ฒ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. `try...catch` ๋ธ๋ก์ ์ฌ์ฉํ์ฌ ์์ธ๋ฅผ ์ก๊ณ ํด๋ผ์ด์ธํธ์ ์ ์ตํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฐํํ์ธ์.
Netlify๋ฅผ ์ฌ์ฉํ ์๋ฒ๋ฆฌ์ค ํจ์ ๊ตฌํ
Netlify๋ Vercel๊ณผ ์ ์ฌํ ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ์ฌ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋ญ๋๋ค. ํ๋ก์ ํธ์ ๋๋ ํฐ๋ฆฌ(์ผ๋ฐ์ ์ผ๋ก `netlify/functions`๋ผ๊ณ ํจ)๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ ํจ์ ํ์ผ์ ๋ฐฐ์นํฉ๋๋ค. Netlify๋ ์ด๋ฌํ ํ์ผ์ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ์๋ฒ๋ฆฌ์ค ํจ์๋ก ๋ฐฐํฌํฉ๋๋ค.
์์ : ๊ฐ๋จํ "Hello World" ํจ์
`netlify/functions` ๋๋ ํฐ๋ฆฌ์ `netlify/functions/hello.js` ํ์ผ์ ๋ค์ ๋ด์ฉ์ผ๋ก ๋ง๋ญ๋๋ค.
exports.handler = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello, world!' }),
};
};
ํ๋ก์ ํธ๋ฅผ Netlify์ ๋ฐฐํฌํฉ๋๋ค. ๋ฐฐํฌ ํ `/.netlify/functions/hello` ์๋ํฌ์ธํธ(์: `https://your-project-name.netlify.app/.netlify/functions/hello`)์์ ์ด ํจ์์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
์์ : ์์ ์ ์ถ ์ฒ๋ฆฌ
๋ค์ ๋ด์ฉ์ผ๋ก `netlify/functions/contact.js`๋ผ๋ ํ์ผ์ ๋ง๋ญ๋๋ค.
exports.handler = async (event, context) => {
if (event.httpMethod === 'POST') {
try {
const data = JSON.parse(event.body);
const { name, email, message } = data;
// TODO: ์ด๋ฉ์ผ ๋ณด๋ด๊ธฐ ๋๋ ๋ฐ์ดํฐ ์ ์ฅ ๋ก์ง์ ์ฌ๊ธฐ์ ๊ตฌํํ์ธ์.
// SendGrid์ ๊ฐ์ ์ด๋ฉ์ผ ์๋น์ค๋ฅผ ์ฌ์ฉํ๊ฑฐ๋
// ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ ์ ์์ต๋๋ค.
// ์์ฐ ๋ชฉ์ ์ผ๋ก ์ฝ์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๊น
ํฉ๋๋ค.
console.log('Name:', name);
console.log('Email:', email);
console.log('Message:', message);
return {
statusCode: 200,
body: JSON.stringify({ message: 'Form submitted successfully!' }),
};
} catch (error) {
console.error('Error processing form submission:', error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'Failed to submit form. Please try again later.' }),
};
}
} else {
return {
statusCode: 405,
body: JSON.stringify({ message: 'Method Not Allowed' }),
};
}
};
์ด ์์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- `event.httpMethod`๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ๋ฉ์๋๊ฐ `POST`์ธ์ง ํ์ธํฉ๋๋ค.
- `JSON.parse(event.body)`๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ๋ณธ๋ฌธ์ ํ์ฑํฉ๋๋ค.
- ํ์ฑ๋ ๋ณธ๋ฌธ์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํฉ๋๋ค.
- ์ฌ์ฉ์ ์ ์ ๋ก์ง์ ์ํ ํ๋ ์ด์คํ๋ ์ฃผ์ `// TODO: Implement your logic here...`๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- ํ์ฑ ๋๋ ์ฒ๋ฆฌ ์ค์ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด `try...catch` ๋ธ๋ก์ ์ฌ์ฉํฉ๋๋ค.
- `statusCode` ๋ฐ `body`๊ฐ ์๋ ์๋ต ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์์ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก
์๋ฒ๋ฆฌ์ค ํจ์๋ ๋ค์ํ ํ๋ก ํธ์๋ ์์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก์ ๋๋ค.
1. ์์ ์ ์ถ ์ฒ๋ฆฌ
์์ ์์ ์์ ๋ณด์ฌ์ฃผ๋ฏ์ด ์๋ฒ๋ฆฌ์ค ํจ์๋ ์์ ์ ์ถ์ ์ฒ๋ฆฌํ๋ ๋ฐ ์ด์์ ์ ๋๋ค. ์ ์ถ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ด๋ฉ์ผ ์๋น์ค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ ๊ธฐํ API์ ์ฝ๊ฒ ํตํฉํ ์ ์์ต๋๋ค.
2. ์ฌ์ฉ์ ์ธ์ฆ
์๋ฒ๋ฆฌ์ค ํจ์๋ Auth0, Firebase Authentication ๋๋ Netlify Identity์ ๊ฐ์ ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ฌ์ฉ์ ๋ฑ๋ก, ๋ก๊ทธ์ธ ๋ฐ ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ์ ์ฒ๋ฆฌํ๋ ํจ์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
์์ : Auth0์ ํตํฉ (๊ฐ๋ )
์ ํํ ๊ตฌํ์ Auth0 SDK์ ๋ฐ๋ผ ๋ค๋ฅด์ง๋ง ์ผ๋ฐ์ ์ธ ์์ด๋์ด๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ํ๋ก ํธ์๋๋ ๋ก๊ทธ์ธ ์์ฒญ์ ์๋ฒ๋ฆฌ์ค ํจ์๋ก ๋ณด๋ ๋๋ค.
- ์๋ฒ๋ฆฌ์ค ํจ์๋ Auth0 Management API๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์๊ฒฉ ์ฆ๋ช ์ ํ์ธํฉ๋๋ค.
- ์๊ฒฉ ์ฆ๋ช ์ด ์ ํจํ๋ฉด ์๋ฒ๋ฆฌ์ค ํจ์๋ JWT(JSON Web Token)๋ฅผ ์์ฑํ์ฌ ํ๋ก ํธ์๋๋ก ๋ฐํํฉ๋๋ค.
- ํ๋ก ํธ์๋๋ JWT๋ฅผ ์ ์ฅํ๊ณ ํ์ ์์ฒญ์ ์ธ์ฆํ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค.
3. API์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
์๋ฒ๋ฆฌ์ค ํจ์๋ ์ธ๋ถ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ํ๋ก ํธ์๋์ ์ ๊ณตํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด API ํค ๋ฐ ๊ธฐํ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์จ๊ธธ ์ ์์ต๋๋ค.
์์ : ๊ณต๊ฐ API์์ ๋ ์จ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
// ์ด ์์ ๋ OpenWeatherMap API๋ฅผ ์ฌ์ฉํฉ๋๋ค.
const API_KEY = process.env.OPENWEATHERMAP_API_KEY; // API ํค๋ฅผ ํ๊ฒฝ ๋ณ์์ ์ ์ฅํ์ธ์!
exports.handler = async (event, context) => {
const { city } = event.queryStringParameters; // ์ฟผ๋ฆฌ ๋ฌธ์์ด์์ ๋์๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
if (!city) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'Please provide a city.' }),
};
}
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`;
const response = await fetch(url);
const data = await response.json();
if (!response.ok) {
throw new Error(`Failed to fetch weather data: ${response.status} ${response.statusText}`);
}
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (error) {
console.error('Error fetching weather data:', error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'Failed to fetch weather data.' }),
};
}
};
์ค์: API ํค ๋ฐ ๊ธฐํ ๋ฏผ๊ฐํ ์ ๋ณด๋ ์ฝ๋์ ์ง์ ์ ์ฅํ์ง ๋ง๊ณ ํญ์ ํ๊ฒฝ ๋ณ์์ ์ ์ฅํ์ธ์. Vercel๊ณผ Netlify๋ ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
4. ๋์ ์ด๋ฏธ์ง ์์ฑ
์๋ฒ๋ฆฌ์ค ํจ์๋ ์ฌ์ฉ์ ์ ๋ ฅ ๋๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ ๊ฐ์ธํ๋ ๋ฐฐ๋, ์์ ๋ฏธ๋์ด ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ๋๋ ๊ธฐํ ๋์ ์ฝํ ์ธ ๋ฅผ ๋ง๋๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
5. ์๋ฒ ์ธก ๋ ๋๋ง(SSR) ๊ตฌํ
Next.js ๋ฐ Nuxt.js์ ๊ฐ์ ํ๋ ์์ํฌ๋ ๋ด์ฅ SSR ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ํน์ ๋ถ๋ถ์ ๋ํ SSR์ ๊ตฌํํ ์๋ ์์ต๋๋ค. ์ด๋ ์ฝํ ์ธ ๊ฐ ๋ง์ ํ์ด์ง์ ๋ํ SEO ๋ฐ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค.
์๋ฒ๋ฆฌ์ค ํจ์ ๊ตฌ์ถ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
๊ฐ๋ ฅํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๊ตฌ์ถํ๋ ค๋ฉด ๋ค์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๊ณ ๋ คํ์ญ์์ค.
- ํจ์๋ฅผ ์๊ณ ์ง์ค์ ์ผ๋ก ์ ์ง: ๊ฐ ํจ์๋ ํ๋์ฉ, ์ ์ ์๋ ๋ชฉ์ ์ ๊ฐ์ ธ์ผ ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ดํด, ํ ์คํธ ๋ฐ ์ ์ง ๊ด๋ฆฌ๊ฐ ๋ ์ฌ์์ง๋๋ค.
- ๊ตฌ์ฑ์ ์ํด ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ: API ํค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๊ฒฉ ์ฆ๋ช ๋ฐ ๊ธฐํ ๋ฏผ๊ฐํ ์ ๋ณด๋ ํ๊ฒฝ ๋ณ์์ ์ ์ฅํฉ๋๋ค.
- ์ค๋ฅ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌ: `try...catch` ๋ธ๋ก์ ์ฌ์ฉํ์ฌ ์์ธ๋ฅผ ์ก๊ณ ํด๋ผ์ด์ธํธ์ ์ ์ตํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฐํํฉ๋๋ค.
- ํจ์ ์ฑ๋ฅ ์ต์ ํ: ํจ์์ ํฌํจ๋ ์ฝ๋์ ์ข ์์ฑ์ ์์ ์ต์ํํฉ๋๋ค. ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ฐจ๋จํ์ง ์๋๋ก ๋น๋๊ธฐ ์์ ์ ์ฌ์ฉํฉ๋๋ค.
- ๋ก๊น ๋ฐ ๋ชจ๋ํฐ๋ง ๊ตฌํ: ๋ก๊น ๋ฐ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ํจ์ ์ฑ๋ฅ์ ์ถ์ ํ๊ณ ๋ฌธ์ ๋ฅผ ์๋ณํฉ๋๋ค.
- ํจ์ ๋ณด์: ๋ฌด๋จ ์ก์ธ์ค๋ก๋ถํฐ ํจ์๋ฅผ ๋ณดํธํ๊ธฐ ์ํด ์ ์ ํ ๋ณด์ ์กฐ์น๋ฅผ ๊ตฌํํฉ๋๋ค. ์ฌ๊ธฐ์๋ ์ ๋ ฅ ์ ํจ์ฑ ๊ฒ์ฌ, ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๊ฐ ํฌํจ๋ ์ ์์ต๋๋ค.
- ์ฝ๋ ์คํํธ ๊ณ ๋ ค: ํจ์ ์ฑ๋ฅ์ ๋ํ ์ฝ๋ ์คํํธ์ ์ ์ฌ์ ์ํฅ์ ์ธ์งํฉ๋๋ค. ์ฝ๋ ์คํํธ๋ ํจ์๊ฐ ์ฒ์ ํธ์ถ๋๊ฑฐ๋ ๋นํ์ฑ ๊ธฐ๊ฐ ํ์ ํธ์ถ๋ ๋ ๋ฐ์ํฉ๋๋ค. ์ฝ๋ ์คํํธ์ ์ํฅ์ ์ํํ๋ ค๋ฉด ํจ์๋ฅผ ์๊ฒ ์ ์งํ๊ณ ํ๋ก๋น์ ๋๋ ๋์์ฑ(์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ)์ ์ฌ์ฉํ์ญ์์ค.
- ํจ์ ์ฒ ์ ํ ํ ์คํธ: ๋จ์ ํ ์คํธ ๋ฐ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํ์ฌ ํจ์๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋์ง ํ์ธํฉ๋๋ค.
- ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ ์ฌ์ฉ: ๊ฐ๋ ์ฑ๊ณผ ์ ์ง ๊ด๋ฆฌ์ฑ์ ํฅ์์ํค๊ธฐ ์ํด ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ์ ๋ฐ๋ฆ ๋๋ค.
- ํจ์ ๋ฌธ์ํ: ํจ์์ ๋ํ ๋ช ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฌธ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ณด์ ๊ณ ๋ ค ์ฌํญ
์๋ฒ๋ฆฌ์ค ํจ์๋ ๋ค์๊ณผ ๊ฐ์ ์๋ก์ด ๋ณด์ ๊ณ ๋ ค ์ฌํญ์ ๋์ ํฉ๋๋ค.
- ์ ๋ ฅ ์ ํจ์ฑ ๊ฒ์ฌ: ์ฃผ์ ๊ณต๊ฒฉ ๋ฐ ๊ธฐํ ๋ณด์ ์ทจ์ฝ์ฑ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํญ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ํจ์ฑ ๊ฒ์ฌํฉ๋๋ค.
- ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ: ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ๋ฐ ๊ธฐ๋ฅ์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ํํ๊ธฐ ์ํด ์ ์ ํ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํฉ๋๋ค.
- ์ข ์์ฑ ๊ด๋ฆฌ: ์๋ ค์ง ๋ณด์ ์ทจ์ฝ์ฑ์ ํด๊ฒฐํ๊ธฐ ์ํด ์ข ์์ฑ์ ์ต์ ์ํ๋ก ์ ์งํฉ๋๋ค.
- ๋น๋ฐ ๊ด๋ฆฌ: API ํค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๊ฒฉ ์ฆ๋ช ๋ฐ ๊ธฐํ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๋ณดํธํ๊ธฐ ์ํด ์์ ํ ๋น๋ฐ ๊ด๋ฆฌ ๊ดํ์ ์ฌ์ฉํฉ๋๋ค. ์ฝ๋๋ ๊ตฌ์ฑ ํ์ผ์ ๋น๋ฐ์ ์ง์ ์ ์ฅํ์ง ๋ง์ญ์์ค.
- ์ ๊ธฐ์ ์ธ ๋ณด์ ๊ฐ์ฌ: ์ ์ฌ์ ์ทจ์ฝ์ฑ์ ์๋ณํ๊ณ ํด๊ฒฐํ๊ธฐ ์ํด ์ ๊ธฐ์ ์ธ ๋ณด์ ๊ฐ์ฌ๋ฅผ ์ํํฉ๋๋ค.
๊ธ๋ก๋ฒ ๊ณ ๋ ค ์ฌํญ
์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๊ฐ๋ฐํ ๋๋ ๋ค์ ์ฌํญ์ ๊ณ ๋ คํ์ญ์์ค.
- ์๊ฐ๋: ๋ ์ง ๋ฐ ์๊ฐ๊ณผ ๊ด๋ จ๋ ์๊ฐ๋ ๋ณํ์ ์ ์ ํ๊ฒ ์ฒ๋ฆฌํฉ๋๋ค. ์๊ฐ๋ ์ฒ๋ฆฌ๋ฅผ ๋จ์ํํ๊ธฐ ์ํด `moment-timezone` ๋๋ `date-fns-tz`์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ์ง์ญํ: ์ฌ๋ฌ ์ธ์ด์ ๋ฌธํ๋ฅผ ์ง์ํ๊ธฐ ์ํด ์ง์ญํ๋ฅผ ๊ตฌํํฉ๋๋ค. ๋ฒ์ญ์ ๊ด๋ฆฌํ๊ธฐ ์ํด `i18next` ๋๋ `react-intl`๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ํตํ: ๊ธ์ต ๊ฑฐ๋์ ๊ด๋ จ๋ ํตํ ๋ณํ์ ์ ์ ํ๊ฒ ์ฒ๋ฆฌํฉ๋๋ค. ์ต์ ํ์จ์ ์ป์ผ๋ ค๋ฉด Exchange Rates API ๋๋ Open Exchange Rates์ ๊ฐ์ API๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ๋ฐ์ดํฐ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ: ๋ค๋ฅธ ๊ตญ๊ฐ ๋ฐ ์ง์ญ์ ๋ฐ์ดํฐ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ ๊ท์ ์ ์ธ์งํฉ๋๋ค. GDPR(์ผ๋ฐ ๋ฐ์ดํฐ ๋ณดํธ ๊ท์ ) ๋ฐ CCPA(์บ๋ฆฌํฌ๋์ ์๋น์ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ๋ฒ)์ ๊ฐ์ ๊ท์ ์ ์ค์ํฉ๋๋ค.
- ์ฝํ ์ธ ์ ์ก ๋คํธ์ํฌ(CDN): ์ฌ์ฉ์์๊ฒ ๋ ๊ฐ๊น์ด ์๋ฒ์์ ์ฝํ ์ธ ๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด CDN์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ํนํ ์ง๋ฆฌ์ ์ผ๋ก ๋ฉ๋ฆฌ ๋จ์ด์ง ์ฌ์ฉ์์ ์ฑ๋ฅ์ ํฅ์์ํค๊ณ ์ง์ฐ ์๊ฐ์ ์ค์ผ ์ ์์ต๋๋ค. Vercel๊ณผ Netlify๋ ๋ชจ๋ ๋ด์ฅ CDN ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
๊ฒฐ๋ก
ํ๋ก ํธ์๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ ์ต์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. Vercel ๋ฐ Netlify์ ๊ฐ์ ํ๋ซํผ์ ํ์ฉํ๋ฉด ๊ฐ๋ฐ์ ๋จ์ํํ๊ณ ์ด์ ์ค๋ฒํค๋๋ฅผ ์ค์ด๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ด ๊ฐ์ด๋์์ ์ค๋ช ํ ์ด์ , ์ฌ์ฉ ์ฌ๋ก ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ดํดํ๋ฉด ์๋ฒ๋ฆฌ์ค ํจ์์ ์ ์ฒด ์ ์ฌ๋ ฅ์ ํ์ฉํ๊ณ ์ฌ์ฉ์์๊ฒ ํ๋ฅญํ ์น ๊ฒฝํ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
์๋ฒ๋ฆฌ์ค์ ํ์ ๋ฐ์๋ค์ด๊ณ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํ ๋จ๊ณ ๋ฐ์ ์ํค์ธ์!