๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Sonamu๋Š” @fastify/compress๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTTP ์‘๋‹ต์„ ์ž๋™์œผ๋กœ ์••์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. gzip, deflate, brotli ๋“ฑ ๋‹ค์–‘ํ•œ ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์›ํ•˜๋ฉฐ, API๋ณ„๋กœ ์••์ถ• ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๊ตฌ์กฐ

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,      // API๋ณ„๋กœ ์ œ์–ด
        threshold: 1024,    // 1KB ์ด์ƒ๋งŒ ์••์ถ•
        encodings: ["gzip", "br"],  // ์ง€์› ์•Œ๊ณ ๋ฆฌ์ฆ˜
      },
    },
  },
  // ...
});

compress ์„ค์ •

ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™”

ํƒ€์ž…: boolean | FastifyCompressOptions
export default defineConfig({
  server: {
    plugins: {
      compress: true,  // ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ ํ™œ์„ฑํ™”
    },
  },
});
๋น„ํ™œ์„ฑํ™”:
export default defineConfig({
  server: {
    plugins: {
      compress: false,  // ์••์ถ• ๋น„ํ™œ์„ฑํ™”
    },
  },
});

์ฃผ์š” ์˜ต์…˜

global

๋ชจ๋“  ์‘๋‹ต์— ์ž๋™์œผ๋กœ ์••์ถ•์„ ์ ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…: boolean ๊ธฐ๋ณธ๊ฐ’: true
export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,  // API๋ณ„๋กœ ์ˆ˜๋™ ์ œ์–ด
      },
    },
  },
});
  • true: ๋ชจ๋“  ์‘๋‹ต์„ ์ž๋™ ์••์ถ• (๊ธฐ๋ณธ๊ฐ’)
  • false: @compress() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ API๋ณ„ ์ œ์–ด
global: false๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ž‘์€ ์‘๋‹ต์ด๋‚˜ ์ด๋ฏธ ์••์ถ•๋œ ํŒŒ์ผ(์ด๋ฏธ์ง€, ๋™์˜์ƒ)์€ ์••์ถ•ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

threshold

์••์ถ•์„ ์‹œ์ž‘ํ•  ์ตœ์†Œ ์‘๋‹ต ํฌ๊ธฐ์ž…๋‹ˆ๋‹ค. ํƒ€์ž…: number (bytes) ๊ธฐ๋ณธ๊ฐ’: 1024 (1KB)
export default defineConfig({
  server: {
    plugins: {
      compress: {
        threshold: 1024,  // 1KB ์ด์ƒ๋งŒ ์••์ถ•
      },
    },
  },
});
๊ถŒ์žฅ๊ฐ’:
  • 1024 (1KB) - ์ผ๋ฐ˜์ ์ธ ์›น ์•ฑ
  • 512 (512B) - ์ž‘์€ JSON ์‘๋‹ต๋„ ์••์ถ•
  • 5120 (5KB) - ํฐ ์‘๋‹ต๋งŒ ์••์ถ•
๋„ˆ๋ฌด ์ž‘์€ ์‘๋‹ต์„ ์••์ถ•ํ•˜๋ฉด ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋” ํด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

encodings

์ง€์›ํ•  ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ํƒ€์ž…: string[] ๊ธฐ๋ณธ๊ฐ’: ["gzip", "deflate"]
export default defineConfig({
  server: {
    plugins: {
      compress: {
        encodings: ["gzip", "br"],  // gzip๊ณผ brotli๋งŒ
      },
    },
  },
});
์ง€์› ์•Œ๊ณ ๋ฆฌ์ฆ˜:
  • "gzip" - ๊ฐ€์žฅ ๋„๋ฆฌ ์ง€์›๋จ, ๋น ๋ฅธ ์••์ถ•
  • "deflate" - gzip๊ณผ ์œ ์‚ฌ
  • "br" (brotli) - ๋†’์€ ์••์ถ•๋ฅ , ๋А๋ฆฐ ์••์ถ•
์šฐ์„ ์ˆœ์œ„: ๋ฐฐ์—ด ์ˆœ์„œ๋Œ€๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง€์›ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ
Brotli๋Š” ์••์ถ•๋ฅ ์ด ๋†’์ง€๋งŒ CPU๋ฅผ ๋” ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ •์  ํŒŒ์ผ์€ ๋ฏธ๋ฆฌ ์••์ถ•ํ•˜๊ณ , ๋™์  ์‘๋‹ต์€ gzip ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์˜ˆ์‹œ

๊ถŒ์žฅ ์„ค์ •

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,      // API๋ณ„๋กœ ์ œ์–ด
        threshold: 1024,    // 1KB ์ด์ƒ๋งŒ
        encodings: ["gzip"], // gzip๋งŒ (๋น ๋ฆ„)
      },
    },
  },
});

