framework/NextJS

๐Ÿ  Next.js App Router Selfโ€‘Hosting ์™„๋ฒฝ ๊ฐ€์ด๋“œ

octo54 2025. 6. 23. 12:06
๋ฐ˜์‘ํ˜•

 

๐Ÿ  Next.js App Router Selfโ€‘Hosting ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Next.js๋Š” Vercel์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์ง€๋งŒ, ์ž์ฒด ์„œ๋ฒ„(Node.js, Docker, CDN ๋“ฑ)์—์„œ๋„ ๊ฐ•๋ ฅํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” App Router ๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์Šค์Šค๋กœ ํ˜ธ์ŠคํŒ…ํ•  ๋•Œ ์•Œ์•„์•ผ ํ•  ๋ชจ๋“  ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ›  1. ์‹คํ–‰ ๋ฐฉ์‹ ์„ ํƒ

Node.js ์„œ๋ฒ„ ์‹คํ–‰

npm run build
npm run start
  • next start๋Š” production ๋ชจ๋“œ ์‹คํ–‰
  • ๋ชจ๋“  ๊ธฐ๋Šฅ(SSR, API Route, ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ๋“ฑ) ์‚ฌ์šฉ ๊ฐ€๋Šฅ

Docker ์ปจํ…Œ์ด๋„ˆ

  • next build → ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ›„ ์ปจํ…Œ์ด๋„ˆํ™”
  • Kubernetes, AWS ECS, GCP ๋“ฑ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

์ •์  ์‚ฌ์ดํŠธ ๋‚ด๋ณด๋‚ด๊ธฐ

  • next export ๋ช…๋ น์œผ๋กœ ์ƒ์„ฑ
  • ๋‹จ, API Routes, SSR, ISR์€ ์ž‘๋™ํ•˜์ง€ ์•Š์Œ

๐Ÿ–ผ๏ธ 2. ์ด๋ฏธ์ง€ ์ตœ์ ํ™” (next/image)

  • ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ๊ธฐ๋Šฅ์€ ์„œ๋ฒ„ ๋‚ด sharp์„ ํ†ตํ•ด ์ˆ˜ํ–‰๋จ
  • ์„œ๋ฒ„์— sharp์ด ์—†์œผ๋ฉด ์„ฑ๋Šฅ์ด ์ œํ•œ๋  ์ˆ˜ ์žˆ์Œ
  • ์ปค์Šคํ…€ ๋กœ๋”๋ฅผ ์ง€์ •ํ•ด ์™ธ๋ถ€ ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ์„œ๋น„์Šค ์—ฐ๊ฒฐ๋„ ๊ฐ€๋Šฅ

๐Ÿ”‘ 3. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •

  • .env, .env.production ๋“ฑ์œผ๋กœ ๊ตฌ์„ฑ
  • ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœํ•˜๋ ค๋ฉด NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ ํ•„์ˆ˜
DATABASE_URL=...
NEXT_PUBLIC_API_URL=https://...

โšก 4. ์บ์‹œ ํ•ธ๋“ค๋ง (ISR ๋“ฑ)

๋ฐ˜์‘ํ˜•

Next.js๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ธฐ๋ฐ˜ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ณต์ˆ˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์šด์˜ํ•˜๊ฑฐ๋‚˜ ํด๋Ÿฌ์Šคํ„ฐ ํ™˜๊ฒฝ(Kubernetes ๋“ฑ)์—์„œ ์บ์‹œ ๊ณต์œ ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ปค์Šคํ…€ ์บ์‹œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// next.config.js
module.exports = {
  cacheHandler: require.resolve('./my-cache-handler.js'),
  cacheMaxMemorySize: 0,
};
  • Redis, S3 ๋“ฑ ์™ธ๋ถ€ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ˜๏ธ 5. CDN ๊ตฌ์„ฑ

  • ์ •์  ์ž์‚ฐ (/_next/static/)์€ CDN์„ ํ†ตํ•ด ์„œ๋น™
  • ISR ์‚ฌ์šฉ ์‹œ stale-while-revalidate ์บ์‹œ ์ •์ฑ…์„ ์ง€์›ํ•˜๋Š” CDN ํ•„์š”
  • ์˜ˆ: AWS CloudFront, Fastly, Cloudflare

