Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ

## 1๋‹จ๊ณ„ - HTTP ์„œ๋ฒ„ ๊ตฌํ˜„ํ•˜๊ธฐ
- [x] 1\. GET `index.html` ์‘๋‹ตํ•˜๊ธฐ
- [x] 2\. CSS ์ง€์›ํ•˜๊ธฐ
- [x] 3\. Query String ํŒŒ์‹ฑ
2 changes: 1 addition & 1 deletion study/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'ch.qos.logback:logback-classic:1.5.7'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.apache.commons:commons-lang3:3.14.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package cache.com.example;

import jakarta.servlet.http.HttpServletResponse;

import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import jakarta.servlet.http.HttpServletResponse;

@Controller
public class GreetingController {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cache.com.example.cachecontrol;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class CacheControlInterceptor implements HandlerInterceptor {

@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable ModelAndView modelAndView
) {
response.setHeader("Cache-Control", "no-cache, private");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new CacheControlInterceptor())
.addPathPatterns("/**");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package cache.com.example.etag;

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;

@Configuration
public class EtagFilterConfiguration {

// @Bean
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
// return null;
// }
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new ShallowEtagHeaderFilter());
filterRegistrationBean.addUrlPatterns("/etag");
filterRegistrationBean.addInitParameter("tag", "etag");
return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package cache.com.example.version;

import java.time.Duration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

Expand All @@ -20,6 +23,8 @@ public CacheBustingWebConfig(ResourceVersion version) {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**")
.addResourceLocations("classpath:/static/");
.addResourceLocations("classpath:/static/")
.setEtagGenerator(__ -> version.getVersion())
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import jakarta.annotation.PostConstruct;

@Component
public class ResourceVersion {

Expand Down
4 changes: 4 additions & 0 deletions study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ handlebars:
suffix: .html

server:
compression:
enabled: true
min-response-size: 10
tomcat:
accept-count: 1
max-connections: 1
threads:
min-spare: 2
max: 2
24 changes: 13 additions & 11 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package study;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
* ์›น์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ html ํŒŒ์ผ์„ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
Expand All @@ -27,8 +28,9 @@ class FileTest {
void resource_๋””๋ ‰ํ„ฐ๋ฆฌ์—_์žˆ๋Š”_ํŒŒ์ผ์˜_๊ฒฝ๋กœ๋ฅผ_์ฐพ๋Š”๋‹ค() {
final String fileName = "nextstep.txt";

// todo
final String actual = "";
final var classLoader = getClass().getClassLoader();
final var url = classLoader.getResource(fileName);
final String actual = url.toString();

assertThat(actual).endsWith(fileName);
}
Expand All @@ -40,14 +42,14 @@ class FileTest {
* File, Files ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ์ฝ์–ด๋ณด์ž.
*/
@Test
void ํŒŒ์ผ์˜_๋‚ด์šฉ์„_์ฝ๋Š”๋‹ค() {
void ํŒŒ์ผ์˜_๋‚ด์šฉ์„_์ฝ๋Š”๋‹ค() throws IOException {
final String fileName = "nextstep.txt";

// todo
final Path path = null;
final var classLoader = getClass().getClassLoader();
final var url = classLoader.getResource(fileName);
final Path path = Path.of(url.getPath());

// todo
final List<String> actual = Collections.emptyList();
final List<String> actual = Files.readAllLines(path);

assertThat(actual).containsOnly("nextstep");
}
Expand Down
44 changes: 34 additions & 10 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
package study;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

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)์€ ํ•˜๋‚˜์˜ ์‹œ์Šคํ…œ์—์„œ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ด๋™ ์‹œํ‚ฌ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
Expand Down Expand Up @@ -54,6 +66,7 @@ class OutputStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* OutputStream ๊ฐ์ฒด์˜ write ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚จ๋‹ค
*/

outputStream.write(bytes);
final String actual = outputStream.toString();

assertThat(actual).isEqualTo("nextstep");
Expand All @@ -78,6 +91,7 @@ class OutputStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* flush๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚จ๋‹ค.
* ByteArrayOutputStream๊ณผ ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ์„๊นŒ?
*/
outputStream.flush();

verify(outputStream, atLeastOnce()).flush();
outputStream.close();
Expand All @@ -96,6 +110,7 @@ class OutputStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* try-with-resources๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
* java 9 ์ด์ƒ์—์„œ๋Š” ๋ณ€์ˆ˜๋ฅผ try-with-resources๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
*/
try (outputStream) {}

verify(outputStream, atLeastOnce()).close();
}
Expand Down Expand Up @@ -128,7 +143,8 @@ class InputStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* todo
* inputStream์—์„œ ๋ฐ”์ดํŠธ๋กœ ๋ฐ˜ํ™˜ํ•œ ๊ฐ’์„ ๋ฌธ์ž์—ด๋กœ ์–ด๋–ป๊ฒŒ ๋ฐ”๊ฟ€๊นŒ?
*/
final String actual = "";
byte[] stream = inputStream.readAllBytes();
final String actual = new String(stream, StandardCharsets.UTF_8);

assertThat(actual).isEqualTo("๐Ÿคฉ");
assertThat(inputStream.read()).isEqualTo(-1);
Expand All @@ -148,6 +164,7 @@ class InputStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* try-with-resources๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
* java 9 ์ด์ƒ์—์„œ๋Š” ๋ณ€์ˆ˜๋ฅผ try-with-resources๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
*/
try (inputStream) {}

verify(inputStream, atLeastOnce()).close();
}
Expand All @@ -169,12 +186,12 @@ class FilterStream_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* ๋ฒ„ํผ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฒ„ํผ์˜ ๊ธฐ๋ณธ ์‚ฌ์ด์ฆˆ๋Š” ์–ผ๋งˆ์ผ๊นŒ?
*/
@Test
void ํ•„ํ„ฐ์ธ_BufferedInputStream๋ฅผ_์‚ฌ์šฉํ•ด๋ณด์ž() {
void ํ•„ํ„ฐ์ธ_BufferedInputStream๋ฅผ_์‚ฌ์šฉํ•ด๋ณด์ž() throws IOException {
final String text = "ํ•„ํ„ฐ์— ์—ฐ๊ฒฐํ•ด๋ณด์ž.";
final InputStream inputStream = new ByteArrayInputStream(text.getBytes());
final InputStream bufferedInputStream = null;
final InputStream bufferedInputStream = new BufferedInputStream(inputStream);

final byte[] actual = new byte[0];
final byte[] actual = bufferedInputStream.readAllBytes();

assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class);
assertThat(actual).isEqualTo("ํ•„ํ„ฐ์— ์—ฐ๊ฒฐํ•ด๋ณด์ž.".getBytes());
Expand All @@ -197,16 +214,23 @@ class InputStreamReader_ํ•™์Šต_ํ…Œ์ŠคํŠธ {
* ํ•„ํ„ฐ์ธ BufferedReader๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด readLine ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฌธ์ž์—ด(String)์„ ํ•œ ์ค„ ์”ฉ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
*/
@Test
void BufferedReader๋ฅผ_์‚ฌ์šฉํ•˜์—ฌ_๋ฌธ์ž์—ด์„_์ฝ์–ด์˜จ๋‹ค() {
void BufferedReader๋ฅผ_์‚ฌ์šฉํ•˜์—ฌ_๋ฌธ์ž์—ด์„_์ฝ์–ด์˜จ๋‹ค() throws IOException {
final String emoji = String.join("\r\n",
"๐Ÿ˜€๐Ÿ˜ƒ๐Ÿ˜„๐Ÿ˜๐Ÿ˜†๐Ÿ˜…๐Ÿ˜‚๐Ÿคฃ๐Ÿฅฒโ˜บ๏ธ๐Ÿ˜Š",
"๐Ÿ˜‡๐Ÿ™‚๐Ÿ™ƒ๐Ÿ˜‰๐Ÿ˜Œ๐Ÿ˜๐Ÿฅฐ๐Ÿ˜˜๐Ÿ˜—๐Ÿ˜™๐Ÿ˜š",
"๐Ÿ˜‹๐Ÿ˜›๐Ÿ˜๐Ÿ˜œ๐Ÿคช๐Ÿคจ๐Ÿง๐Ÿค“๐Ÿ˜Ž๐Ÿฅธ๐Ÿคฉ",
"");
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());
final var inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

final StringBuilder actual = new StringBuilder();

while (bufferedReader.ready()) {
actual.append(bufferedReader.readLine())
.append("\r\n");
}

assertThat(actual).hasToString(emoji);
}
}
Expand Down
Loading