[์ธํ”„๋ผ๊ณต๋ฐฉ] HTTP Cache

2024. 2. 28. 23:33ยท๐Ÿ“‚ education & lecture/์ธํ”„๋ผ ๊ณต๋ฐฉ

1. HTTP Caching

  • ์‘๋‹ต ๊ฐ’์˜ ๋ณต์‚ฌ๋ณธ์„ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ์ด๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ GET ์‘๋‹ต๋งŒ ์บ์‹ฑํ•˜๋ฉฐ, <key, value> ๋ฅผ <URL, response> ๊ตฌ์กฐ ํ˜•ํƒœ๋กœ ๊ฐ€์ง„๋‹ค.
  • ์บ์‹ฑํ•œ ๋ฆฌ์†Œ์Šค์˜ ์œ„์น˜๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ private cache ์— ์กด์žฌํ•  ์ˆ˜๋„ ์žˆ๊ณ , ํ”„๋ก์‹œ ์„œ๋ฒ„(proxy, reverse proxy, CDN) ์˜ Shared Cache ๋กœ ์กด์žฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

2. HTTP Cache ๋™์ž‘ ์›๋ฆฌ

  1. cache hit : ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ, ์บ์‹œ์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ
  2. cache miss : ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ, ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ (ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ์— ์ €์žฅ)
  3. cache revalidation
    1. ์บ์‹œ์˜ ์‚ฌ๋ณธ์ด ์ตœ์‹ ์ธ์ง€ ์„œ๋ฒ„์— ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญ.
    2. ๊ตฌ๋ถ„

cache hit, cache miss, cache revalidate hit

 

 

 

3. HTTP Header : Cache-Control

  1. Cache-Control : Date Header ๋กœ ๋ถ€ํ„ฐ ๊ฒฝ๊ณผ๋œ ์‹œ๊ฐ„๊ณผ ๋น„๊ตํ•˜์—ฌ ๋งŒ๋ฃŒ์—ฌ๋ถ€ ํŒ๋‹จ
    • ์บ์‹œ ๊ฐ’ 
      • Cache-Control: max-age=604800 : Date Header ๋กœ ๋ถ€ํ„ฐ 1์ฃผ์ผ์ด ๊ฒฝ๊ณผํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ๋งŒ๋ฃŒ์—ฌ๋ถ€ ํŒ๋‹จ
      • Cache-Control: no-cache : max-age=0 ๊ณผ ๊ฐ™๋‹ค. ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์ง€๋Š” ์•Š์œผ๋ฉฐ, ํ•ญ์ƒ ์„œ๋ฒ„์— ์ตœ์‹  ์ปจํ…์ธ  ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•œ๋‹ค.
      • Cache-Control: no-store : ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ, ์„œ๋ฒ„ ์‘๋‹ต์— ๊ฐ„ํ•ด ์–ด๋–ค ๊ฒƒ๋„ ์ €์žฅํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ์š”์ฒญ์€ ์„œ๋ฒ„ ์ธก์œผ๋กœ ์ „์†ก๋˜๊ณ  ์ „์ฒด ์‘๋‹ต์€ ๋งค๋ฒˆ ๋‹ค์šด๋กœ๋“œ๋œ๋‹ค.
      • Cache-Control: private : ์š”์ฒญํ•œ ์‚ฌ์šฉ์ž๋งŒ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ (๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ์˜๋ฏธX) 
    • Cache-Control Expires Header ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ํœด๋ฆฌ์Šคํ‹ฑ ์บ์‹ฑ์ด ์ ์šฉ๋˜์–ด ์ž์ฒด์ ์œผ๋กœ ์บ์‹ฑ์„ ์ ์šฉํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•œ๋‹ค.
      • ํœด๋ฆฌ์Šคํ‹ฑ ์บ์‹ฑ
        • ๊ฒฝํ—˜์ ์œผ๋กœ ๋งŒ๋ฃŒ ์ผ์ž๋ฅผ ์ €์žฅํ•˜๋Š” ์บ์‹ฑ ๋ฐฉ๋ฒ•์œผ๋กœ, ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €)๊ฐ€ ์ž„์˜๋กœ ์บ์‹œ๋ฅผ ์กฐ์ ˆํ•œ๋‹ค.
        • ํœด๋ฆฌ์Šคํ‹ฑ ์บ์‹ฑ์€ ์„œ๋ฒ„์—์„œ ํ•ด๋‹น ์บ์‹œ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†์–ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์—์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.
  2. Date : ์‘๋‹ต์ด ์ƒ์„ฑ๋œ ์‹œ๊ฐ„์„ ์˜๋ฏธ
    • Date: Mon, 26 Dec 2022 22:22:22 GMT
  3. Expires : ์‘๋‹ต์ด ๋งŒ๋ฃŒ๋˜๋Š” ์‹œ๊ฐ„์„ ์˜๋ฏธ
    • Expires: Sun, 23 Feb 2025 08:23:51 GMT
    • Cache-Control ํ—ค๋”๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ, Expires Header ๋Š” ๋ฌด์‹œ๋œ๋‹ค.

 

