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

์™œ ์••์ถ•์ด ํ•„์š”ํ•œ๊ฐ€?

์••์ถ•์€ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ์ค๋‹ˆ๋‹ค.

์••์ถ• ์—†์ด

์••์ถ• ์‚ฌ์šฉ

ํšจ๊ณผ:
  • ์ „์†ก ํฌ๊ธฐ: 100KB โ†’ 10KB (90% ๊ฐ์†Œ)
  • ๋‹ค์šด๋กœ๋“œ ์‹œ๊ฐ„: 1์ดˆ โ†’ 0.1์ดˆ (10๋ฐฐ ๋น ๋ฆ„)
  • ๋Œ€์—ญํญ ์ ˆ์•ฝ

๊ธฐ๋ณธ ์„ค์ •

sonamu.config.ts

import { type SonamuConfig } from "sonamu";

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: true,  // ์••์ถ• ํ™œ์„ฑํ™” (๊ธฐ๋ณธ ์„ค์ •)
    },
  },
};
๊ธฐ๋ณธ ๋™์ž‘:
  • threshold: 1024 (1KB ์ด์ƒ๋งŒ ์••์ถ•)
  • encodings: ["br", "gzip", "deflate"] (์šฐ์„ ์ˆœ์œ„ ์ˆœ)
  • ๋ชจ๋“  API์— ์ž๋™ ์ ์šฉ

์„ค์ • ์˜ต์…˜

๊ฐ„๋‹จํ•œ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™”
plugins: {
  compress: true,   // ํ™œ์„ฑํ™” (๊ธฐ๋ณธ ์„ค์ •)
  compress: false,  // ๋น„ํ™œ์„ฑํ™”
}

์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜

Brotli (br)

๊ฐ€์žฅ ์••์ถ•๋ฅ ์ด ๋†’์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜
plugins: {
  compress: {
    threshold: 1024,
    encodings: ["br"],  // Brotli๋งŒ ์‚ฌ์šฉ
  }
}
ํŠน์ง•:
  • ์••์ถ•๋ฅ : ์ตœ๊ณ  (gzip ๋Œ€๋น„ 15~20% ๋” ์••์ถ•)
  • ์†๋„: ๋А๋ฆผ (CPU ์‚ฌ์šฉ๋Ÿ‰ ๋†’์Œ)
  • ์ง€์›: ๋ชจ๋˜ ๋ธŒ๋ผ์šฐ์ € (IE ๋ฏธ์ง€์›)
์ ํ•ฉํ•œ ๊ฒฝ์šฐ:
  • ์ •์  ์ฝ˜ํ…์ธ  (์‚ฌ์ „ ์••์ถ• ๊ฐ€๋Šฅ)
  • ๋Œ€์—ญํญ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ
  • ์„œ๋ฒ„ CPU์— ์—ฌ์œ ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ

Gzip

๊ฐ€์žฅ ๋ณดํŽธ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜
plugins: {
  compress: {
    threshold: 1024,
    encodings: ["gzip"],  // Gzip๋งŒ ์‚ฌ์šฉ
  }
}
ํŠน์ง•:
  • ์••์ถ•๋ฅ : ์ค‘๊ฐ„
  • ์†๋„: ๋น ๋ฆ„
  • ์ง€์›: ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €
์ ํ•ฉํ•œ ๊ฒฝ์šฐ:
  • ๋™์  API ์‘๋‹ต
  • ์‹ค์‹œ๊ฐ„ ์••์ถ•
  • ํ˜ธํ™˜์„ฑ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ

Deflate

๋ ˆ๊ฑฐ์‹œ ์•Œ๊ณ ๋ฆฌ์ฆ˜
plugins: {
  compress: {
    threshold: 1024,
    encodings: ["deflate"],
  }
}
ํŠน์ง•:
  • ์••์ถ•๋ฅ : gzip๊ณผ ์œ ์‚ฌ
  • ์†๋„: ๋น ๋ฆ„
  • ์ง€์›: ๋Œ€๋ถ€๋ถ„ ๋ธŒ๋ผ์šฐ์ €
์‚ฌ์šฉ ๊ถŒ์žฅ ์‚ฌํ•ญ: gzip์„ ์‚ฌ์šฉํ•˜์„ธ์š” (๋” ์•ˆ์ •์ )

์••์ถ• ์šฐ์„ ์ˆœ์œ„

๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—ฌ๋Ÿฌ ์ธ์ฝ”๋”ฉ์„ ์ง€์›ํ•  ๋•Œ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
plugins: {
  compress: {
    encodings: ["br", "gzip", "deflate"],  // ์šฐ์„ ์ˆœ์œ„: br > gzip > deflate
  }
}
์ž‘๋™ ๋ฐฉ์‹:
๋ธŒ๋ผ์šฐ์ € ์š”์ฒญ: Accept-Encoding: br, gzip, deflate

1. br ์ง€์›? โ†’ br๋กœ ์••์ถ•
2. br ๋ฏธ์ง€์›, gzip ์ง€์›? โ†’ gzip์œผ๋กœ ์••์ถ•
3. gzip ๋ฏธ์ง€์›, deflate ์ง€์›? โ†’ deflate๋กœ ์••์ถ•
4. ๋ชจ๋‘ ๋ฏธ์ง€์›? โ†’ ์••์ถ• ์—†์Œ

Threshold (์ตœ์†Œ ํฌ๊ธฐ)

์ž‘์€ ์‘๋‹ต์€ ์••์ถ•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
plugins: {
  compress: {
    threshold: 1024,  // 1KB ์ด์ƒ๋งŒ ์••์ถ•
  }
}
์ด์œ :
  • ์ž‘์€ ๋ฐ์ดํ„ฐ๋Š” ์••์ถ• ํšจ์œจ์ด ๋‚ฎ์Œ
  • ์••์ถ•/์••์ถ• ํ•ด์ œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋” ํด ์ˆ˜ ์žˆ์Œ
  • HTTP ํ—ค๋” ํฌ๊ธฐ ์ฆ๊ฐ€
๊ถŒ์žฅ๊ฐ’:
  • ๊ธฐ๋ณธ๊ฐ’: 1024 (1KB)
  • ์ ๊ทน์ : 256 (256B)
  • ๋ณด์ˆ˜์ : 4096 (4KB)

์˜ˆ์‹œ

// 100 ๋ฐ”์ดํŠธ ์‘๋‹ต
threshold: 1024  // ์••์ถ• ์•ˆ ํ•จ (๋„ˆ๋ฌด ์ž‘์Œ)

// 2KB ์‘๋‹ต
threshold: 1024  // ์••์ถ• ํ•จ (>= 1KB)

Custom Types