Brotli ํฌํ•จ

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,
        threshold: 1024,
        encodings: ["br", "gzip"],  // Brotli ์šฐ์„ , gzip ๋Œ€์ฒด
      },
    },
  },
});

์ž๋™ ์••์ถ• (๋ชจ๋“  ์‘๋‹ต)

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: true,       // ๋ชจ๋“  ์‘๋‹ต ์ž๋™ ์••์ถ•
        threshold: 512,     // 512B ์ด์ƒ
        encodings: ["gzip"],
      },
    },
  },
});

API๋ณ„ ์••์ถ• ์ œ์–ด

global: false๋กœ ์„ค์ •ํ–ˆ๋‹ค๋ฉด @compress() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ API๋ณ„๋กœ ์••์ถ•์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
import { api, compress } from "sonamu";

export class DataModel {
  // ์••์ถ• ์ ์šฉ
  @compress()
  @api()
  static async getLargeData() {
    return {
      // ํฐ ๋ฐ์ดํ„ฐ...
    };
  }
  
  // ์••์ถ• ์•ˆ ํ•จ (์ž‘์€ ์‘๋‹ต)
  @api()
  static async getSmallData() {
    return { status: "ok" };
  }
}
โ†’ Compress ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์‚ฌ์šฉ๋ฒ•

์‹ค์ „ ์˜ˆ์‹œ

ํ‘œ์ค€ ์›น API

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,      // @compress() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์ œ์–ด
        threshold: 1024,    // 1KB ์ด์ƒ๋งŒ
        encodings: ["gzip"],
      },
    },
  },
});
์‚ฌ์šฉ:
export class ArticleModel {
  @compress()  // ํฐ ๋ชฉ๋ก ์‘๋‹ต
  @api()
  static async list() {
    return this.getPuri().select("*");
  }
  
  @compress()  // ํฐ ์ƒ์„ธ ๋ฐ์ดํ„ฐ
  @api()
  static async detail(id: number) {
    return this.findById(id, ["**"]);
  }
  
  @api()  // ์ž‘์€ ์‘๋‹ต์€ ์••์ถ• ์•ˆ ํ•จ
  static async save(data) {
    return { id: 123 };
  }
}

CDN ์ตœ์ ํ™”

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,
        threshold: 2048,    // 2KB ์ด์ƒ๋งŒ (CDN ๊ณ ๋ ค)
        encodings: ["br", "gzip"],  // Brotli ์šฐ์„ 
      },
    },
  },
});

๊ฐœ๋ฐœ vs ํ”„๋กœ๋•์…˜

const isDev = process.env.NODE_ENV === "development";

export default defineConfig({
  server: {
    plugins: {
      compress: isDev
        ? false  // ๊ฐœ๋ฐœ: ์••์ถ• ๋น„ํ™œ์„ฑํ™” (๋””๋ฒ„๊น… ํŽธ์˜)
        : {
            global: false,
            threshold: 1024,
            encodings: ["gzip"],
          },
    },
  },
});

๊ณ ์••์ถ•๋ฅ  ์„ค์ •

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,
        threshold: 512,     // ์ž‘์€ ์‘๋‹ต๋„ ์••์ถ•
        encodings: ["br"],  // Brotli๋งŒ (์ตœ๊ณ  ์••์ถ•๋ฅ )
        brotliOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,  // ์ตœ๋Œ€ ํ’ˆ์งˆ
          },
        },
      },
    },
  },
});
Brotli ํ’ˆ์งˆ์„ ๋†’์ด๋ฉด ์••์ถ•๋ฅ ์€ ์ข‹์•„์ง€์ง€๋งŒ CPU ์‚ฌ์šฉ๋Ÿ‰์ด ํฌ๊ฒŒ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ๋Š” ํ…Œ์ŠคํŠธ ํ›„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ถ”๊ฐ€ ์˜ต์…˜

gzip ๋ ˆ๋ฒจ

import zlib from "zlib";

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: false,
        threshold: 1024,
        encodings: ["gzip"],
        zlibOptions: {
          level: zlib.constants.Z_BEST_COMPRESSION,  // ์ตœ๋Œ€ ์••์ถ•
        },
      },
    },
  },
});
gzip ๋ ˆ๋ฒจ:
  • Z_BEST_SPEED (1) - ๋น ๋ฅธ ์••์ถ•
  • Z_DEFAULT_COMPRESSION (6) - ๊ธฐ๋ณธ๊ฐ’ (๊ถŒ์žฅ)
  • Z_BEST_COMPRESSION (9) - ์ตœ๋Œ€ ์••์ถ•

ํŠน์ • Content-Type๋งŒ ์••์ถ•

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: true,
        threshold: 1024,
        encodings: ["gzip"],
        customTypes: /^text\/|json$/,  // text/* ์™€ json๋งŒ
      },
    },
  },
});

์••์ถ• ์ œ์™ธ