๐Ÿšฟ 6. ์ŠคํŠธ๋ฆฌ๋ฐ & ํ”„๋ก์‹œ ์„ค์ •

App Router๋Š” ์„œ๋ฒ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ธฐ๋Šฅ์„ ์ ๊ทน ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํ”„๋ก์‹œ(Nginx ๋“ฑ)์—์„œ buffering ๋น„ํ™œ์„ฑํ™”๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

proxy_buffering off;

๐Ÿงฌ 7. ๋นŒ๋“œ ID ๊ด€๋ฆฌ (๋นŒ๋“œ ๊ฐ„ ๋ฒ„์ „ ์ •ํ•ฉ์„ฑ)

์—ฌ๋Ÿฌ ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค ๊ฐ„ ์ž์‚ฐ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด generateBuildId๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// next.config.js
module.exports = {
  generateBuildId: async () => process.env.GIT_COMMIT_SHA || 'default',
};

๐Ÿงน 8. ์ข…๋ฃŒ ์‹œ๊ทธ๋„ ์ฒ˜๋ฆฌ (Graceful Shutdown)

Docker ๋˜๋Š” Kubernetes ํ™˜๊ฒฝ์—์„œ๋Š” SIGTERM, SIGINT ์‹œ๊ทธ๋„์„ ์ˆ˜์‹ ํ•ด ์„œ๋ฒ„๋ฅผ ์ •๋ฆฌ ์ข…๋ฃŒํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค:

NEXT_MANUAL_SIG_HANDLE=true npm run start
process.on('SIGTERM', () => {
  console.log('SIGTERM received, closing...');
  server.close();
});

โœ… ์ „์ฒด ์ฒดํฌ๋ฆฌ์ŠคํŠธ ์š”์•ฝ

ํ•ญ๋ชฉ ์„ค์ • ํ•„์š” ์—ฌ๋ถ€

Node.js ์‹คํ–‰ or Docker ์ง€์› โœ…
์ด๋ฏธ์ง€ ์ตœ์ ํ™” (sharp) โœ…
ํ™˜๊ฒฝ ๋ณ€์ˆ˜ (NEXT_PUBLIC ํฌํ•จ) โœ…
์บ์‹œ ๊ณต์œ  ํ•ธ๋“ค๋Ÿฌ โœ…
CDN ์„ค์ • (ISR ์ง€์›) โœ…
ํ”„๋ก์‹œ ๋ฒ„ํผ๋ง ๋น„ํ™œ์„ฑํ™” โœ…
๋นŒ๋“œ ID ๋™๊ธฐํ™” (generateBuildId) โœ…
์ข…๋ฃŒ ์‹œ๊ทธ๋„ graceful ์ฒ˜๋ฆฌ โœ…

โœจ ๋งˆ๋ฌด๋ฆฌ

Next.js๋ฅผ ์ง์ ‘ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ๊ฒƒ์€ ๋” ๋งŽ์€ ์œ ์—ฐ์„ฑ์„ ์ฃผ์ง€๋งŒ, ์ฑ…์ž„๋„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์บ์‹ฑ ์ „๋žต, ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ, CDN ์ตœ์ ํ™”, ๋กœ๊ทธ/๋ชจ๋‹ˆํ„ฐ๋ง๊นŒ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•  ๊ฒƒ์ด ๋งŽ์ง€๋งŒ, App Router๋Š” ์ด๋ฅผ ์ž˜ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


 

Next.js, App Router, Self Hosting, Docker, Node.js ์„œ๋ฒ„, ์ด๋ฏธ์ง€ ์ตœ์ ํ™”, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •, ์บ์‹œ ํ•ธ๋“ค๋ง, CDN ๊ตฌ์„ฑ, graceful shutdown, build ID ๊ด€๋ฆฌ