From d00a615d39715d8c2f209e0ebe4169b4e07adb61 Mon Sep 17 00:00:00 2001 From: eunbii0213 Date: Tue, 12 Sep 2023 11:27:16 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20study=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/build.gradle | 35 --- .../src/main/java/cache/com/example/App.java | 12 - .../main/java/cache/com/example/Greeting.java | 20 -- .../cache/com/example/GreetingController.java | 70 ------ .../example/cachecontrol/CacheWebConfig.java | 13 -- .../com/example/dto/RegisterRequest.java | 20 -- .../example/etag/EtagFilterConfiguration.java | 12 - .../version/CacheBustingWebConfig.java | 25 -- .../com/example/version/ResourceVersion.java | 29 --- .../version/VersionHandlebarsHelper.java | 25 -- study/src/main/java/thread/stage2/App.java | 12 - .../java/thread/stage2/HelloWorldService.java | 11 - .../java/thread/stage2/SampleController.java | 33 --- study/src/main/resources/application.yml | 9 - study/src/main/resources/static/js/index.js | 1 - study/src/main/resources/templates/index.html | 10 - .../templates/resource-versioning.html | 11 - .../com/example/GreetingControllerTest.java | 102 --------- study/src/test/java/study/FileTest.java | 54 ----- study/src/test/java/study/IOStreamTest.java | 213 ------------------ .../thread/stage0/SynchronizationTest.java | 56 ----- .../java/thread/stage0/ThreadPoolsTest.java | 66 ------ .../test/java/thread/stage0/ThreadTest.java | 82 ------- .../java/thread/stage1/ConcurrencyTest.java | 40 ---- .../java/thread/stage1/HttpProcessor.java | 17 -- study/src/test/java/thread/stage1/User.java | 36 --- .../test/java/thread/stage1/UserServlet.java | 27 --- .../src/test/java/thread/stage2/AppTest.java | 50 ---- .../java/thread/stage2/TestHttpUtils.java | 29 --- study/src/test/resources/nextstep.txt | 1 - 30 files changed, 1121 deletions(-) delete mode 100644 study/build.gradle delete mode 100644 study/src/main/java/cache/com/example/App.java delete mode 100644 study/src/main/java/cache/com/example/Greeting.java delete mode 100644 study/src/main/java/cache/com/example/GreetingController.java delete mode 100644 study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java delete mode 100644 study/src/main/java/cache/com/example/dto/RegisterRequest.java delete mode 100644 study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java delete mode 100644 study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java delete mode 100644 study/src/main/java/cache/com/example/version/ResourceVersion.java delete mode 100644 study/src/main/java/cache/com/example/version/VersionHandlebarsHelper.java delete mode 100644 study/src/main/java/thread/stage2/App.java delete mode 100644 study/src/main/java/thread/stage2/HelloWorldService.java delete mode 100644 study/src/main/java/thread/stage2/SampleController.java delete mode 100644 study/src/main/resources/application.yml delete mode 100644 study/src/main/resources/static/js/index.js delete mode 100644 study/src/main/resources/templates/index.html delete mode 100644 study/src/main/resources/templates/resource-versioning.html delete mode 100644 study/src/test/java/cache/com/example/GreetingControllerTest.java delete mode 100644 study/src/test/java/study/FileTest.java delete mode 100644 study/src/test/java/study/IOStreamTest.java delete mode 100644 study/src/test/java/thread/stage0/SynchronizationTest.java delete mode 100644 study/src/test/java/thread/stage0/ThreadPoolsTest.java delete mode 100644 study/src/test/java/thread/stage0/ThreadTest.java delete mode 100644 study/src/test/java/thread/stage1/ConcurrencyTest.java delete mode 100644 study/src/test/java/thread/stage1/HttpProcessor.java delete mode 100644 study/src/test/java/thread/stage1/User.java delete mode 100644 study/src/test/java/thread/stage1/UserServlet.java delete mode 100644 study/src/test/java/thread/stage2/AppTest.java delete mode 100644 study/src/test/java/thread/stage2/TestHttpUtils.java delete mode 100644 study/src/test/resources/nextstep.txt diff --git a/study/build.gradle b/study/build.gradle deleted file mode 100644 index 56714a5f76..0000000000 --- a/study/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -plugins { - id "java" - id "org.springframework.boot" version "2.7.14" - id "io.spring.dependency-management" version "1.0.11.RELEASE" -} - -group "nextstep" -version "1.0-SNAPSHOT" - -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 - -repositories { - mavenCentral() -} - -dependencies { - implementation "ch.qos.logback:logback-classic:1.2.12" - implementation "org.apache.commons:commons-lang3:3.13.0" - implementation "com.fasterxml.jackson.core:jackson-databind:2.15.2" - implementation "pl.allegro.tech.boot:handlebars-spring-boot-starter:0.3.4" - implementation "org.springframework.boot:spring-boot-starter-web:2.7.14" - implementation "org.springframework.boot:spring-boot-starter-webflux:2.7.14" - - testImplementation "org.assertj:assertj-core:3.24.2" - testImplementation "org.mockito:mockito-core:5.4.0" - testImplementation "org.junit.jupiter:junit-jupiter-engine:5.7.2" - testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.2" - testImplementation "org.springframework.boot:spring-boot-starter-test:2.7.2" -} - -test { - maxParallelForks 3 - useJUnitPlatform() -} diff --git a/study/src/main/java/cache/com/example/App.java b/study/src/main/java/cache/com/example/App.java deleted file mode 100644 index b83b139cc0..0000000000 --- a/study/src/main/java/cache/com/example/App.java +++ /dev/null @@ -1,12 +0,0 @@ -package cache.com.example; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class App { - - public static void main(String[] args) { - SpringApplication.run(App.class, args); - } -} diff --git a/study/src/main/java/cache/com/example/Greeting.java b/study/src/main/java/cache/com/example/Greeting.java deleted file mode 100644 index cde79d9c79..0000000000 --- a/study/src/main/java/cache/com/example/Greeting.java +++ /dev/null @@ -1,20 +0,0 @@ -package cache.com.example; - -public class Greeting { - - private final long id; - private final String content; - - public Greeting(String content) { - this.id = 1; - this.content = content; - } - - public long getId() { - return id; - } - - public String getContent() { - return content; - } -} diff --git a/study/src/main/java/cache/com/example/GreetingController.java b/study/src/main/java/cache/com/example/GreetingController.java deleted file mode 100644 index 8f04768530..0000000000 --- a/study/src/main/java/cache/com/example/GreetingController.java +++ /dev/null @@ -1,70 +0,0 @@ -package cache.com.example; - -import cache.com.example.dto.RegisterRequest; -import org.springframework.http.CacheControl; -import org.springframework.http.HttpHeaders; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.servlet.http.HttpServletResponse; - -@Controller -public class GreetingController { - - @GetMapping("/") - public String index() { - return "index"; - } - - @GetMapping("/index.html") - public String indexHtml() { - return "index"; - } - - @GetMapping("/login") - public String login(@RequestParam String account, @RequestParam String password) { - return "login"; - } - - @GetMapping("/login") - public String loginWithNoParams() { - return "login"; - } - - @GetMapping("/register") - public String register() { - return "register"; - } - - @PostMapping("/register") - public String registerUser(@RequestBody String requestBody) { - return "index"; - } - - /** - * 인터셉터를 쓰지 않고 response에 직접 헤더값을 지정할 수도 있다. - */ - @GetMapping("/cache-control") - public String cacheControl(final HttpServletResponse response) { - final String cacheControl = CacheControl - .noCache() - .cachePrivate() - .getHeaderValue(); - response.addHeader(HttpHeaders.CACHE_CONTROL, cacheControl); - return "index"; - } - - @GetMapping("/etag") - public String etag() { - return "index"; - } - - @GetMapping("/resource-versioning") - public String resourceVersioning() { - return "resource-versioning"; - } -} diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java deleted file mode 100644 index 305b1f1e1e..0000000000 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java +++ /dev/null @@ -1,13 +0,0 @@ -package cache.com.example.cachecontrol; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class CacheWebConfig implements WebMvcConfigurer { - - @Override - public void addInterceptors(final InterceptorRegistry registry) { - } -} diff --git a/study/src/main/java/cache/com/example/dto/RegisterRequest.java b/study/src/main/java/cache/com/example/dto/RegisterRequest.java deleted file mode 100644 index 3ea6aa79ea..0000000000 --- a/study/src/main/java/cache/com/example/dto/RegisterRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package cache.com.example.dto; - -public class RegisterRequest { - - private String account; - private String email; - private String password; - - public String getAccount() { - return account; - } - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } -} diff --git a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java deleted file mode 100644 index 41ef7a3d9a..0000000000 --- a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java +++ /dev/null @@ -1,12 +0,0 @@ -package cache.com.example.etag; - -import org.springframework.context.annotation.Configuration; - -@Configuration -public class EtagFilterConfiguration { - -// @Bean -// public FilterRegistrationBean shallowEtagHeaderFilter() { -// return null; -// } -} diff --git a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java deleted file mode 100644 index 6da6d2c795..0000000000 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package cache.com.example.version; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class CacheBustingWebConfig implements WebMvcConfigurer { - - public static final String PREFIX_STATIC_RESOURCES = "/resources"; - - private final ResourceVersion version; - - @Autowired - public CacheBustingWebConfig(ResourceVersion version) { - this.version = version; - } - - @Override - public void addResourceHandlers(final ResourceHandlerRegistry registry) { - registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") - .addResourceLocations("classpath:/static/"); - } -} diff --git a/study/src/main/java/cache/com/example/version/ResourceVersion.java b/study/src/main/java/cache/com/example/version/ResourceVersion.java deleted file mode 100644 index 7049b3d82a..0000000000 --- a/study/src/main/java/cache/com/example/version/ResourceVersion.java +++ /dev/null @@ -1,29 +0,0 @@ -package cache.com.example.version; - -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -@Component -public class ResourceVersion { - - private static final String DEFAULT_DATE_TIME_FORMAT = "yyyyMMddHHmmSS"; - - private String version; - - @PostConstruct - public void init() { - this.version = now(); - } - - public String getVersion() { - return version; - } - - private static String now() { - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT); - return LocalDateTime.now().format(formatter); - } -} diff --git a/study/src/main/java/cache/com/example/version/VersionHandlebarsHelper.java b/study/src/main/java/cache/com/example/version/VersionHandlebarsHelper.java deleted file mode 100644 index a8e004466a..0000000000 --- a/study/src/main/java/cache/com/example/version/VersionHandlebarsHelper.java +++ /dev/null @@ -1,25 +0,0 @@ -package cache.com.example.version; - -import com.github.jknack.handlebars.Options; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import pl.allegro.tech.boot.autoconfigure.handlebars.HandlebarsHelper; - -@HandlebarsHelper -public class VersionHandlebarsHelper { - - private static final Logger log = LoggerFactory.getLogger(VersionHandlebarsHelper.class); - - private final ResourceVersion version; - - @Autowired - public VersionHandlebarsHelper(ResourceVersion version) { - this.version = version; - } - - public String staticUrls(String path, Options options) { - log.debug("static url : {}", path); - return String.format("/resources/%s%s", version.getVersion(), path); - } -} diff --git a/study/src/main/java/thread/stage2/App.java b/study/src/main/java/thread/stage2/App.java deleted file mode 100644 index 34873c6169..0000000000 --- a/study/src/main/java/thread/stage2/App.java +++ /dev/null @@ -1,12 +0,0 @@ -package thread.stage2; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class App { - - public static void main(String[] args) { - SpringApplication.run(App.class, args); - } -} diff --git a/study/src/main/java/thread/stage2/HelloWorldService.java b/study/src/main/java/thread/stage2/HelloWorldService.java deleted file mode 100644 index e888c5e504..0000000000 --- a/study/src/main/java/thread/stage2/HelloWorldService.java +++ /dev/null @@ -1,11 +0,0 @@ -package thread.stage2; - -import org.springframework.stereotype.Component; - -@Component -public class HelloWorldService { - - public String helloWorld() { - return "Hello World"; - } -} diff --git a/study/src/main/java/thread/stage2/SampleController.java b/study/src/main/java/thread/stage2/SampleController.java deleted file mode 100644 index a9636dd934..0000000000 --- a/study/src/main/java/thread/stage2/SampleController.java +++ /dev/null @@ -1,33 +0,0 @@ -package thread.stage2; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.util.concurrent.atomic.AtomicInteger; - -@Controller -public class SampleController { - - private static final Logger log = LoggerFactory.getLogger(SampleController.class); - - private static final AtomicInteger count = new AtomicInteger(0); - - private final HelloWorldService helloWorldService; - - @Autowired - public SampleController(final HelloWorldService helloWorldService) { - this.helloWorldService = helloWorldService; - } - - @GetMapping("/test") - @ResponseBody - public String helloWorld() throws InterruptedException { - Thread.sleep(500); - log.info("http call count : {}", count.incrementAndGet()); - return helloWorldService.helloWorld(); - } -} diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml deleted file mode 100644 index 4e8655a962..0000000000 --- a/study/src/main/resources/application.yml +++ /dev/null @@ -1,9 +0,0 @@ -handlebars: - suffix: .html - -server: - tomcat: - accept-count: 1 - max-connections: 1 - threads: - max: 2 diff --git a/study/src/main/resources/static/js/index.js b/study/src/main/resources/static/js/index.js deleted file mode 100644 index 5893f9d0d5..0000000000 --- a/study/src/main/resources/static/js/index.js +++ /dev/null @@ -1 +0,0 @@ -console.log('hello world'); \ No newline at end of file diff --git a/study/src/main/resources/templates/index.html b/study/src/main/resources/templates/index.html deleted file mode 100644 index 46cbef0f24..0000000000 --- a/study/src/main/resources/templates/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Getting Started: Serving Web Content - - - -Hello, World! - - diff --git a/study/src/main/resources/templates/resource-versioning.html b/study/src/main/resources/templates/resource-versioning.html deleted file mode 100644 index cb8323ebf9..0000000000 --- a/study/src/main/resources/templates/resource-versioning.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Document - - -html > head > script 태그의 src에 version 디렉터리가 생겼다. - - diff --git a/study/src/test/java/cache/com/example/GreetingControllerTest.java b/study/src/test/java/cache/com/example/GreetingControllerTest.java deleted file mode 100644 index 9ce2a394f7..0000000000 --- a/study/src/test/java/cache/com/example/GreetingControllerTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package cache.com.example; - -import cache.com.example.version.ResourceVersion; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.CacheControl; -import org.springframework.http.HttpHeaders; -import org.springframework.test.web.reactive.server.WebTestClient; - -import java.time.Duration; - -import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -class GreetingControllerTest { - - private static final Logger log = LoggerFactory.getLogger(GreetingControllerTest.class); - - @Autowired - private ResourceVersion version; - - @Autowired - private WebTestClient webTestClient; - - @Test - void testNoCachePrivate() { - final var response = webTestClient - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectHeader().cacheControl(CacheControl.noCache().cachePrivate()) - .expectBody(String.class).returnResult(); - - log.info("response body\n{}", response.getResponseBody()); - } - - @Test - void testCompression() { - final var response = webTestClient - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - - // gzip으로 요청 보내도 어떤 방식으로 압축할지 서버에서 결정한다. - // 웹브라우저에서 localhost:8080으로 접근하면 응답 헤더에 "Content-Encoding: gzip"이 있다. - .expectHeader().valueEquals(HttpHeaders.TRANSFER_ENCODING, "chunked") - .expectBody(String.class).returnResult(); - - log.info("response body\n{}", response.getResponseBody()); - } - - @Test - void testETag() { - final var response = webTestClient - .get() - .uri("/etag") - .exchange() - .expectStatus().isOk() - .expectHeader().exists(HttpHeaders.ETAG) - .expectBody(String.class).returnResult(); - - log.info("response body\n{}", response.getResponseBody()); - } - - /** - * http://localhost:8080/resource-versioning - * 위 url의 html 파일에서 사용하는 js, css와 같은 정적 파일에 캐싱을 적용한다. - * 보통 정적 파일을 캐싱 무효화하기 위해 캐싱과 함께 버전을 적용시킨다. - * 정적 파일에 변경 사항이 생기면 배포할 때 버전을 바꿔주면 적용된 캐싱을 무효화(Caching Busting)할 수 있다. - */ - @Test - void testCacheBustingOfStaticResources() { - final var uri = String.format("%s/%s/js/index.js", PREFIX_STATIC_RESOURCES, version.getVersion()); - - // "/resource-versioning/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 body\n{}", response.getResponseBody()); - - final var etag = response.getResponseHeaders().getETag(); - - // 캐싱되었다면 "/resource-versioning/js/index.js"로 다시 호출했을때 HTTP status는 304를 반환한다. - webTestClient.get() - .uri(uri) - .header(HttpHeaders.IF_NONE_MATCH, etag) - .exchange() - .expectStatus() - .isNotModified(); - } -} diff --git a/study/src/test/java/study/FileTest.java b/study/src/test/java/study/FileTest.java deleted file mode 100644 index e1b6cca042..0000000000 --- a/study/src/test/java/study/FileTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package study; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. - * File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다. - */ -@DisplayName("File 클래스 학습 테스트") -class FileTest { - - /** - * resource 디렉터리 경로 찾기 - * - * File 객체를 생성하려면 파일의 경로를 알아야 한다. - * 자바 애플리케이션은 resource 디렉터리에 HTML, CSS 같은 정적 파일을 저장한다. - * resource 디렉터리의 경로는 어떻게 알아낼 수 있을까? - */ - @Test - void resource_디렉터리에_있는_파일의_경로를_찾는다() { - final String fileName = "nextstep.txt"; - - // todo - final String actual = ""; - - assertThat(actual).endsWith(fileName); - } - - /** - * 파일 내용 읽기 - * - * 읽어온 파일의 내용을 I/O Stream을 사용해서 사용자에게 전달 해야 한다. - * File, Files 클래스를 사용하여 파일의 내용을 읽어보자. - */ - @Test - void 파일의_내용을_읽는다() { - final String fileName = "nextstep.txt"; - - // todo - final Path path = null; - - // todo - final List actual = Collections.emptyList(); - - assertThat(actual).containsOnly("nextstep"); - } -} diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java deleted file mode 100644 index 47a79356b6..0000000000 --- a/study/src/test/java/study/IOStreamTest.java +++ /dev/null @@ -1,213 +0,0 @@ -package study; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.io.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - -/** - * 자바는 스트림(Stream)으로부터 I/O를 사용한다. - * 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다. - * - * InputStream은 데이터를 읽고, OutputStream은 데이터를 쓴다. - * FilterStream은 InputStream이나 OutputStream에 연결될 수 있다. - * FilterStream은 읽거나 쓰는 데이터를 수정할 때 사용한다. (e.g. 암호화, 압축, 포맷 변환) - * - * Stream은 데이터를 바이트로 읽고 쓴다. - * 바이트가 아닌 텍스트(문자)를 읽고 쓰려면 Reader와 Writer 클래스를 연결한다. - * Reader, Writer는 다양한 문자 인코딩(e.g. UTF-8)을 처리할 수 있다. - */ -@DisplayName("Java I/O Stream 클래스 학습 테스트") -class IOStreamTest { - - /** - * OutputStream 학습하기 - * - * 자바의 기본 출력 클래스는 java.io.OutputStream이다. - * OutputStream의 write(int b) 메서드는 기반 메서드이다. - * public abstract void write(int b) throws IOException; - */ - @Nested - class OutputStream_학습_테스트 { - - /** - * OutputStream은 다른 매체에 바이트로 데이터를 쓸 때 사용한다. - * OutputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 쓰기 위해 write(int b) 메서드를 사용한다. - * 예를 들어, FilterOutputStream은 파일로 데이터를 쓸 때, - * 또는 DataOutputStream은 자바의 primitive type data를 다른 매체로 데이터를 쓸 때 사용한다. - * - * write 메서드는 데이터를 바이트로 출력하기 때문에 비효율적이다. - * write(byte[] data)write(byte b[], int off, int len) 메서드는 - * 1바이트 이상을 한 번에 전송 할 수 있어 훨씬 효율적이다. - */ - @Test - void OutputStream은_데이터를_바이트로_처리한다() throws IOException { - final byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112}; - final OutputStream outputStream = new ByteArrayOutputStream(bytes.length); - - /** - * todo - * OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다 - */ - - final String actual = outputStream.toString(); - - assertThat(actual).isEqualTo("nextstep"); - outputStream.close(); - } - - /** - * 효율적인 전송을 위해 스트림에서 버퍼링을 사용 할 수 있다. - * BufferedOutputStream 필터를 연결하면 버퍼링이 가능하다. - * - * 버퍼링을 사용하면 OutputStream을 사용할 때 flush를 사용하자. - * flush() 메서드는 버퍼가 아직 가득 차지 않은 상황에서 강제로 버퍼의 내용을 전송한다. - * Stream은 동기(synchronous)로 동작하기 때문에 버퍼가 찰 때까지 기다리면 - * 데드락(deadlock) 상태가 되기 때문에 flush로 해제해야 한다. - */ - @Test - void BufferedOutputStream을_사용하면_버퍼링이_가능하다() throws IOException { - final OutputStream outputStream = mock(BufferedOutputStream.class); - - /** - * todo - * flush를 사용해서 테스트를 통과시킨다. - * ByteArrayOutputStream과 어떤 차이가 있을까? - */ - - verify(outputStream, atLeastOnce()).flush(); - outputStream.close(); - } - - /** - * 스트림 사용이 끝나면 항상 close() 메서드를 호출하여 스트림을 닫는다. - * 장시간 스트림을 닫지 않으면 파일, 포트 등 다양한 리소스에서 누수(leak)가 발생한다. - */ - @Test - void OutputStream은_사용하고_나서_close_처리를_해준다() throws IOException { - final OutputStream outputStream = mock(OutputStream.class); - - /** - * todo - * try-with-resources를 사용한다. - * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. - */ - - verify(outputStream, atLeastOnce()).close(); - } - } - - /** - * InputStream 학습하기 - * - * 자바의 기본 입력 클래스는 java.io.InputStream이다. - * InputStream은 다른 매체로부터 바이트로 데이터를 읽을 때 사용한다. - * InputStream의 read() 메서드는 기반 메서드이다. - * public abstract int read() throws IOException; - * - * InputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 읽기 위해 read() 메서드를 사용한다. - */ - @Nested - class InputStream_학습_테스트 { - - /** - * read() 메서드는 매체로부터 단일 바이트를 읽는데, 0부터 255 사이의 값을 int 타입으로 반환한다. - * int 값을 byte 타입으로 변환하면 -128부터 127 사이의 값으로 변환된다. - * 그리고 Stream 끝에 도달하면 -1을 반환한다. - */ - @Test - void InputStream은_데이터를_바이트로_읽는다() throws IOException { - byte[] bytes = {-16, -97, -92, -87}; - final InputStream inputStream = new ByteArrayInputStream(bytes); - - /** - * todo - * inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까? - */ - final String actual = ""; - - assertThat(actual).isEqualTo("🤩"); - assertThat(inputStream.read()).isEqualTo(-1); - inputStream.close(); - } - - /** - * 스트림 사용이 끝나면 항상 close() 메서드를 호출하여 스트림을 닫는다. - * 장시간 스트림을 닫지 않으면 파일, 포트 등 다양한 리소스에서 누수(leak)가 발생한다. - */ - @Test - void InputStream은_사용하고_나서_close_처리를_해준다() throws IOException { - final InputStream inputStream = mock(InputStream.class); - - /** - * todo - * try-with-resources를 사용한다. - * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. - */ - - verify(inputStream, atLeastOnce()).close(); - } - } - - /** - * FilterStream 학습하기 - * - * 필터는 필터 스트림, reader, writer로 나뉜다. - * 필터는 바이트를 다른 데이터 형식으로 변환 할 때 사용한다. - * reader, writer는 UTF-8, ISO 8859-1 같은 형식으로 인코딩된 텍스트를 처리하는 데 사용된다. - */ - @Nested - class FilterStream_학습_테스트 { - - /** - * BufferedInputStream은 데이터 처리 속도를 높이기 위해 데이터를 버퍼에 저장한다. - * InputStream 객체를 생성하고 필터 생성자에 전달하면 필터에 연결된다. - * 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까? - */ - @Test - void 필터인_BufferedInputStream를_사용해보자() { - final String text = "필터에 연결해보자."; - final InputStream inputStream = new ByteArrayInputStream(text.getBytes()); - final InputStream bufferedInputStream = null; - - final byte[] actual = new byte[0]; - - assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class); - assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes()); - } - } - - /** - * 자바의 기본 문자열은 UTF-16 유니코드 인코딩을 사용한다. - * 문자열이 아닌 바이트 단위로 처리하려니 불편하다. - * 그리고 바이트를 문자(char)로 처리하려면 인코딩을 신경 써야 한다. - * reader, writer를 사용하면 입출력 스트림을 바이트가 아닌 문자 단위로 데이터를 처리하게 된다. - * 그리고 InputStreamReader를 사용하면 지정된 인코딩에 따라 유니코드 문자로 변환할 수 있다. - */ - @Nested - class InputStreamReader_학습_테스트 { - - /** - * InputStreamReader를 사용해서 바이트를 문자(char)로 읽어온다. - * 읽어온 문자(char)를 문자열(String)로 처리하자. - * 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다. - */ - @Test - void BufferedReader를_사용하여_문자열을_읽어온다() { - final String emoji = String.join("\r\n", - "😀😃😄😁😆😅😂🤣🥲☺️😊", - "😇🙂🙃😉😌😍🥰😘😗😙😚", - "😋😛😝😜🤪🤨🧐🤓😎🥸🤩", - ""); - final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes()); - - final StringBuilder actual = new StringBuilder(); - - assertThat(actual).hasToString(emoji); - } - } -} diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java deleted file mode 100644 index 0333c18e3b..0000000000 --- a/study/src/test/java/thread/stage0/SynchronizationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package thread.stage0; - -import org.junit.jupiter.api.Test; - -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다. - * 자바는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지한다. - * 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다. - * - * Synchronization - * https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html - */ -class SynchronizationTest { - - /** - * 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자. - * synchronized 키워드에 대하여 찾아보고 적용하면 된다. - * - * Guide to the Synchronized Keyword in Java - * https://www.baeldung.com/java-synchronized - */ - @Test - void testSynchronized() throws InterruptedException { - var executorService = Executors.newFixedThreadPool(3); - var synchronizedMethods = new SynchronizedMethods(); - - IntStream.range(0, 1000) - .forEach(count -> executorService.submit(synchronizedMethods::calculate)); - executorService.awaitTermination(500, TimeUnit.MILLISECONDS); - - assertThat(synchronizedMethods.getSum()).isEqualTo(1000); - } - - private static final class SynchronizedMethods { - - private int sum = 0; - - public void calculate() { - setSum(getSum() + 1); - } - - public int getSum() { - return sum; - } - - public void setSum(int sum) { - this.sum = sum; - } - } -} diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java deleted file mode 100644 index 238611ebfe..0000000000 --- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package thread.stage0; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * 스레드 풀은 무엇이고 어떻게 동작할까? - * 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자. - * - * Thread Pools - * https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html - * - * Introduction to Thread Pools in Java - * https://www.baeldung.com/thread-pool-java-and-guava - */ -class ThreadPoolsTest { - - private static final Logger log = LoggerFactory.getLogger(ThreadPoolsTest.class); - - @Test - void testNewFixedThreadPool() { - final var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); - executor.submit(logWithSleep("hello fixed thread pools")); - executor.submit(logWithSleep("hello fixed thread pools")); - executor.submit(logWithSleep("hello fixed thread pools")); - - // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; - final int expectedQueueSize = 0; - - assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); - assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); - } - - @Test - void testNewCachedThreadPool() { - final var executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); - executor.submit(logWithSleep("hello cached thread pools")); - executor.submit(logWithSleep("hello cached thread pools")); - executor.submit(logWithSleep("hello cached thread pools")); - - // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; - final int expectedQueueSize = 0; - - assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); - assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); - } - - private Runnable logWithSleep(final String message) { - return () -> { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - log.info(message); - }; - } -} diff --git a/study/src/test/java/thread/stage0/ThreadTest.java b/study/src/test/java/thread/stage0/ThreadTest.java deleted file mode 100644 index 3ffef18869..0000000000 --- a/study/src/test/java/thread/stage0/ThreadTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package thread.stage0; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 자바로 동시에 여러 작업을 처리할 때 스레드를 사용한다. - * 스레드 객체를 직접 생성하는 방법부터 알아보자. - * 진행하면서 막히는 부분은 아래 링크를 참고해서 해결한다. - * - * Thread Objects - * https://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html - * - * Defining and Starting a Thread - * https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html - */ -class ThreadTest { - - private static final Logger log = LoggerFactory.getLogger(ThreadTest.class); - - /** - * 자바에서 직접 스레드를 만드는 방법은 2가지가 있다. - * 먼저 Thread 클래스를 상속해서 스레드로 만드는 방법을 살펴보자. - * 주석을 참고하여 테스트 코드를 작성하고, 테스트를 실행시켜서 메시지가 잘 나오는지 확인한다. - */ - @Test - void testExtendedThread() throws InterruptedException { - // 하단의 ExtendedThread 클래스를 Thread 클래스로 상속하고 스레드 객체를 생성한다. - Thread thread = new ExtendedThread("hello thread"); - - // 생성한 thread 객체를 시작한다. - thread.start(); - - // thread의 작업이 완료될 때까지 기다린다. - thread.join(); - } - - /** - * Runnable 인터페이스를 사용하는 방법도 있다. - * 주석을 참고하여 테스트 코드를 작성하고, 테스트를 실행시켜서 메시지가 잘 나오는지 확인한다. - */ - @Test - void testRunnableThread() throws InterruptedException { - // 하단의 RunnableThread 클래스를 Runnable 인터페이스의 구현체로 만들고 Thread 클래스를 활용하여 스레드 객체를 생성한다. - Thread thread = new Thread(new RunnableThread("hello thread")); - - // 생성한 thread 객체를 시작한다. - thread.start(); - - // thread의 작업이 완료될 때까지 기다린다. - thread.join(); - } - - private static final class ExtendedThread extends Thread { - - private String message; - - public ExtendedThread(final String message) { - this.message = message; - } - - @Override - public void run() { - log.info(message); - } - } - - private static final class RunnableThread implements Runnable { - - private String message; - - public RunnableThread(final String message) { - this.message = message; - } - - @Override - public void run() { - log.info(message); - } - } -} diff --git a/study/src/test/java/thread/stage1/ConcurrencyTest.java b/study/src/test/java/thread/stage1/ConcurrencyTest.java deleted file mode 100644 index f5e8ee070a..0000000000 --- a/study/src/test/java/thread/stage1/ConcurrencyTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package thread.stage1; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * 스레드를 다룰 때 어떤 상황을 조심해야 할까? - * - 상태를 가진 한 객체를 여러 스레드에서 동시에 접근할 경우 - * - static 변수를 가진 객체를 여러 스레드에서 동시에 접근할 경우 - * - * 위 경우는 동기화(synchronization)를 적용시키거나 객체가 상태를 갖지 않도록 한다. - * 객체를 불변 객체로 만드는 방법도 있다. - * - * 웹서버는 여러 사용자가 동시에 접속을 시도하기 때문에 동시성 이슈가 생길 수 있다. - * 어떤 사례가 있는지 아래 테스트 코드를 통해 알아보자. - */ -class ConcurrencyTest { - - @Test - void test() throws InterruptedException { - final var userServlet = new UserServlet(); - - // 웹서버로 동시에 2명의 유저가 gugu라는 이름으로 가입을 시도했다. - // UserServlet의 users에 이미 가입된 회원이 있으면 중복 가입할 수 없도록 코드를 작성했다. - final var firstThread = new Thread(new HttpProcessor(new User("gugu"), userServlet)); - final var secondThread = new Thread(new HttpProcessor(new User("gugu"), userServlet)); - - // 스레드는 실행 순서가 정해져 있지 않다. - // firstThread보다 늦게 시작한 secondThread가 먼저 실행될 수도 있다. - firstThread.start(); - secondThread.start(); - secondThread.join(); // secondThread가 먼저 gugu로 가입했다. - firstThread.join(); - - // 이미 gugu로 가입한 사용자가 있어서 UserServlet.join() 메서드의 if절 조건은 false가 되고 크기는 1이다. - // 하지만 디버거로 개별 스레드를 일시 중지하면 if절 조건이 true가 되고 크기가 2가 된다. 왜 그럴까? - assertThat(userServlet.getUsers()).hasSize(1); - } -} diff --git a/study/src/test/java/thread/stage1/HttpProcessor.java b/study/src/test/java/thread/stage1/HttpProcessor.java deleted file mode 100644 index 8186e8ad13..0000000000 --- a/study/src/test/java/thread/stage1/HttpProcessor.java +++ /dev/null @@ -1,17 +0,0 @@ -package thread.stage1; - -public class HttpProcessor implements Runnable { - - private final User user; - private final UserServlet userServlet; - - public HttpProcessor(final User user, final UserServlet userServlet) { - this.user = user; - this.userServlet = userServlet; - } - - @Override - public void run() { - userServlet.service(user); - } -} diff --git a/study/src/test/java/thread/stage1/User.java b/study/src/test/java/thread/stage1/User.java deleted file mode 100644 index 62e1bf959c..0000000000 --- a/study/src/test/java/thread/stage1/User.java +++ /dev/null @@ -1,36 +0,0 @@ -package thread.stage1; - -import java.util.Objects; - -public class User { - - private final String name; - - public User(final String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (!(o instanceof User)) return false; - final User user = (User) o; - return Objects.equals(name, user.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - - @Override - public String toString() { - return "User{" + - "name='" + name + '\'' + - '}'; - } -} diff --git a/study/src/test/java/thread/stage1/UserServlet.java b/study/src/test/java/thread/stage1/UserServlet.java deleted file mode 100644 index b180a84c32..0000000000 --- a/study/src/test/java/thread/stage1/UserServlet.java +++ /dev/null @@ -1,27 +0,0 @@ -package thread.stage1; - -import java.util.ArrayList; -import java.util.List; - -public class UserServlet { - - private final List users = new ArrayList<>(); - - public void service(final User user) { - join(user); - } - - private void join(final User user) { - if (!users.contains(user)) { - users.add(user); - } - } - - public int size() { - return users.size(); - } - - public List getUsers() { - return users; - } -} diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java deleted file mode 100644 index e253c4a249..0000000000 --- a/study/src/test/java/thread/stage2/AppTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package thread.stage2; - -import org.junit.jupiter.api.Test; - -import java.net.http.HttpResponse; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.assertj.core.api.Assertions.assertThat; - -class AppTest { - - private static final AtomicInteger count = new AtomicInteger(0); - - /** - * 1. App 클래스의 애플리케이션을 실행시켜 서버를 띄운다. - * 2. 아래 테스트를 실행시킨다. - * 3. AppTest가 아닌 App의 콘솔에서 SampleController가 생성한 http call count 로그를 확인한다. - * 4. application.yml에서 설정값을 변경해보면서 어떤 차이점이 있는지 분석해본다. - * - 로그가 찍힌 시간 - * - 스레드명(nio-8080-exec-x)으로 생성된 스레드 갯수를 파악 - * - http call count - * - 테스트 결과값 - */ - @Test - void test() throws Exception { - final var NUMBER_OF_THREAD = 10; - var threads = new Thread[NUMBER_OF_THREAD]; - - for (int i = 0; i < NUMBER_OF_THREAD; i++) { - threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test"))); - } - - for (final var thread : threads) { - thread.start(); - Thread.sleep(50); - } - - for (final var thread : threads) { - thread.join(); - } - - assertThat(count.intValue()).isEqualTo(2); - } - - private static void incrementIfOk(final HttpResponse response) { - if (response.statusCode() == 200) { - count.incrementAndGet(); - } - } -} diff --git a/study/src/test/java/thread/stage2/TestHttpUtils.java b/study/src/test/java/thread/stage2/TestHttpUtils.java deleted file mode 100644 index ca37f2f8bb..0000000000 --- a/study/src/test/java/thread/stage2/TestHttpUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package thread.stage2; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.time.Duration; - -public class TestHttpUtils { - - private static final HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .connectTimeout(Duration.ofSeconds(1)) - .build(); - - public static HttpResponse send(final String path) { - final var request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:8080" + path)) - .timeout(Duration.ofSeconds(1)) - .build(); - - try { - return httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } -} diff --git a/study/src/test/resources/nextstep.txt b/study/src/test/resources/nextstep.txt deleted file mode 100644 index 3ba24f830d..0000000000 --- a/study/src/test/resources/nextstep.txt +++ /dev/null @@ -1 +0,0 @@ -nextstep \ No newline at end of file