λ€μ΄κ°λ©°
REST API λ₯Ό κΉμ΄ 곡λΆνλ©° RESTful μν€ν μ² μ μ½μ¬νμ λν΄μ μκ²λμλ€. κ·Έ μ€ Cacheable 쑰건μ λν΄μ μ²μ μκ² λμλλ°, μμ²μ λν μλ²μ μλ΅μ λ°μ΄ν°κ° μΊμ κ°λ₯νμ§ λΆκ°λ₯νμ§ λͺ μν΄μΌ νλ 쑰건μ΄λ€. HTTP ν€λ μ€ Cache-Control μ΄λΌλ ν€λλ₯Ό νμ©νλ©΄ λλλ°, λΈλΌμ°μ κ° μΊμλ₯Ό μ μ₯νκ³ νμ©ν μ μλλ‘ λμμ£Όλ νμ€ ν€λμ΄λ€. μ΄κ±Έλ‘ λΈλΌμ°μ κ° μμμ μΊμ±ν μ μλλ‘ μ€μ ν μ μλ€.
λΈλΌμ°μ μΊμμ ETag
μΉ λΈλΌμ°μ μ μΊμλ μ²μ ꡬνν΄ λ³΄κΈ° λλ¬Έμ μ 리ν΄λ³΄μλ€. HTTP ν€λ μ€ Cache-Control μ΄λΌλ ν€λλ₯Ό νμ©νλ©΄ μΊμλ₯Ό μ μ΄ν μ μλ€. κ·Έλ μ§λ§ λ°μ΄ν°λ₯Ό μλͺ» μΊμνκ² λλ©΄ λ°μ΄ν°μ λΆμΌμΉκ° λ°μν μ μμΌλ―λ‘ μΊμ κ²μ¦λ κ°μ΄ μνν΄μΌ νλ€. κ²μ¦μ μν ν€λκ° ETag μ΄λ€.
Cache-Control ν€λ : μΊμμ μ ν¨κΈ°κ°μ λͺ μνλ μλ΅ ν€λ.
ETag ν€λ : νΉμ λ²μ μ 리μμ€λ₯Ό μλ³νλ κ³ μ μλ³μ. μλ²λ λ°μ΄ν°κ° λ³κ²½λ λ λ§λ€ μ ETag λ₯Ό μμ±νλ€.
ETag λ§λλ λ°©λ²
ETag λ μλ΅ λ°μ΄ν°μ λ΄μ©μ΄ λ°λλ©΄ μλ‘μ΄ κ°μ μμ±νλλ‘ ν΄μΌ νλ€. (κ·ΈλμΌ λ°μ΄ν°κ° λ³κ²½λμλμ§ μ¬λΆλ₯Ό μ½κ² νλ¨ν μ μλ€) λ°λΌμ, ETag λ λ€μ μμλ₯Ό κΈ°λ°μΌλ‘ μμ±ν μ μλ€.
μλ΅ λ°μ΄ν° μ체
- μ : products.toString()
λ°μ΄ν°μ λ§μ§λ§ μμ μκ°
- μ : products.getUpdatedAt()
λ°μ΄ν° μ체λ₯Ό ν΄μνν κ°
- μ : MD5, SHA-256 λ± ν΄μ± μκ³ λ¦¬μ¦ μ¬μ©
μ΄ μ€μμ ν΄μκ°μ μ¬μ©νλ κ²μ΄ ETag λ₯Ό μ§§κ³ ν¨μ¨μ μΌλ‘ μ΄μ©ν μ μμ΄ 3λ²μ μ ννλ€. (μ°Έκ³ λ‘β¦ MD5 μ SHA-256 μ μ°¨μ΄λ ν΄λΉ κΈμ μ°Έκ³ νλλ‘ νμβ¦) μΌλ°μ μΌλ‘ MD5 λ³΄λ€ SHA λ°©μμ΄ μλλ λ리μ§λ§ μμ νλ―λ‘, SHA λ₯Ό νμ©νμ¬ ν΄μ±ν΄λ³΄μ !
@GetMapping("/products")
public ResponseEntity<List<Products>> getAllProducts(HttpHeaders headers) {
try {
// 1οΈβ£ μ‘°νν λ°μ΄ν°
List<Products> products = productsService.findAll();
// 2οΈβ£ ETag μμ± (SHA-256 ν΄μ±)
String data = objectMapper.writeValueAsString(products);
String etag = "\"" + generateSHA256ETag(data) + "\"";
// 3οΈβ£ If-None-Match ν€λμ λΉκ΅νμ¬ 304 μλ΅
if (etag.equals(headers.getFirst(HttpHeaders.IF_NONE_MATCH))) {
return ResponseEntity
.status(HttpStatus.NOT_MODIFIED)
.eTag(etag)
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES))
.build();
}
// 4οΈβ£ μλ‘μ΄ λ°μ΄ν°λ₯Ό λ°ν
return ResponseEntity.status(200)
.eTag(etag)
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES))
.body(products);
} catch (JsonProcessingException e) {
e.printStackTrace();
return ResponseEntity.status(500).build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return ResponseEntity.status(500).build();
}
}
μ΄λ κ² νμ λ, λ°μ΄ν°μ λ³κ²½μ΄ μλ€λ©΄ λΈλΌμ°μ λ 304 μλ΅μ λ°ννλ€.
μΉ μΊμμ νκ³ ?
κ·Έλ°λ° μ‘°κΈ κ±°μ¬λ¦¬λ λΆλΆμ΄ μμλλ°, λΈλΌμ°μ μΊμλ₯Ό μ€μ ν΄λλ μλ² μ μ₯μμλ μΊμλ λ°μ΄ν°μ λ³κ²½μ΄ μλμ§ μμλ³΄λ €λ©΄ κ²°κ΅ DB λ₯Ό λ μ‘°νν΄μ ETag λ₯Ό μμ±ν΄ λΉκ΅ν΄μΌ νλ€. DB μ‘°νλ₯Ό μ΅λν μ€μ΄κΈ° μν΄ μΊμλ₯Ό μ¬μ©νλ κ²μΈλ°, μ΄λ κ² λλ©΄ λ루묡 μλκ°? μΆμλ€β¦ κ·Έλμ μΉ μΊμλ₯Ό μ΄μ©ν¨κ³Ό λμμ redis λ₯Ό νμ©ν μλ² μΊμλ κ°μ΄ μ΄μ©νλ©΄ μλ²½ν κ² κ°λ€λ μκ°μ΄ λ€μλ€. λ€μ μκ°μ κ³μ μμ보μ !
π μ°Έκ³ μλ£