4. HTTP ์กฐ๊ฑด๋ถ€ ์š”์ฒญ

  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„์— ์—…๋ฐ์ดํŠธ ๋œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ๋Š”์ง€ ๋ณด๋‚ด๋Š” ์š”์ฒญ
  • ์บ์‹œ๋Š” ์„œ๋ฒ„์— ์ฃผ๊ธฐ์ ์œผ๋กœ revalidate ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
    • revalidate hit(slow hit) : ์บ์‹œ๊ฐ€ ์ตœ์‹ ์ผ ๊ฒฝ์šฐ, ์„œ๋ฒ„์—์„œ 304 Not Modified ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ „๋‹ฌํ•˜๋ฉฐ ์บ์‹œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
    • revalidate miss : ์บ์‹œ๊ฐ€ ์ตœ์‹  ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ์„œ๋ฒ„์—์„œ 200 OK ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ (ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ์— ์ €์žฅ)

revalidate hit, revalidate miss

 

 

5. HTTP Header : last-modified, etag(entity tag)

[1] Time-based

  • Last-Modified : ์‘๋‹ต ํ—ค๋”์— Last-Modified ๊ฐ€ ์žˆ์œผ๋ฉด Time-based ์กฐ๊ฑด์ด ํ™œ์„ฑํ™”๋˜๋Š” ํ—ค๋” 
    • Last-Modified : Mon, 03 Jan 2011 17:45:57 GMT
  •  If-Modified-Since : ๋ธŒ๋ผ์šฐ์ €๋Š” ๋‚ ์งœ๋ฅผ ์ €์žฅํ•ด๋‘๊ณ  ํ•ด๋‹น ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•  ๋•Œ ํ—ค๋”์— If-Modified-Since ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
    • If-Modified-Since :  If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
    • ๊ตฌ๋ถ„
      • ๋ฆฌ์†Œ์Šค ๋‚ ์งœ๊ฐ€ ๋™์ผํ•˜๋ฉด 304 Not modified ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์ „์†กํ•œ๋‹ค. (์บ์‹œ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ)
      • ๋ฆฌ์†Œ์Šค ๋‚ ์งœ๊ฐ€ ๋™์ผํ•˜์ง€ ์•Š์œผ๋ฉด 200 OK ์‘๋‹ต๋ฅผ ์ „์†กํ•˜๋ฉฐ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (์บ์‹œ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ)

 

[2] Content-based

  • ETag : ํŠน์ • ๋ฒ„์ „์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์‹๋ณ„ํ•˜๋Š” ์‹๋ณ„์ž ํ—ค๋”์ด๋‹ค.
    • ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
    • ETag: W/"0815"
  • If-None-Match : ์‘๋‹ต ํ—ค๋”์— ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด Content-based ์กฐ๊ฑด์ด ํ™œ์„ฑํ™”๋˜๋Š” ํ—ค๋”์ด๋‹ค.
    • ๊ตฌ๋ถ„ 
      • Etag ๊ฐ’์ด ๋™์ผํ•˜๋ฉด ์„œ๋ฒ„์—์„œ 304Not modified ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์ „์†กํ•œ๋‹ค. (์บ์‹œ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ)
      • Etag ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด 200 OK ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์ „์†กํ•˜๋ฉฐ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (์บ์‹œ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ)

 

6. Spring ์—์„œ Cache-Control ์ ์šฉํ•˜๊ธฐ

  • ๋ชจ๋“  path ์— no-cache, private ๋ฅผ ์„ค์ •ํ•˜๋Š” ์ฝ”๋“œ
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(final InterceptorRegistry registry) {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/**");
        registry.addInterceptor(interceptor);
    }
    
}

 

 

 

7. Spring ์—์„œ ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ ์šฉํ•˜๊ธฐ

  • server.compression.enabled : Content-Encoding: gzip ์„ ์‚ฌ์šฉํ•จ์„ ๋ช…์‹œํ•˜๋Š” ํ”„๋กœํผํ‹ฐ
  • server.compression.mime-types : ์••์ถ•์„ ์ ์šฉํ•  mime-type ์„ ์„ค์ •ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ
  • server.compression.min-response-size : ์‘๋‹ต์ด ์••์ถ•๋˜๊ธฐ ์ „ ์ตœ์†Œ ํฌ๊ธฐ ์„ค์ •
    • defualt: 2048 bytes = 2KB
    • ํ•ด๋‹น ํฌ๊ธฐ๋ณด๋‹ค ์ž‘์€ ์••์ถ•์€ ๋˜์ง€ ์•Š์Œ
server:
  compression:
    enabled: true
    mime-types: text/html,text/css,application/javascript,application/json
    min-response-size: 10

 

 

 

8. Spring ์—์„œ Etag ์ ์šฉํ•˜๊ธฐ

import javax.servlet.Filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        Filter etagHeaderFilter = new ShallowEtagHeaderFilter();
        registration.setFilter(etagHeaderFilter);
        registration.addUrlPatterns("/etag");
        return registration;
    }
    
}

 

9. Spring ์—์„œ Cache busting

  1. localhost:8080/resource-versioning ์š”์ฒญ
    • /resources/[version-number]/js/index.js ์‘๋‹ต ํ—ค๋” : Cache-Control : max-age=31536000 ํ™•์ธ
  2. /resources/[version-number]/js/index.js ์š”์ฒญ
    • ์‘๋‹ต ํ—ค๋” 304 Not Modified ์‘๋‹ต ํ—ค๋” ํ™•์ธ

/resource-versioning network
/resources/[version]/js/index.js network

 

source code

import java.time.Duration;

import javax.servlet.Filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

import com.brainbackdoor.support.ResourceVersion;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    public static final String PREFIX_STATIC_RESOURCES = "/resources";

    private final ResourceVersion version;

    @Autowired
    public WebMvcConfig(ResourceVersion version) {
        this.version = version;
    }

    @Override
    public void addInterceptors(final InterceptorRegistry registry) {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/*");
        registry.addInterceptor(interceptor)
            .excludePathPatterns(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**");
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        Filter etagHeaderFilter = new ShallowEtagHeaderFilter();
        registration.setFilter(etagHeaderFilter);
        registration.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/*");
        return registration;
    }

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        CacheControl cacheControl = CacheControl.maxAge(Duration.ofDays(365)).cachePublic(); // 1์‹œ๊ฐ„ ์„ค์ •
        registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") // path prefix ์„ค์ •
            .addResourceLocations("classpath:/") // ์ •์  ๋ฆฌ์†Œ์Šค ๊ฒฝ๋กœ
            .setCacheControl(cacheControl);
    }

}
@Test
void testCacheBustingOfStaticResources() {
    final var uri = String.format("%s/%s/js/index.js", PREFIX_STATIC_RESOURCES, version.getVersion());

    // "/resource-versioning/[version]/js/index.js" ๊ฒฝ๋กœ์˜ ์ •์  ํŒŒ์ผ์— ETag๋ฅผ ์‚ฌ์šฉํ•œ ์บ์‹ฑ์ด ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
    final var response = webTestClient
            .get()
            .uri(uri)
            .exchange()
            .expectStatus().isOk()
            .expectHeader().exists(HttpHeaders.ETAG)
            .expectHeader().cacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic())
            .expectBody(String.class).returnResult();

    log.info("response header\n{}", response.getResponseHeaders());
    log.info("response body\n{}", response.getResponseBody());

    final var etag = response.getResponseHeaders().getETag();

    // ์บ์‹ฑ๋˜์—ˆ๋‹ค๋ฉด "/resource-versioning/[version]/js/index.js"๋กœ ๋‹ค์‹œ ํ˜ธ์ถœํ–ˆ์„๋•Œ HTTP status๋Š” 304๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    webTestClient.get()
            .uri(uri)
            .header(HttpHeaders.IF_NONE_MATCH, etag)
            .exchange()
            .expectStatus()
            .isNotModified();
}

 

 

Reference

  • ์ฐธ๊ณ  ๊ฐ•์˜
    • https://www.inflearn.com/course/%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B3%B5%EB%B0%A9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0/dashboard
  • HTTP Header: Cache-Control
    • https://developer.mozilla.org/ko/docs/Web/HTTP/Caching#%EC%BA%90%EC%8B%B1_%EC%A0%9C%EC%96%B4
    • https://amaran-th.github.io/Spring/[HTTP]%20HTTP%20%EC%B5%9C%EC%A0%81%ED%99%94%EC%99%80%20%EC%BA%90%EC%8B%B1/
  • HTTP Header: last-modified, Etag(entity tag) 
    • https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Last-Modified 
    • https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/If-Modified-Since 
    • https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/If-None-Match
    • https://developer.mozilla.org/ko/docs/Web/HTTP/Conditional_requests 
  • spring cache control
    • https://www.baeldung.com/spring-mvc-cache-headers
  • server compression (gzip)
    • https://docs.spring.io/spring-boot/docs/2.0.6.RELEASE/reference/html/howto-embedded-web-servers.html 
    • https://medium.com/@AlexanderObregon/spring-microservices-data-compression-techniques-for-faster-responses-b61d3fc6fae1

'๐Ÿ“‚ education & lecture > ์ธํ”„๋ผ ๊ณต๋ฐฉ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[์ธํ”„๋ผ๊ณต๋ฐฉ] ์ปจํผ๋Ÿฐ์Šค ์‹ ์ฒญ - 1. github action ์„ ํ†ตํ•œ ๋นŒ๋“œ ์„ค์ •  (1) 2024.03.05
[์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 4. ํ™”๋ฉด ์‘๋‹ต ๊ฐœ์„ ํ•˜๊ธฐ  (0) 2024.02.29
[์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 3. 3-tier ๊ตฌ์„ฑํ•˜๊ธฐ  (0) 2024.02.27
[์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 2. ์„œ๋ฒ„ ๊ตฌ์„ฑํ•˜๊ธฐ  (1) 2024.02.25
[์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 1. ๋ง ๊ตฌ์„ฑํ•˜๊ธฐ  (1) 2024.02.23
'๐Ÿ“‚ education & lecture/์ธํ”„๋ผ ๊ณต๋ฐฉ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [์ธํ”„๋ผ๊ณต๋ฐฉ] ์ปจํผ๋Ÿฐ์Šค ์‹ ์ฒญ - 1. github action ์„ ํ†ตํ•œ ๋นŒ๋“œ ์„ค์ •
  • [์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 4. ํ™”๋ฉด ์‘๋‹ต ๊ฐœ์„ ํ•˜๊ธฐ
  • [์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 3. 3-tier ๊ตฌ์„ฑํ•˜๊ธฐ
  • [์ธํ”„๋ผ๊ณต๋ฐฉ] ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ - 2. ์„œ๋ฒ„ ๊ตฌ์„ฑํ•˜๊ธฐ
cooper25
cooper25
  • cooper25
    dev cooper
    cooper25
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (84)
      • ๐Ÿ“‚ backend (34)
        • spring (19)
        • architecture (10)
        • test (5)
      • ๐Ÿ“‚ computer-science (5)
      • ๐Ÿ“‚ programming-language (12)
        • java (12)
      • ๐Ÿ“‚ infra (9)
        • mysql (4)
        • redis (2)
        • message-queue (3)
      • ๐Ÿ“‚ cloud (2)
        • aws (2)
      • ๐Ÿ“‚ frontend (1)
        • react (0)
      • ๐Ÿ“‚ education & lecture (16)
        • ์ธํ”„๋ผ ๊ณต๋ฐฉ (11)
        • ํ•ญํ”Œ ๋ฐฑ์—”๋“œ 7๊ธฐ (5)
      • ๐Ÿ“‚ ai (1)
        • claude (1)
      • ๐Ÿ“‚ etc (2)
        • ํšŒ๊ณ  (1)
        • ์ปจํผ๋Ÿฐ์Šค (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ธํ”„๋ผ๊ณต๋ฐฉ
    gof
    ์Šคํ”„๋ง์บ ํ”„ ํ›„๊ธฐ
    spring camp 2025
    ํ•ญํ•ดํ”Œ๋Ÿฌ์Šค
    spring-batch
    UUID
    ํšŒ๊ณ 
    nGrinder
    AWS
    react
    mysql
    Redisson
    ๋™์‹œ์„ฑ
    spring AOP
    ๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์„ค๊ณ„
    ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๊ณ„
    spring
    kafka
    JPA
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
cooper25
[์ธํ”„๋ผ๊ณต๋ฐฉ] HTTP Cache
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”