export default defineConfig({
  server: {
    plugins: {
      compress: {
        global: true,
        threshold: 1024,
        encodings: ["gzip"],
        removeContentLengthHeader: false,
        inflateIfDeflated: false,
        onUnsupportedEncoding: (encoding, request, reply) => {
          reply.code(406);  // Not Acceptable
          return "Unsupported encoding";
        },
      },
    },
  },
});

์••์ถ• ํ™•์ธ

์‘๋‹ต์ด ์••์ถ•๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•:

๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ

  1. Network ํƒญ ์—ด๊ธฐ
  2. ์‘๋‹ต ํ—ค๋”์—์„œ ํ™•์ธ:
    Content-Encoding: gzip
    
  3. Size ํ™•์ธ:
    Size: 2.5 KB (transferred)
    Size: 10 KB (uncompressed)
    

curl ํ…Œ์ŠคํŠธ

# gzip ์••์ถ• ์š”์ฒญ
curl -H "Accept-Encoding: gzip" http://localhost:1028/api/data -i

# ์‘๋‹ต ํ—ค๋” ํ™•์ธ
# Content-Encoding: gzip
# Content-Length: 1234

# Brotli ์••์ถ• ์š”์ฒญ
curl -H "Accept-Encoding: br" http://localhost:1028/api/data -i

์„ฑ๋Šฅ ์˜ํ–ฅ

์••์ถ•์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„

์žฅ์ :
  • ๋Œ€์—ญํญ ์ ˆ๊ฐ (50-80% ๊ฐ์†Œ)
  • ๋น ๋ฅธ ์ „์†ก ์‹œ๊ฐ„ (ํŠนํžˆ ๋А๋ฆฐ ๋„คํŠธ์›Œํฌ)
  • CDN ๋น„์šฉ ์ ˆ๊ฐ
๋‹จ์ :
  • CPU ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€
  • ์ฒซ ๋ฐ”์ดํŠธ๊นŒ์ง€ ์‹œ๊ฐ„ ์ฆ๊ฐ€ (TTFB)
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€

๊ถŒ์žฅ ์‚ฌํ•ญ

์••์ถ•ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ:
  • JSON ์‘๋‹ต (API)
  • HTML, CSS, JavaScript
  • SVG, XML
  • ํ…์ŠคํŠธ ํŒŒ์ผ
์••์ถ•ํ•˜์ง€ ๋ง์•„์•ผ ํ•˜๋Š” ๊ฒƒ:
  • ์ด๋ฏธ์ง€ (JPEG, PNG, WebP) - ์ด๋ฏธ ์••์ถ•๋จ
  • ๋™์˜์ƒ (MP4, WebM) - ์ด๋ฏธ ์••์ถ•๋จ
  • ์••์ถ• ํŒŒ์ผ (ZIP, GZ) - ์ด๋ฏธ ์••์ถ•๋จ
  • ๋งค์šฐ ์ž‘์€ ์‘๋‹ต (< 1KB)

์ฃผ์˜์‚ฌํ•ญ

1. ์ด๋ฏธ ์••์ถ•๋œ ํŒŒ์ผ

// โŒ ๋‚˜์œ ์˜ˆ: ์ด๋ฏธ์ง€๋„ ์••์ถ•
compress: {
  global: true,  // ๋ชจ๋“  ๊ฒƒ์„ ์••์ถ•
}

// โœ… ์ข‹์€ ์˜ˆ: API๋ณ„๋กœ ์ œ์–ด
compress: {
  global: false,  // ํ•„์š”ํ•œ ๊ฒƒ๋งŒ @compress()
}

2. CPU ์‚ฌ์šฉ๋Ÿ‰

// โŒ ๋‚˜์œ ์˜ˆ: Brotli ์ตœ๋Œ€ ํ’ˆ์งˆ
compress: {
  encodings: ["br"],
  brotliOptions: {
    params: {
      [zlib.constants.BROTLI_PARAM_QUALITY]: 11,  // ๋„ˆ๋ฌด ๋А๋ฆผ!
    },
  },
}

// โœ… ์ข‹์€ ์˜ˆ: ์ ์ ˆํ•œ ๊ท ํ˜•
compress: {
  encodings: ["gzip"],  // ๋น ๋ฅด๊ณ  ์ถฉ๋ถ„ํ•œ ์••์ถ•๋ฅ 
}

3. threshold ์„ค์ •

// โŒ ๋‚˜์œ ์˜ˆ: ๋„ˆ๋ฌด ๋‚ฎ์€ threshold
threshold: 100  // 100B - ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋” ํด ์ˆ˜ ์žˆ์Œ

// โœ… ์ข‹์€ ์˜ˆ: ์ ์ ˆํ•œ ํฌ๊ธฐ
threshold: 1024  // 1KB

๋‹ค์Œ ๋‹จ๊ณ„

์‘๋‹ต ์••์ถ• ์„ค์ •์„ ์™„๋ฃŒํ–ˆ๋‹ค๋ฉด: