** π3-1λ¨κ³: Ajax λ€νΈμν¬ μ±λ₯ μ΅μ ν **
π‘ Ajaxκ° μλ²μ ν΅μ ν λ βλ λΉ λ₯΄κ³ ν¨μ¨μ μΌλ‘β μλνκ² λ§λλ κΈ°μ λ€μ λλ€.
β 1. μ λ€νΈμν¬ μ΅μ νκ° νμν κΉ?
- Ajaxλ βμλ²μ μμ² β μλ΅ λ°κΈ°β μ΄ κ³Όμ μ κ³μ λ°λ³΅ν©λλ€.
- μμ² νλνλλ μ§§μ§λ§, λ°λ³΅λλ©΄ μλκ° μ μ λλ €μ§κ³ , μλ²λ νλ€μ΄μ Έμ.
π§ κ·Έλμ μ°λ¦¬λ λ€μμ λͺ©νλ‘ ν΄μ:
λͺ©ν | μ€λͺ |
---|---|
β± λΉ λ₯΄κ² | λ€νΈμν¬ λΉμ© μ€μ΄κΈ° (μ μ‘ μκ° λ¨μΆ) |
π¦ μκ² | λ°μ΄ν° ν¬κΈ°λ₯Ό μ€μ΄κΈ° (μμΆ) |
π μ κ² | κ°μ μμ²μ λ°λ³΅νμ§ μκΈ° (μΊμ νμ©) |
β 2. HTTP Keep-Alive
π¦ κ°λ
νλμ μ°κ²°λ‘ μ¬λ¬ μμ²μ μ£Όκ³ λ°λ κΈ°μ
π μλ λ°©μ (λΉν¨μ¨)
[λΈλΌμ°μ ] ---μ°κ²°μμ²---> [μλ²]
<---μλ΅--- (μ°κ²° μ’
λ£)
[λΈλΌμ°μ ] ---λ€μ μ°κ²°---> [μλ²]
<---μλ΅--- (μ°κ²° μ’
λ£)
(λ°λ³΅)
π μμ²ν λλ§λ€ μ°κ²°μ μλ‘ λ§λ¬ β λλ¦Ό, λλΉ
β Keep-Alive λ°©μ (ν¨μ¨)
[λΈλΌμ°μ ] ---μ°κ²°μμ² (Keep-Alive)--->
[μλ²] <---μλ΅ (Keep-Alive μ μ§)
β± μ΄ν μμ²λ κ°μ μ°κ²°λ‘ μ²λ¦¬
π§ μ₯μ
- μ°κ²° μ¬μ¬μ© κ°λ₯ β μλ λΉ¨λΌμ§
- CPU/λ©λͺ¨λ¦¬ λΆλ΄ μ€μ΄λ¦
- μλ² λ¦¬μμ€ μ μ½
β 3. Gzip μμΆ
π¦ κ°λ
μλ²μμ μλ΅ λ°μ΄ν°λ₯Ό μμΆν΄μ 보λ΄λ κΈ°μ
μμ
μλ² μλ΅: {"user":"hong", "email":"hong@gmail.com"}
보ν΅: 60λ°μ΄νΈ
Gzip μ¬μ© μ: 20λ°μ΄νΈλ‘ μμΆλ¨ π
π§ μ₯μ
- μ μ‘ ν¬κΈ° κ°μ β λΉ λ₯΄κ² λμ°©
- μ±λ₯ ν₯μ
- μ΄λ―Έμ§/HTML/JS/CSS μ λΆ μμΆ κ°λ₯
π‘ μ¬μ© λ°©λ² (μλ² μ€μ )
# Apache μμ
AddOutputFilterByType DEFLATE text/html text/css application/javascript
# Nginx μμ
gzip on;
gzip_types text/plain text/css application/json;
β 4. HTTP/2 + Server Push
π¦ κ°λ
HTTP/2λ κΈ°μ‘΄ HTTPμ μ κ·Έλ μ΄λ λ²μ (λ λΉ λ₯΄κ³ λ³λ ¬μ )
νΉμ§
κΈ°μ | μ€λͺ |
---|---|
νλμ μ°κ²°λ‘ μ¬λ¬ μμ² κ°λ₯ | κΈ°μ‘΄μ 1κ°μ© β μ΄μ λ λ³λ ¬ μ²λ¦¬ |
Header μμΆ | μμ² ν€λλ μκ² μ μ‘ |
Server Push | ν΄λΌμ΄μΈνΈκ° μμ²νμ§ μμλ, μλ²κ° 미리 μλ°μ€ν¬λ¦½νΈ/μ€νμΌμ νΈμ κ°λ₯ |
π‘ μ€λ¬΄ μ μ© μμ
λΈλΌμ°μ κ° HTML μμ² β μλ²κ° HTML + CSS + JS μ λΆ ν λ²μ μ μ‘
β 첫 νλ©΄ λ‘λ©μ΄ ν¨μ¬ λΉ¨λΌμ§ π
β 5. μΊμ± μ λ΅ (Cache-Control)
π¦ μΊμλ?
λκ°μ λ°μ΄ν°λ₯Ό λ€μ μμ²νμ§ μλλ‘ λ―Έλ¦¬ μ μ₯ν΄λλ κ²
π μμ
β
첫 μμ² β μλ²μμ JSON λ°μ
β
λ λ²μ§Έ μμ² β μ μ₯λ μΊμμμ λ°λ‘ κ°μ Έμ΄ (0.001μ΄)
π§ Cache-Control ν€λ
Cache-Control: max-age=3600
- 1μκ° λμμ λ€μ μμ²νμ§ μκ³ λΈλΌμ°μ κ° μΊμ μ¬μ©
- μμ λ³κ²½ μμλ μλ‘ μμ² κ°λ₯
β 6. ETag & If-Modified-Since
π¦ ETag (Entity Tag)
βμ΄ νμΌμ΄ λ°λμλμ§ μ λ°λμλμ§ νμΈνλ κ³ μ μ½λβ
π λμ μμ
- λΈλΌμ°μ κ° μλ²μ μμ² β μλ²κ° μλ΅ +
ETag
ν¨κ» 보λ - λΈλΌμ°μ λ
ETag
λ₯Ό μ μ₯ν¨ -
λμ€μ λ μμ²ν λ:
If-None-Match: "etag123"
-
μλ²κ° νμΌμ΄ κ·Έλλ‘λΌλ©΄ β μλ΅ μμ΄ 304 Not Modifiedλ§ λ³΄λ
β λ°μ΄ν° μ¬μ μ‘ μμ΄ μΊμ μ¬μ©
π‘ μ₯μ
- λΆνμν λ°μ΄ν° μ μ‘ μμ
- λ°μ΄ν°κ° λ°λμμ λλ§ μλ² μλ΅
- Ajax μΊμ μ λ΅μΌλ‘ λ§€μ° μ μ©
β μ 체 μμ½ μΉ΄λ
κΈ°μ | μ½κ² μ€λͺ | μ±λ₯ ν¨κ³Ό |
---|---|---|
Keep-Alive | μ°κ²°μ λμ§ μκ³ μ¬μ¬μ© | β± μ°κ²° μλ ν₯μ |
Gzip | μλ΅ μμΆ | π¦ μ μ‘λ κ°μ |
HTTP/2 | μμ²μ ν λ²μ λ³λ ¬ μ²λ¦¬ | π λΉ λ₯Έ λ³λ ¬ μ μ‘ |
Server Push | μλ²κ° νμν νμΌμ 미리 μ€ | π λ‘λ© μλ ν₯μ |
μΊμ | κ°μ μμ²μ λ€μ νμ§ μμ | π μμ² μ κ°μ |
ETag | λ³κ²½λμ λλ§ μλ² μλ΅ | π§ μ€λ§νΈ μμ² μ μ΄ |
β λ©΄μ μ§λ¬Έ μμ + ν΄μ€
β Q. Ajax μ±λ₯ μ΅μ νλ₯Ό μν΄ μ¬μ©ν μ μλ λ€νΈμν¬ κ³μΈ΅ κΈ°μ μ?
β A.
Keep-Alive
λ‘ μ°κ²°μ μ¬μ¬μ©νκ³ ,Gzip
μΌλ‘ μλ΅ ν¬κΈ°λ₯Ό μ€μ΄λ©°,HTTP/2
μServer Push
λ‘ μ΄κΈ° 리μμ€ μ μ‘μ λΉ λ₯΄κ² νκ³ ,-
ETag
λ°Cache-Control
λ‘ μ€λ³΅ μμ²μ λ°©μ§ν©λλ€.β μ΄λ₯Ό ν΅ν΄ νΈλν½ μ κ° + μλ΅ μλ κ°μ + μ¬μ©μ κ²½ν ν₯μ
β 3-2λ¨κ³: Ajax μμ² μ΅μ ν (Debounce / Throttle / λ³ν© / μ€λ³΅ μ κ±°)
μ¬μ©μκ° λΉ λ₯΄κ² νμ΄ννκ±°λ μ€ν¬λ‘€ν λλ§λ€ Ajaxλ₯Ό νΈμΆνλ©΄β¦
β μλ²λ ν°μ§κ³ , μ±λ₯μ λ¨μ΄μ§κ³ , κ²°κ³Όλ κΌ¬μ΄κ² λ©λλ€.
κ·Έλμ μ°λ¦¬λ βμ§λ₯μ μΌλ‘ μμ²μ μ€μ΄λ λ°©λ²βμ λ°°μμΌ ν΄μ.
β 1. μ μμ² μ΅μ νκ° νμν κΉ?
π μμ: κ²μμ°½μμ νμ΄νν λλ§λ€ Ajax μμ²μ 보λΈλ€λ©΄?
h
β Ajax νΈμΆhe
β λ νΈμΆhel
β λ νΈμΆ- β¦
π£ λ무 λ§μ μμ² β μλ² ν°μ§
β οΈ μλ΅ μμκ° κΌ¬μ¬μ κ²°κ³Όλ μλͺ» λμ¬ μ μμ
β 2. Debounce (λλ°μ΄μ€)
β βλ§μ§λ§ μ λ ₯ μ΄ν μΌμ μκ° κΈ°λ€λ Έλ€κ° λ± 1λ²λ§ μ€νβ
π μ¬μ΄ μ€λͺ :
μ€νμμ΄ κ²μμ°½μ νμλ₯Ό μΉ λ, μ λ ₯ λλ λκΉμ§ κΈ°λ€λ Έλ€κ° Ajaxλ₯Ό 1λ²λ§ 보λ
π¦ μμ μ½λ
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer); // μ΄μ νμ΄λ¨Έ μ·¨μ
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const searchAjax = debounce(function() {
fetch("/search?q=" + input.value);
}, 500);
- μ λ ₯ ν 0.5μ΄(500ms) λμ μ λ ₯μ΄ μμΌλ©΄ μ€ν
- μ λ ₯ μ€μ μ€ν μ ν¨
β μ¬μ© μμ
- π μ€μκ° κ²μ (κ²μμ΄ μλμμ±)
- π¦ νν°λ§ κΈ°λ₯
- π¬ μ λ ₯ κ°μ§
β 3. Throttle (μ€λ‘ν)
β βμ΅λ μ΄λΉ 1λ²λ§ μ€ν β λ무 μμ£Ό μ€νλμ§ μλλ‘ μ μ΄β
π μ¬μ΄ μ€λͺ
βμ¬μ©μκ° μ무리 λ§μ°μ€λ₯Ό νμ μ΄λ, 1μ΄μ ν λ²λ§ Ajax μμ² λ³΄λ΄κΈ°β
π¦ μμ μ½λ
function throttle(fn, limit) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}
const throttledScroll = throttle(function() {
fetch("/load/more");
}, 1000);
β μ¬μ© μμ
- π 무ν μ€ν¬λ‘€ Ajax νΈμΆ
- π±οΈ resize, scroll μ΄λ²€νΈ μ μ΄
- π· μ€μκ° μΉ΄λ©λΌ μ²λ¦¬
β 4. μμ² λ³ν© (Batching)
μ¬λ¬ μμ²μ λͺ¨μμ ν λ²μ 보λ΄λ κΈ°μ
π μ¬μ΄ μ€λͺ
βμΉκ΅¬ 10λͺ μ΄ μμ΄μ€ν¬λ¦Ό νλμ© μν¬ λ,
π ν μ¬λμ΄ 10κ°λ₯Ό ν λ²μ μ£Όλ¬Ένλ©΄ λΉ λ₯΄κ³ ν¨μ¨μ !β
π¦ μμ
// 3κ°μ μμ²μ 1κ°λ‘ λ¬ΆκΈ°
fetch("/batch", {
method: "POST",
body: JSON.stringify([
{ type: "search", keyword: "JavaScript" },
{ type: "recommend", userId: "abc123" },
{ type: "popular", category: "tech" }
])
});
β μ₯μ
- μμ² μ μ€μ΄κΈ° β μλ² λΆλ΄ κ°μ
- μλ΅λ μΌκ΄ μ²λ¦¬ β μ±λ₯ κ°μ
β 5. μ€λ³΅ μ κ±° μ²λ¦¬
κ°μ μμ²μ΄ μ¬λ¬ λ² λ°μν κ²½μ°, μ΄λ―Έ λ³΄λΈ κ±΄ 무μνκ±°λ μ€λ³΅μ μ·¨μν¨
π μ¬μ΄ μ€λͺ
βμ΄λ―Έ λ°°λ¬ μ¨ μ§μ₯λ©΄μ΄ μλλ° λ μμΌ°μ΄μ?β β κ·ΈλΌ λ λ²μ§Έ 건 μ·¨μ!
π¦ μμ: μμ² μ·¨μ + μ€λ³΅ λ°©μ§
let controller;
function sendRequest(query) {
if (controller) controller.abort(); // κΈ°μ‘΄ μμ² μ·¨μ
controller = new AbortController();
fetch(`/search?q=${query}`, { signal: controller.signal })
.then(res => res.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === "AbortError") {
console.log("μ€λ³΅ μμ² μ€λ¨λ¨");
}
});
}
β μ€μ UI μ‘°ν© μμ
κΈ°λ₯ | Debounce | Throttle | μ€λ³΅μ κ±° | λ³ν© |
---|---|---|---|---|
κ²μ μλμμ± | β | β | β | β |
무ν μ€ν¬λ‘€ | β | β | β | β |
체ν¬λ°μ€ μ ν ν μμ² | β | β | β | β |
β λ©΄μ μ§λ¬Έ μμ + ν΄μ€
β Q. μ¬μ©μκ° λΉ λ₯΄κ² μ λ ₯ν λ μλ²μ λ§μ μμ²μ΄ λ°μν©λλ€. μ΄λ»κ² μ²λ¦¬νμκ² μ΅λκΉ?
β A.
- μ
λ ₯μ΄ λ©μΆ λ€μλ§ μμ²μ΄ λ°μνλλ‘
debounce
λ₯Ό μ μ©νμ¬ λΆνμν μμ² μ κ±° - μ€λ³΅ μμ²μ΄ λ€μ΄μ€λ κ²½μ°λ₯Ό λλΉν΄ μ΄μ μμ²μ
AbortController
λ‘ μ·¨μ μ²λ¦¬ - λ€μ μμ²μ λ¬Άμ΄ λ³΄λ΄λ
batching
μ ν΅ν΄ μμ² μλ₯Ό μ€μ΄κ³ μλ΅ μΌκ΄ μ²λ¦¬ν¨
β μ 체 μμ½ μΉ΄λ
κΈ°μ | μ€λͺ | μ¬μ© μ |
---|---|---|
Debounce | μ λ ₯μ΄ λ©μΆ λ€ μΌμ μκ° ν 1λ² μ€ν | κ²μμ°½ |
Throttle | μΌμ μκ°λ§λ€ μ΅λ 1λ² μ€ν | μ€ν¬λ‘€ μ΄λ²€νΈ |
Batching | μμ²μ λ¬Άμ΄μ ν λ²μ 보λ | λ©ν° μμ² μ²λ¦¬ |
μ€λ³΅ μ κ±° | λμΌ μμ²μ΄ κ²ΉμΉλ©΄ νλλ§ μ²λ¦¬ | λΉ λ₯Έ ν΄λ¦/μ λ ₯ λ°©μ§ |
β 3-3λ¨κ³: Ajax μλ΅ μ΅μ ν
μ¬μ©μκ° μμ²ν λ°μ΄ν°λ₯Ό μλ²κ° λΉ λ₯΄κ³ κ°λ³κ², λ± νμν λ§νΌλ§ 보λ΄μ€μΌ ν©λλ€.
π μ΄κ² λ°λ‘ βμλ΅ μ΅μ ν(Response Optimization)βμ λλ€!
β 1. Lazy Rendering vs Pre-rendering
π Lazy Rendering (μ§μ° λ λλ§)
β β보μ΄κΈ° μ κΉμ§λ νλ©΄μ κ·Έλ¦¬μ§ λ§μ!β
π¦π» μ¬μ΄ μ€λͺ
μ νλΈμ²λΌ μλλ‘ μ€ν¬λ‘€νκΈ° μ μ μΈλ€μΌμ΄ μ λ¨λ κ²!
β λμ λ³΄μΌ λλ§ κ·Έλ¦¬κΈ° = μ±λ₯ κ΅Ώ!
π§ μ€λ¬΄ μ½λ μμ
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src; // μ΄λ―Έμ§ λ‘λ©
}
});
});
document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));
β λ©λͺ¨λ¦¬ μ€κ³ , λ‘λ© μλ ν₯μ!
π Pre-rendering (μ λ λλ§)
β βνμν κ² κ°μΌλ 미리 νλ©΄μ κ·Έλ €λμ!β
π¦π» μ¬μ΄ μ€λͺ
λ€μ νμ΄μ§ λ΄μ©μ 미리 λ°±κ·ΈλΌμ΄λμμ λΆλ¬μ€κΈ°
β ν΄λ¦νμλ§μ ν λΈ! μλ 체κ°μ μ΅κ³ !
π§ μ€λ¬΄ μμ
// λ€μ νμ΄μ§ 미리 μμ²
fetch("/page/2").then(res => res.text()).then(html => {
sessionStorage.setItem("preloaded", html);
});
β λΉκ΅ μ 리
νλͺ© | Lazy Rendering | Pre-rendering |
---|---|---|
μΈμ 그리λ? | μ¬μ©μκ° λ³Ό λ | 미리 |
μλ μ²΄κ° | μ½κ° λλ¦Ό β μ μ λΉ λ¦ | μ²μλΆν° λΉ λ¦ |
λ©λͺ¨λ¦¬ μ¬μ© | μ μ | λ§μ (미리 λ λλ§ νλκΉ) |
μ¬μ© μ | 무ν μ€ν¬λ‘€, μ΄λ―Έμ§ 리μ€νΈ | λ€μ νμ΄μ§, μΆμ² κΈ°μ¬ λ± |
β 2. μλ΅ λ°μ΄ν° μ΅μν (μκ² λ§λ€κΈ°)
π₯ μ νμνκ°μ?
Ajax μλ΅ λ°μ΄ν°κ° λ무 ν¬λ©΄β¦
- λ€μ΄λ‘λ λλ¦Ό
- λ λλ§λ μ€λ κ±Έλ¦Ό
- λͺ¨λ°μΌμμ λ°μ΄ν° λλΉκΉμ§ π
π¦ μ΅μν μ λ΅ 1: νμν νλλ§ μ£ΌκΈ°
β λμ μμ
{
"id": 1,
"title": "κ²μκΈ μ λͺ©",
"content": "λ΄μ©",
"writer": "κ΄λ¦¬μ",
"writerEmail": "admin@gdu.com",
"writerPhone": "010-0000-0000"
}
β νλ©΄μ μ λͺ©, μμ±μλ§ μ°λλ° 5κ° νλλ₯Ό λ€ μ€ β
β μ’μ μμ
{
"id": 1,
"title": "κ²μκΈ μ λͺ©",
"writer": "κ΄λ¦¬μ"
}
β νμν κ²λ§! β λΉ λ₯΄κ³ κ°λ²Όμ!
π¦ μ΅μν μ λ΅ 2: JSON νλ μ§§κ² λ§λ€κΈ°
{
"i": 1,
"t": "κ²μκΈ μ λͺ©",
"w": "κ΄λ¦¬μ"
}
β β λ¨μΆ ν€μ²λΌ μ¬μ©νλ©΄ μ μ‘ ν¬κΈ° β
- λ¨μ : ν΄λΌμ΄μΈνΈ μ½λμμ λ³λλ‘ λ§€ν νμ
- μ€λ¬΄μμλ GraphQL λ±μΌλ‘ ν΄κ²° κ°λ₯
β 3. Pagination (νμ΄μ§ μ²λ¦¬)
π κ°λ
β βν λ²μ 10κ°λ§ 보μ¬μ£Όμβ
π¦π» μ¬μ΄ μ€λͺ
μΈμ€νκ·Έλ¨μμ ν λ²μ λͺ¨λ μ¬μ§μ λ€ λ³΄μ¬μ£Όμ§ μμ£ ?
β μ€ν¬λ‘€ν λλ§λ€ μ‘°κΈμ© λ‘λ©!
π¦ μμ μμ²
GET /posts?page=1&size=10
- νμ΄μ§ 1λ², 10κ°μ©
- μλ²λ λ± 10κ°λ§ JSONμΌλ‘ 보λ΄μ€
β μ₯μ
νλͺ© | ν¨κ³Ό |
---|---|
μλ΅ μλ | μ€μ΄λ¦ |
λ λλ§ μ±λ₯ | λΉ¨λΌμ§ |
λ°μ΄ν° μ μ‘λ | ν μ€μ΄λ¦ |
β 4. μλ² μ±λ₯μ μν μλ΅ κ΅¬μ‘° μ€κ³ ν
μ λ΅ | μ€λͺ |
---|---|
λ°μ΄ν° νν°λ§ | νλ‘ νΈμμ μ μ°λ 건 μλ²μμ 미리 μ κ±° |
νμ΄μ§λ€μ΄μ | limit , offset λλ cursor λ°©μ |
μμ½ λ°μ΄ν° μ 곡 | μ 체 κΈ μ, μ΄ νμ΄μ§ μ λ±μ ν¨κ» λ°ν |
μ‘°κ±΄λΆ μλ΅ | μ: If-None-Match , 304 Not Modified |
β 5. λ©΄μ μ§λ¬Έ μμ + ν΄μ€
β Q. Ajax μλ΅ μ΅μ νλ₯Ό μν΄ μ΄λ€ μ λ΅μ μ¬μ©νμλμ?
β A.
- νμν νλλ§ ν¬ν¨νκ³ , μλ΅ JSONμ μ΅μνν¨
- ν λ²μ λ무 λ§μ λ°μ΄ν°λ₯Ό μ μ‘νμ§ μλλ‘
pagination
μ²λ¦¬ - Lazy RenderingμΌλ‘ μ΄κΈ° λ λλ§ λΆλ΄μ μ€μ΄κ³ , Pre-renderingμΌλ‘ UXλ₯Ό ν₯μ
- μ‘°κ±΄λΆ μμ²(
ETag
,If-Modified-Since
)μΌλ‘ λ€νΈμν¬ μ κ°
β μ 체 μμ½ μΉ΄λ
μ λ΅ | μ€λͺ | ν¨κ³Ό |
---|---|---|
Lazy Rendering | λ³΄μΌ λλ§ λ λλ§ | λ©λͺ¨λ¦¬ μ μ½, μ±λ₯ ν₯μ |
Pre-rendering | 미리 λ λλ§ | UX ν₯μ, λΉ λ₯Έ μ ν |
JSON νλ μ΅μν | νμν μ λ³΄λ§ μ λ¬ | μλ΅ ν¬κΈ° β |
Pagination | μΌμ κ°μλ§ μλ΅ | μλ β, λΆν β |