Skip to content

Commit 20faf64

Browse files
authored
[톰캣 구현하기 - 2, 3, 4단계] 밀리(김미성) 미션 제출합니다. (#464)
* feat: cookie, session 구현 * refactor: Http Request 파싱 로직 리팩토링 * refactor: Controller 인터페이스 리팩토링 * refactor: HttpResponseGenerator로 응답 메세지 생성하도록 수정 * refactor: FrontController 도입하여 양방향 의존성 제거 * test: cache 학습 테스트 작성 * test: thread 학습 테스트 작성 * feat: Executors로 Thread Pool 적용 * feat: 동시성 컬렉션으로 변경 * refactor: RequestBody 파싱 타입 변경 * docs: 기능 요구사항 작성 * refactor: content-length 헤더 세팅 컨트롤러에서 하도록 수정 * refactor: 코드 가독성 개선 * refactor: static resource 요청을 처리하는 StaticController 구현 * refactor: 세션 책임 분리 * refactor: FrontController를 RequestMapping으로 변경
1 parent 21135a4 commit 20faf64

36 files changed

+557
-269
lines changed

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@
1818
- [x] 회원가입 페이지는 GET으로 요청한다.
1919
- [x] 회원가입을 완료하면 index.html로 리다이렉트한다.
2020
- [x] 로그인 페이지도 버튼을 눌렀을 때 POST 방식으로 전송하도록 변경한다.
21-
- [ ] 서버에서 HTTP 응답을 전달할 때 응답 헤더에 Set-Cookie를 추가한다.
22-
- [ ] Cookie에 JSESSIONID가 없으면 응답 헤더에 Set-Cookie를 반환해준다.
23-
- [ ] 쿠키에서 전달 받은 JSESSIONID의 값으로 로그인 여부를 체크한다.
24-
- [ ] 로그인된 상태에서 /login 페이지에 접근하면 index.html 페이지로 리다이렉트 처리한다.
21+
- [x] 서버에서 HTTP 응답을 전달할 때 응답 헤더에 Set-Cookie를 추가한다.
22+
- [x] Cookie에 JSESSIONID가 없으면 응답 헤더에 Set-Cookie를 반환해준다.
23+
- [x] 쿠키에서 전달 받은 JSESSIONID의 값으로 로그인 여부를 체크한다.
24+
- [x] 로그인된 상태에서 /login 페이지에 접근하면 index.html 페이지로 리다이렉트 처리한다.
25+
26+
### 3단계
27+
28+
- [x] HttpRequest 클래스 구현하기
29+
- [x] HttpResponse 클래스 구현하기
30+
- [x] Controller 인터페이스 추가하기
31+
32+
### 4단계
33+
34+
- [x] Executors로 Thread Pool 적용
35+
- [x] 동시성 컬렉션 사용하기
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package cache.com.example.cachecontrol;
22

33
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.http.CacheControl;
45
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
56
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import org.springframework.web.servlet.mvc.WebContentInterceptor;
68

79
@Configuration
810
public class CacheWebConfig implements WebMvcConfigurer {
911

1012
@Override
1113
public void addInterceptors(final InterceptorRegistry registry) {
14+
final WebContentInterceptor interceptor = new WebContentInterceptor();
15+
interceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/*");
16+
registry.addInterceptor(interceptor);
1217
}
1318
}
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
package cache.com.example.etag;
22

3+
import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES;
4+
5+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
6+
import org.springframework.context.annotation.Bean;
37
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.web.filter.ShallowEtagHeaderFilter;
49

510
@Configuration
611
public class EtagFilterConfiguration {
712

8-
// @Bean
9-
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
10-
// return null;
11-
// }
13+
@Bean
14+
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
15+
final FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>(
16+
new ShallowEtagHeaderFilter());
17+
filterRegistrationBean.addUrlPatterns(
18+
"/etag",
19+
PREFIX_STATIC_RESOURCES + "/*"
20+
);
21+
return filterRegistrationBean;
22+
}
1223
}

study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package cache.com.example.version;
22

3+
import java.time.Duration;
34
import org.springframework.beans.factory.annotation.Autowired;
45
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.http.CacheControl;
57
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
68
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
79

@@ -13,13 +15,14 @@ public class CacheBustingWebConfig implements WebMvcConfigurer {
1315
private final ResourceVersion version;
1416

1517
@Autowired
16-
public CacheBustingWebConfig(ResourceVersion version) {
18+
public CacheBustingWebConfig(final ResourceVersion version) {
1719
this.version = version;
1820
}
1921

2022
@Override
2123
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
2224
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**")
23-
.addResourceLocations("classpath:/static/");
25+
.addResourceLocations("classpath:/static/")
26+
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic());
2427
}
2528
}

study/src/main/resources/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ server:
77
max-connections: 1
88
threads:
99
max: 2
10+
compression:
11+
enabled: true
12+
min-response-size: 10

study/src/test/java/thread/stage0/SynchronizationTest.java

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
package thread.stage0;
22

3-
import org.junit.jupiter.api.Test;
3+
import static org.assertj.core.api.Assertions.assertThat;
44

55
import java.util.concurrent.Executors;
66
import java.util.concurrent.TimeUnit;
77
import java.util.stream.IntStream;
8-
9-
import static org.assertj.core.api.Assertions.assertThat;
8+
import org.junit.jupiter.api.Test;
109

1110
/**
12-
* 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다.
13-
* 자바는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지한다.
14-
* 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다.
15-
*
16-
* Synchronization
17-
* https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
11+
* 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다. 자바는 공유 데이터에 대한 스레드 접근을
12+
* 동기화(synchronization)하여 경쟁 조건을 방지한다. 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다.
13+
* <p>
14+
* Synchronization https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
1815
*/
1916
class SynchronizationTest {
2017

2118
/**
22-
* 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자.
23-
* synchronized 키워드에 대하여 찾아보고 적용하면 된다.
24-
*
25-
* Guide to the Synchronized Keyword in Java
26-
* https://www.baeldung.com/java-synchronized
19+
* 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자. synchronized 키워드에 대하여 찾아보고 적용하면 된다.
20+
* <p>
21+
* Guide to the Synchronized Keyword in Java https://www.baeldung.com/java-synchronized
2722
*/
2823
@Test
2924
void testSynchronized() throws InterruptedException {
30-
var executorService = Executors.newFixedThreadPool(3);
31-
var synchronizedMethods = new SynchronizedMethods();
25+
final var executorService = Executors.newFixedThreadPool(3);
26+
final var synchronizedMethods = new SynchronizedMethods();
3227

3328
IntStream.range(0, 1000)
3429
.forEach(count -> executorService.submit(synchronizedMethods::calculate));
@@ -41,15 +36,15 @@ private static final class SynchronizedMethods {
4136

4237
private int sum = 0;
4338

44-
public void calculate() {
39+
public synchronized void calculate() {
4540
setSum(getSum() + 1);
4641
}
4742

4843
public int getSum() {
4944
return sum;
5045
}
5146

52-
public void setSum(int sum) {
47+
public void setSum(final int sum) {
5348
this.sum = sum;
5449
}
5550
}

study/src/test/java/thread/stage0/ThreadPoolsTest.java

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package thread.stage0;
22

3-
import org.junit.jupiter.api.Test;
4-
import org.slf4j.Logger;
5-
import org.slf4j.LoggerFactory;
3+
import static org.assertj.core.api.Assertions.assertThat;
64

75
import java.util.concurrent.Executors;
86
import java.util.concurrent.ThreadPoolExecutor;
9-
10-
import static org.assertj.core.api.Assertions.assertThat;
7+
import org.junit.jupiter.api.Test;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
1110

1211
/**
13-
* 스레드 풀은 무엇이고 어떻게 동작할까?
14-
* 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
15-
*
16-
* Thread Pools
17-
* https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
18-
*
19-
* Introduction to Thread Pools in Java
20-
* https://www.baeldung.com/thread-pool-java-and-guava
12+
* 스레드 풀은 무엇이고 어떻게 동작할까? 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
13+
* <p>
14+
* Thread Pools https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
15+
* <p>
16+
* Introduction to Thread Pools in Java https://www.baeldung.com/thread-pool-java-and-guava
2117
*/
2218
class ThreadPoolsTest {
2319

@@ -31,8 +27,8 @@ void testNewFixedThreadPool() {
3127
executor.submit(logWithSleep("hello fixed thread pools"));
3228

3329
// 올바른 값으로 바꿔서 테스트를 통과시키자.
34-
final int expectedPoolSize = 0;
35-
final int expectedQueueSize = 0;
30+
final int expectedPoolSize = 2;
31+
final int expectedQueueSize = 1;
3632

3733
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
3834
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
@@ -46,7 +42,7 @@ void testNewCachedThreadPool() {
4642
executor.submit(logWithSleep("hello cached thread pools"));
4743

4844
// 올바른 값으로 바꿔서 테스트를 통과시키자.
49-
final int expectedPoolSize = 0;
45+
final int expectedPoolSize = 3;
5046
final int expectedQueueSize = 0;
5147

5248
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
@@ -57,7 +53,7 @@ private Runnable logWithSleep(final String message) {
5753
return () -> {
5854
try {
5955
Thread.sleep(1000);
60-
} catch (InterruptedException e) {
56+
} catch (final InterruptedException e) {
6157
throw new RuntimeException(e);
6258
}
6359
log.info(message);

tomcat/src/main/java/handler/RequestHandler.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

tomcat/src/main/java/handler/RequestHandlerMapping.java

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package nextstep;
22

3+
import nextstep.jwp.JwpRequestMapping;
34
import org.apache.catalina.startup.Tomcat;
5+
import org.apache.coyote.handler.RequestMapping;
46

57
public class Application {
68

7-
public static void main(String[] args) {
8-
final var tomcat = new Tomcat();
9+
private static final RequestMapping requestMapping = new JwpRequestMapping();
10+
11+
public static void main(final String[] args) {
12+
final var tomcat = new Tomcat(requestMapping);
913
tomcat.start();
1014
}
1115
}

0 commit comments

Comments
 (0)