๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Frontend/Next.js

[Next.js] ๊ฒฐ์ œ ์ฝœ๋ฐฑ ํŽ˜์ด์ง€ ์ ์šฉ ์ค‘ serverRuntimeConfig๋ฅผ ๋‹ค๋ค„๋ณด๋ฉฐ..

by YWTechIT 2025. 8. 5.
728x90

๐Ÿ“ ๊ฒฐ์ œ ์ฝœ๋ฐฑ ํŽ˜์ด์ง€ ์ ์šฉ ์ค‘ serverRuntimeConfig๋ฅผ ๋‹ค๋ค„๋ณด๋ฉฐ..

๊ธฐ์กด์— ํ•˜๋‚˜์˜ ์„œ๋น„์Šค์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋˜ ๋นŒ๋ง ๋ชจ๋“ˆ์„ ์ธํ„ฐํŒŒํฌ์™€ ๋†€ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ†ตํ•ฉ ๋นŒ๋ง ๋ชจ๋“ˆ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ–ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ฒฐ์ œ์ˆ˜๋‹จ์„ ์‹ ์šฉ์นด๋“œ๋กœ ์„ ํƒํ•˜๋ฉด, ๊ธฐ์กด์— ์—†๋˜ ์ด๋‹ˆ์‹œ์Šค(Inicis) ์ฝœ๋ฐฑ ํŽ˜์ด์ง€๊ฐ€ ์ƒˆ๋กœ ํ•„์š”ํ•˜๊ฒŒ๋˜์—ˆ๋‹ค. (๊ธฐ์กด์—๋Š” ํ† ์ŠคํŽ˜์ด๋จผ์ธ ๋งŒ ์‚ฌ์šฉํ•˜๊ณ ์žˆ์—ˆ๋‹ค.)

์ด๋‹ˆ์‹œ์Šค์˜ ๊ฒฝ์šฐ, ๊ฒฐ์ œ ์ฝœ๋ฐฑ์ด /api/payments/[id]/[paymentAgency]์™€ ๊ฐ™์€ API ๋ผ์šฐํ„ฐ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰๋œ๋‹ค. ์ด ๊ตฌ์กฐ๋Š” ๊ธฐ์กด ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์™€ ๋‹ฌ๋ฆฌ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ๋งŒ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— serverRuntimeConfig ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์žˆ๋‹ค.


โœ… serverRuntimeConfig๋ž€?

serverRuntimeConfig๋ž€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • ๋ฐฉ๋ฒ•์ด๋‹ค. ๋‹ค๋งŒ ํ˜„์žฌ๋Š” ๋ ˆ๊ฑฐ์‹œ๋กœ ๊ฐ„์ฃผ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, Next.js๋ฅผ standalone ๋ชจ๋“œ๋กœ ๋นŒ๋“œํ•  ๊ฒฝ์šฐ ๋นŒ๋“œ์‹œ์ ์˜ ๊ฐ’์œผ๋กœ ๊ณ ์ •๋œ๋‹ค. ๋˜ํ•œ React server components์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ฒฝ์šฐ์—๋Š” environment variables ์‚ฌ์šฉ์ด ๊ถŒ์žฅ๋œ๋‹ค.

// next.config.js
module.exports = {
  serverRuntimeConfig: {
    // ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ๋งŒ ์‹คํ–‰
    mySecret: 'secret',
    secondSecret: process.env.SECOND_SECRET, // Pass through env variables
  },
  publicRuntimeConfig: {
    // ์„œ๋ฒ„ & ํด๋ผ์ด์–ธํŠธ ๋ชจ๋‘ ์‹คํ–‰ ๊ฐ€๋Šฅ
    staticFolder: '/static',
  },
}

// index.tsx
import getConfig from 'next/config'
import Image from 'next/image'

// Only holds serverRuntimeConfig and publicRuntimeConfig
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
// Will only be available on the server-side
console.log(serverRuntimeConfig.mySecret)
// Will be available on both server-side and client-side
console.log(publicRuntimeConfig.staticFolder)

function MyImage() {
  return (
    <div>
      <Image
        src={`${publicRuntimeConfig.staticFolder}/logo.png`}
        alt="logo"
        layout="fill"
      />
    </div>
  )
}

export MyImage

๐Ÿงฉ ๋ฌธ์ œ ์ƒํ™ฉ: ์„œ๋ฒ„์‚ฌ์ด๋“œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ˆ„๋ฝ

์—ฌํŠผ ๋ฌธ์ œ๊ฐ€ ๋˜์—ˆ๋˜ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€๋ฐ, ๊ณตํ†ต๋ชจ๋“ˆ๋‚ด์—์„œ ๊ฒฐ์ œ์Šน์ธ API๋ฅผ ์š”์ฒญํ•˜๊ธฐ์œ„ํ•œ baseUrl์„ ๋‹ค์Œ์ฒ˜๋Ÿผ ์„ค์ •ํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ํ† ์ŠคํŽ˜์ด๋จผ์ธ (ํด๋ผ์ด์–ธํŠธ์‚ฌ์ด๋“œ)์™€ ์ด๋‹ˆ์‹œ์Šค(์„œ๋ฒ„์‚ฌ์ด๋“œ) ๋ชจ๋‘์—์„œ ๋™์ผํ•˜๊ฒŒ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๊ณ , ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ณ ์žˆ์–ด์„œ ๋ณ„๋‹ค๋ฅธ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •์ด ํ•„์š”์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ DEVํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ ์ค‘ ๊ฒฐ์ œ์Šน์ธ API๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ๋ชปํ•œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ–ˆ๊ณ  datadog์—์„œ ์—๋Ÿฌ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด๋ณด๋‹ˆ URL์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž˜๋ชป ์ƒ์„ฑ๋˜๊ณ  ์žˆ์—ˆ๋‹ค.

// ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ œ๋Œ€๋กœ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ๋˜ ์ฝ”๋“œ
const baseUrl = serverRuntimeConfig?.API_GATEWAY_URL ?? process.env.PAYMENT_PROXY_PATH
const url = `${baseUrl}/externals/v1/payments/...`

TypeError: Failed to parse URL from ${PAYMENT_PROXY_PATH}/tour-billing/externals/v1/payments/...

PAYMENT_PROXY_PATH๋Š” ๋‹จ์ˆœํ•˜๊ฒŒ basePath/api routes์˜€๊ณ , ์ฒ˜์Œ์—” ์ด๊ฒŒ ์™œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์˜๋ฌธ์ด์—ˆ์ง€๋งŒ ๊ฒฐ๊ตญ ์›์ธ์€ ์ด ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์‹คํ–‰๋˜๋ฉด์„œ ํ˜ธ์ŠคํŠธ ์ •๋ณด๊ฐ€ ๋ˆ„๋ฝ๋œ ์ƒํƒœ๋กœ fetch๋ฅผ ์‹œ๋„ํ•œ ๊ฒƒ์ด์—ˆ๋‹ค.


๐Ÿ”ง ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: apiGatewayUrl ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ช…์‹œ

// ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜
export const withPaymentSdkEnv =
(options: WithPaymentSdkEnvOptions) =>
(nextConfig = {}) => {
  const env = {}

  return {
    ...nextConfig,
    env,
    serverRuntimeConfig: { 
      ...env, 
      API_GATEWAY_URL: options.apiGatewayUrl 
    }, // ๋„๋ฉ”์ธ์—์„œ apiGatewayUrl๋ฅผ ๋„ฃ์–ด์ฃผ์ง€์•Š์•„ PAYMENT_PROXY_PATH๋ฅผ ๋ฆฌํ„ดํ•˜์˜€๋‹ค.     
  }
}

ํด๋ผ์ด์–ธํŠธ์—์„œ ํ˜ธ์ถœํ•˜๊ฒŒ๋˜๋ฉด web-gateway๋ฅผ ๊ฑฐ์น˜๊ธฐ๋•Œ๋ฌธ์— ํ˜ธ์ŠคํŠธ ์—†์ด๋„ PAYMENT_PROXY_PATH๋ฅผ ํ†ตํ•ด proxy ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์ด๋‹ˆ์‹œ์Šค ๊ฒฐ์ œ ์ฝœ๋ฐฑ์€ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์‹คํ–‰๋˜๋ฉฐ, web-gateway๋ฅผ ๊ฑฐ์น˜์ง€์•Š๊ธฐ๋•Œ๋ฌธ์— fetch์‹œ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋ˆ„๋ฝ๋œ ๊ฒฝ๋กœ๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์ด์—ˆ๋‹ค.

์ตœ์ข…์ ์œผ๋กœ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— apiGatewayUrl ๊ฐ’์„ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ ,

  • ์ด๋‹ˆ์‹œ์Šค(์„œ๋ฒ„) ๊ฒฐ์ œ ์‹œ → API_GATEWAY_URL ์‚ฌ์šฉ (ํ˜ธ์ŠคํŠธ ํฌํ•จ)
  • ํ† ์ŠคํŽ˜์ด๋จผ์ธ (ํด๋ผ์ด์–ธํŠธ) ๊ฒฐ์ œ ์‹œ → PAYMENT_PROXY_PATH ์‚ฌ์šฉ (path๋งŒ ์‚ฌ์šฉ)

์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค.


โœ… ์ •๋ฆฌํ•˜๋ฉฐ

์ด๋ฒˆ ์ด์Šˆ๋ฅผ ํ†ตํ•ด fetch ํ˜ธ์ถœ ์‹œ ํ˜„์žฌ ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„์‚ฌ์ด๋“œ์ธ์ง€ ํด๋ผ์ด์–ธํŠธ์‚ฌ์ด๋“œ์ธ์ง€ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๋Š” ์Šต๊ด€์˜ ์ค‘์š”์„ฑ์„ ๋‹ค์‹œ๊ธˆ ๊นจ๋‹ฌ์•˜๋‹ค.

ํŠนํžˆ API ํ†ต์‹ ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์€ ์‚ฌ์ด๋“œ์— ๋”ฐ๋ผ ์ „ํ˜€ ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ,ํ™˜๊ฒฝ๋ณ€์ˆ˜(serverRuntimeConfig, process.env, publicRuntimeConfig)๋ณ„ ๊ฐ๊ฐ์˜ ์“ฐ์ž„์ƒˆ๋ฅผ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์ ์ ˆํžˆ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๋А๊ผˆ๋‹ค.

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€