-
Notifications
You must be signed in to change notification settings - Fork 310
[톰캣 구현하기 - 3, 4단계] 준팍(박준현) 미션 제출합니다. #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
757aa2a
15b7437
4cb9a8c
08af0cb
aa667c6
a721f0b
791fd98
41582a5
782c35b
4a06f65
af1bc42
03a14a7
8be2ead
030dd63
aa6b77c
b7a3fda
a8228d2
08ab904
dd29b24
fda4afa
b4c483d
727135f
04f7da6
7fe6019
08b0e2f
7b7d82a
a21062c
ce3b483
9cf7608
7d8ddae
09d4a9c
b78dbdb
1bd761c
8dea78c
61a7a60
4a830e2
5479d54
e1e9eb9
2407c89
56ee2a9
10c25e9
fb494d3
a29c816
42f9516
189b083
fec6b10
ecf241a
c09714c
d8463a2
c0ba83b
07d5d93
2691b61
a1a5dc5
4b1cfd0
265a9e7
e7ac707
fe30e41
45cb835
8f06001
ba6a311
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,14 @@ | ||
# 톰캣 구현하기 | ||
|
||
## 1단계 - HTTP 서버 구현하기 | ||
- [x] GET /index.html 응답하기 | ||
- [x] CSS 지원하기 | ||
- [x] Query String 파싱 | ||
## 3단계 - 리팩터링 | ||
- [x] HttpRequest 클래스 구현하기 | ||
- [x] HttpResponse 클래스 구현하기 | ||
- [x] Controller 인터페이스 추가하기 | ||
- [x] AbstractController를 상속한 구현체 만들기 | ||
|
||
## 2단계 - 로그인 구현하기 | ||
- [x] HTTP Status Code 302 | ||
- [x] 로그인 성공 시, 응답 헤더에 http status code를 302로 반환하고 `/index.html`로 리다이렉트 | ||
- [x] 로그인 실패 시, `401.html`로 리다이렉트 | ||
- [x] POST 방식으로 회원가입 | ||
- [x] `http://localhost:8080/register` 으로 접속하면 회원가입 페이지(`register.html`)를 보여준다. | ||
- [x] 회원가입 페이지를 보여줄 때는 GET을 사용한다. | ||
- [x] 회원가입을 버튼을 누르면 HTTP method를 GET이 아닌 POST를 사용한다. | ||
- [x] 회원가입을 완료하면 `index.html`로 리다이렉트한다. | ||
- [x] 로그인 페이지도 버튼을 눌렀을 때 GET 방식에서 POST 방식으로 전송하도록 변경하자. | ||
- [x] Cookie에 JSESSIONID 값 저장하기 | ||
- [x] 서버에서 HTTP 응답을 전달할 때 응답 헤더에 `Set-Cookie`를 추가하고 `JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46` 형태로 값을 전달하면 | ||
클라이언트 요청 헤더의 `Cookie` 필드에 값이 추가된다. | ||
- [x] Cookie 클래스를 추가하고 HTTP Request Header의 Cookie에 `JSESSIONID`가 없으면 HTTP Response Header에 `Set-Cookie`를 반환해주는 기능을 | ||
구현한다. | ||
- [x] Session 구현하기 | ||
- [x] 쿠키에서 전달 받은 JSESSIONID의 값으로 로그인 여부를 체크할 수 있어야 한다. | ||
- [x] 로그인에 성공하면 Session 객체의 값으로 User 객체를 저장해보자. | ||
- [x] 로그인 상태에서 `/login` 페이지에 HTTP GET method로 접근하면 `index.html` 페이지로 리다이렉트 처리한다 | ||
## 4단계 - 동시성 확장하기 | ||
- [x] Executors로 Thread Pool 적용하기 | ||
- [x] Connector 클래스에서 Executors 클래스를 사용해서 ExecutorService 객체 생성하기 | ||
- [x] maxThreads라는 변수로 스레드 갯수 지정하기 | ||
- [x] 동시성 컬렉션 사용하기 | ||
- [x] SessionManager 클래스에서 Session 컬렉션에 동시성 컬렉션 적용하기 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,23 @@ | ||
package cache.com.example.cachecontrol; | ||
|
||
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 CacheWebConfig implements WebMvcConfigurer { | ||
|
||
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
CacheControl cacheControl = CacheControl | ||
.noCache() | ||
.cachePrivate(); | ||
|
||
WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); | ||
webContentInterceptor.addCacheMapping(cacheControl, "/"); | ||
|
||
registry.addInterceptor(webContentInterceptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,23 @@ | ||
package cache.com.example.etag; | ||
|
||
import cache.com.example.version.CacheBustingWebConfig; | ||
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<>( | ||
new ShallowEtagHeaderFilter()); | ||
filterRegistrationBean.addUrlPatterns( | ||
"/etag", | ||
CacheBustingWebConfig.PREFIX_STATIC_RESOURCES + "/*" | ||
); | ||
|
||
return filterRegistrationBean; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.common.HttpStatus; | ||
import org.apache.coyote.http11.controller.Controller; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
public class DefaultController implements Controller { | ||
|
||
@Override | ||
public void service(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
httpResponse.setHttpStatus(HttpStatus.OK) | ||
.setPath("/default"); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import nextstep.jwp.model.User; | ||
import nextstep.jwp.service.UserService; | ||
import org.apache.coyote.http11.common.HttpCookie; | ||
import org.apache.coyote.http11.common.HttpStatus; | ||
import org.apache.coyote.http11.controller.AbstractController; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.request.RequestBody; | ||
import org.apache.coyote.http11.request.RequestLine; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.session.HttpSession; | ||
|
||
public class LoginController extends AbstractController<UserService> { | ||
|
||
public LoginController(UserService userService) { | ||
super(userService); | ||
} | ||
|
||
@Override | ||
protected void doGet(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. QueryString으로 로그인 요청하는 것에 대한 처리 로직이 누락 된 것 같아요. 의도하신 걸까요 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저번 리뷰에서 지우겠다고 말씀드렸었는데... 초큼 섭섭할지도..?ㅠ
위 두 가지 이유로 인해 해당 로직 삭제했습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry bro... |
||
RequestLine requestLine = httpRequest.getRequestLine(); | ||
HttpSession session = httpRequest.getSession(false); | ||
|
||
if (session != null && session.getAttribute("user") != null) { | ||
httpResponse.setHttpStatus(HttpStatus.FOUND) | ||
.setPath("/index"); | ||
return; | ||
} | ||
|
||
httpResponse.setHttpStatus(HttpStatus.OK) | ||
.setPath(requestLine.getPath()); | ||
} | ||
|
||
@Override | ||
protected void doPost(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
RequestBody requestBody = httpRequest.getRequestBody(); | ||
|
||
String account = requestBody.get("account"); | ||
String password = requestBody.get("password"); | ||
|
||
try { | ||
User user = service.login(account, password); | ||
|
||
HttpSession httpSession = httpRequest.getSession(true); | ||
httpSession.setAttribute("user", user); | ||
|
||
httpRequest.addSession(httpSession); | ||
|
||
HttpCookie httpCookie = HttpCookie.create(); | ||
httpCookie.putJSessionId(httpSession.getId()); | ||
|
||
httpResponse.setHttpStatus(HttpStatus.FOUND) | ||
.setPath("/index") | ||
.setCookie(httpCookie); | ||
} catch (IllegalArgumentException e) { | ||
httpResponse.setHttpStatus(HttpStatus.FOUND) | ||
.setPath("/401"); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.common.HttpStatus; | ||
import org.apache.coyote.http11.controller.Controller; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
public class NotFoundController implements Controller { | ||
|
||
@Override | ||
public void service(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
httpResponse.setHttpStatus(HttpStatus.NOT_FOUND) | ||
.setPath("/404"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import nextstep.jwp.service.UserService; | ||
import org.apache.coyote.http11.common.HttpStatus; | ||
import org.apache.coyote.http11.controller.AbstractController; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.request.RequestBody; | ||
import org.apache.coyote.http11.request.RequestLine; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
public class RegisterController extends AbstractController<UserService> { | ||
|
||
public RegisterController(UserService userService) { | ||
super(userService); | ||
} | ||
|
||
@Override | ||
protected void doGet(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
RequestLine requestLine = httpRequest.getRequestLine(); | ||
|
||
httpResponse.setHttpStatus(HttpStatus.OK) | ||
.setPath(requestLine.getPath()); | ||
} | ||
|
||
@Override | ||
protected void doPost(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
RequestBody requestBody = httpRequest.getRequestBody(); | ||
|
||
String account = requestBody.get("account"); | ||
String password = requestBody.get("password"); | ||
String email = requestBody.get("email"); | ||
|
||
service.save(account, password, email); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.common.HttpStatus; | ||
import org.apache.coyote.http11.controller.Controller; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
public class UnauthorizedController implements Controller { | ||
|
||
@Override | ||
public void service(HttpRequest httpRequest, HttpResponse httpResponse) { | ||
httpResponse.setHttpStatus(HttpStatus.UNAUTHORIZED) | ||
.setPath("/401"); | ||
} | ||
|
||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Controller를 구현한 클래스와, AbstractController를 구현한 클래스를 어떤 기준으로 나누셨는지 궁금해요 !
정적 파일을 서빙하는 컨트롤러는
Controller
를 구현한게 맞나요 ? (뷰 리졸버 느끼이이임?)(ViewResolver가 삭제된 이력이 있어서 유추해봤습니다 ㅋ.ㅋ.ㅋ)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일단 제가 Controller와 AbstractController를 나눈 기준은 비즈니스 로직이 필요한지 여부 입니당.
단순히 파일만 서빙하는 경우는 doGet()이나 doPost()를 사용할 필요가 없기 때문에 Controller를 구현했고,
비즈니스 로직이 필요한 경우 (get과 post의 분기가 필요한 경우)에는 AbstractController로 구현했습니다.
정적 파일을 서빙하는 컨트롤러는
ResourceController
라는 네이밍으로 coyote 패키지 내부에 위치하고 있습니다.뷰 리졸버는 Tomcat 미션에 적절하지 않은 네이밍이라고 생각하여 Renderer라는
Renderer라는 네이밍으로 변경하고, 내부에 로직을 더 정리했습니다.
ResourceController라는 네이밍은 좀 더 명시적으로 수정하겠습니당