ํŠน์ • Content-Type๋งŒ ์••์ถ•ํ•ฉ๋‹ˆ๋‹ค.
plugins: {
  compress: {
    customTypes: /^text\/|application\/json/,  // ์ •๊ทœ์‹
  }
}
๊ธฐ๋ณธ ๋™์ž‘: ๋Œ€๋ถ€๋ถ„์˜ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์‘๋‹ต ์••์ถ•
  • text/* (text/html, text/css, text/plain)
  • application/json
  • application/javascript
  • application/xml
์ด๋ฏธ์ง€/๋น„๋””์˜ค๋Š” ์ด๋ฏธ ์••์ถ•๋จ:
  • image/png, image/jpeg (์ด๋ฏธ ์••์ถ•๋œ ํ˜•์‹)
  • video/mp4 (์ด๋ฏธ ์••์ถ•๋œ ํ˜•์‹)
  • ์ถ”๊ฐ€ ์••์ถ•ํ•ด๋„ ํšจ๊ณผ ์—†์Œ

ํ•จ์ˆ˜๋กœ ์ œ์–ด

plugins: {
  compress: {
    customTypes: (contentType: string) => {
      // JSON๋งŒ ์••์ถ•
      return contentType === 'application/json';
    }
  }
}

์‹ค์ „ ์„ค์ • ์˜ˆ์ œ

1. ๊ธฐ๋ณธ ์„ค์ • (๊ถŒ์žฅ)

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: true,  // ๊ฐ„๋‹จํ•˜๊ฒŒ ํ™œ์„ฑํ™”
    },
  },
};
์‚ฌ์šฉํ•˜๋Š” ์„ค์ •:
  • threshold: 1024 (1KB)
  • encodings: ["br", "gzip", "deflate"]

2. ๊ณ ์„ฑ๋Šฅ API ์„œ๋ฒ„

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: {
        threshold: 256,  // ์ž‘์€ ์‘๋‹ต๋„ ์••์ถ•
        encodings: ["br", "gzip", "deflate"],  // ๋ชจ๋“  ์•Œ๊ณ ๋ฆฌ์ฆ˜
      }
    },
  },
};
ํŠน์ง•:
  • ์ตœ๋Œ€ ์••์ถ•๋ฅ  (brotli ์šฐ์„ )
  • 256 ๋ฐ”์ดํŠธ ์ด์ƒ ๋ชจ๋‘ ์••์ถ•
  • ๋Œ€์—ญํญ ์ตœ์ ํ™”

3. ๋ ˆ๊ฑฐ์‹œ ๋ธŒ๋ผ์šฐ์ € ์ง€์›

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: {
        threshold: 1024,
        encodings: ["gzip", "deflate"],  // brotli ์ œ์™ธ
      }
    },
  },
};
ํŠน์ง•:
  • IE ๋“ฑ ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ € ์ง€์›
  • gzip๋งŒ ์‚ฌ์šฉ (์•ˆ์ •์ )

4. ์„ ํƒ์  ์••์ถ•

API๋ณ„๋กœ ์••์ถ•์„ ์ œ์–ดํ•˜๊ณ  ์‹ถ์„ ๋•Œ:
export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: {
        global: false,  // ๊ธฐ๋ณธ์ ์œผ๋กœ ์••์ถ• ์•ˆ ํ•จ
        threshold: 1024,
        encodings: ["br", "gzip", "deflate"],
      }
    },
  },
};

// API๋ณ„ ์„ค์ •
@api({
  httpMethod: 'GET',
  compress: true,  // ์ด API๋งŒ ์••์ถ•
})
async getData() {
  return this.findMany({});
}
์šฉ๋„:
  • ๋Œ€๋ถ€๋ถ„ API๋Š” ์••์ถ• ๋ถˆํ•„์š”
  • ํŠน์ • API๋งŒ ์„ ํƒ์ ์œผ๋กœ ์••์ถ•

5. CPU ์ ˆ์•ฝ (๋ณด์ˆ˜์ )

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: {
        threshold: 4096,  // 4KB ์ด์ƒ๋งŒ ์••์ถ•
        encodings: ["gzip"],  // gzip๋งŒ (๋น ๋ฆ„)
      }
    },
  },
};
ํŠน์ง•:
  • CPU ์‚ฌ์šฉ๋Ÿ‰ ์ตœ์†Œํ™”
  • ํฐ ์‘๋‹ต๋งŒ ์••์ถ•
  • ๋น ๋ฅธ ์ฒ˜๋ฆฌ

ํ™˜๊ฒฝ๋ณ„ ์„ค์ •

export const config: SonamuConfig = {
  server: {
    plugins: {
      compress: process.env.NODE_ENV === 'production'
        ? {
            threshold: 256,
            encodings: ["br", "gzip", "deflate"],
          }
        : false,  // ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๋น„ํ™œ์„ฑํ™”
    },
  },
};
๊ฐœ๋ฐœ ํ™˜๊ฒฝ: ์••์ถ• ๋น„ํ™œ์„ฑํ™”
  • ๋””๋ฒ„๊น… ์šฉ์ด
  • ๋น ๋ฅธ ์‘๋‹ต (์••์ถ• ์˜ค๋ฒ„ํ—ค๋“œ ์—†์Œ)
ํ”„๋กœ๋•์…˜: ์ ๊ทน์  ์••์ถ•
  • ๋Œ€์—ญํญ ์ ˆ์•ฝ
  • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ 

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

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

Network ํƒญ โ†’ ์š”์ฒญ ์„ ํƒ โ†’ Headers ํƒญ

Response Headers:
  Content-Encoding: gzip
  Content-Length: 1234 (์••์ถ•๋œ ํฌ๊ธฐ)
  
Size:
  10.5 KB (์••์ถ•๋œ ํฌ๊ธฐ)
  52.3 KB (์›๋ณธ ํฌ๊ธฐ)

curl ๋ช…๋ น

# gzip ์••์ถ• ์š”์ฒญ
curl -H "Accept-Encoding: gzip" http://localhost:3000/api/products -v

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

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

์••์ถ• ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ:
  1. ์ด๋ฏธ ์••์ถ•๋œ ํŒŒ์ผ์€ ์ œ์™ธ: ์ด๋ฏธ์ง€, ๋น„๋””์˜ค๋Š” ์••์ถ• ํšจ๊ณผ ์—†์Œ
    compress: {
      customTypes: /^text\/|application\/json/,  // ํ…์ŠคํŠธ๋งŒ
    }
    
  2. ์ž‘์€ ์‘๋‹ต์€ ์••์ถ•ํ•˜์ง€ ๋ง ๊ฒƒ: threshold ์„ค์ •
    compress: {
      threshold: 1024,  // 1KB ๋ฏธ๋งŒ์€ ์••์ถ• ์•ˆ ํ•จ
    }
    
  3. CPU ๋ถ€ํ•˜ ๊ณ ๋ ค: brotli๋Š” CPU ์‚ฌ์šฉ๋Ÿ‰์ด ๋†’์Œ
    // โŒ ๋‚ฎ์€ ์‚ฌ์–‘ ์„œ๋ฒ„์—์„œ brotli ์‚ฌ์šฉ
    encodings: ["br"]
    
    // โœ… gzip ์‚ฌ์šฉ (๋น ๋ฆ„)
    encodings: ["gzip"]
    
  4. HTTPS์—์„œ๋งŒ brotli ์ง€์›: HTTP์—์„œ๋Š” gzip ์‚ฌ์šฉ
  5. ์••์ถ• ํ•ด์ œ ์˜ค๋ฅ˜ ์ฃผ์˜: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ์••์ถ• ํ•ด์ œํ•˜๋Š”์ง€ ํ™•์ธ

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

์žฅ์ 

  • ๋Œ€์—ญํญ ์ ˆ์•ฝ: 70~90% ํฌ๊ธฐ ๊ฐ์†Œ
  • ๋‹ค์šด๋กœ๋“œ ์†๋„: ํŠนํžˆ ๋А๋ฆฐ ๋„คํŠธ์›Œํฌ์—์„œ ํšจ๊ณผ์ 
  • ๋น„์šฉ ์ ˆ๊ฐ: CDN ์ „์†ก๋Ÿ‰ ๊ฐ์†Œ

๋‹จ์ 

  • CPU ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€: ์••์ถ•/์••์ถ• ํ•ด์ œ ์˜ค๋ฒ„ํ—ค๋“œ
  • ์‘๋‹ต ์ง€์—ฐ: ์••์ถ• ์‹œ๊ฐ„ ์ถ”๊ฐ€ (๋ณดํ†ต ์ˆ˜ ms)
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ์••์ถ• ๋ฒ„ํผ

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

// โœ… ๊ถŒ์žฅ: ํฐ JSON ์‘๋‹ต
@api({ compress: true })
async getLargeData() {
  return this.findMany({ num: 1000 });  // 100KB+
}

// โŒ ๋น„๊ถŒ์žฅ: ์ž‘์€ ์‘๋‹ต
@api({ compress: true })
async getStatus() {
  return { status: "ok" };  // 10 ๋ฐ”์ดํŠธ
}

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