Skip to content
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

[HTTP 서버 구현하기 - 2, 3단계] 검프(김태정) 미션 제출합니다. #79

Merged
merged 27 commits into from
Sep 2, 2021

Conversation

Livenow14
Copy link

안녕하세요 에드!
두번째 PR이네요 ㅎㅎ

고민한부분

Http요청에대해 어떻게 처리 할 것인지 오래동안 고민했어요.

FrontController를 통해 FrontController가 전반적인 요청과 응답에 관여하게 했어요.
많은 구조 변경으로 길을 잃을뻔 했지만, 겨우 돌아왔어요 ㅎㅎ

HttpSession의 생명주기

HttpRequest와 HttpResponse에서 HttpSession이 어디까지 관여를 해야할까 고민했어요.
처음엔 HttpRequest에만 뒀다가 이후에 HttpResponse에 HttpRequest 의존관계를 형성함으로써 요청과 응답에 생명을 가지게 했어요.

이번 미션에 테스트를 작성하는 부분이 정말 힘들었어요. 어떻게든 작성해보며, 어떤 테스트를 해야하고 어떤 테스트를 하지 말아야 하는지 나름의 이류를 찾았어요. (최소 구현사항에서 사용되는 로직을 테스트 하는 것을 우선시한다. )

이번에도 잘 부탁드립니다!

@Livenow14 Livenow14 self-assigned this Sep 2, 2021
Copy link

@sjpark-dev sjpark-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 검프! 🙂

HttpServer와 구성 요소들을 객체지향적으로 정말 잘 설계를 하셨네요. 👍👍
그리고 든든한 테스트 코드까지 👍
리뷰하면서 많이 배워갑니다!

몇가지 의견을 남겼는데 궁금하신 점 있으면 언제든지 DM주세요.

app/build.gradle Outdated
Comment on lines 55 to 56
minimum = 0.90
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 커버리지 90% 👍👍👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백을 통해 95%까지 올릴 수 있었습니다! 감사합니다 😊

Comment on lines 34 to 39
if (Objects.isNull(controller)) {
httpResponse.setHttpStatusCode(HttpStatusCode.NOT_FOUND);
errorResolver(httpRequest, httpResponse);
return;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 경우를 위해서 디폴트 컨트롤러를 만들어서 활용하는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI에 관련된 부분만 Controller에서 넣고싶어서, 현재와 같이 작성을 했는데요,

DefaultController를 두어 해결할 수 있었네요! 감사합니다 ㅎㅎ

5656618


private static final String HEADER_DELIMITER = ": ";

private final BufferedReader bufferedReader;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bufferedReader는 초기화 할 때만 쓰이는데 필드로 가지고 있을 필요가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 방법인 것 같습니다 😊

this.requestLine = requestLine;
this.httpHeaders = httpHeaders;
this.parameters = parameters;
this.httpSession = HttpSessions.getSession(httpHeaders.getSessionId());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

httpSession이 null이 될 수 있네요.
이 경우 getHttpSession 메소드를 거쳐야 비로소 httpSession이 초기화 되네요.
null보다는 default 값을 활용하는 것은 어떨까요?

Copy link
Author

@Livenow14 Livenow14 Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확실히 null에 열러있다보니 위험해 보였던거 같아요!
좋은 피드백 감사합니다 ㅎㅎㅎ

515179a


import java.util.Optional;

public class UserService {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login과 register를 동시에 처리하는 UserService 좋네요. 👍
저는 왜 이 생각을 못했을까요? 😂

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜냐면 에드는 더 먼수를 보니까..?

Comment on lines +32 to 39
final HttpRequest httpRequest = new HttpRequest(new HttpMessageReader(inputStream));
final HttpResponse httpResponse = new HttpResponse(httpRequest);

if ("/".equals(extractedUri)) {
final String response = http200Message(DEFAULT_RESPONSE_BODY, ContentType.HTML.getContentType());
writeOutputStream(outputStream, response);
return;
}

if (extractedUri.endsWith(".html")) {
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri, ContentType.HTML));
return;
}

if (extractedUri.endsWith(".css")) {
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri, ContentType.CSS));
return;
}

if (extractedUri.endsWith(".js")) {
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri, ContentType.JAVASCRIPT));
return;
}

if (extractedUri.startsWith("/assets/img")) {
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri, ContentType.IMAGE));
return;
}

if ("/login".equals(extractedUri)) {
if ("POST".equals(extractedMethod)) {
try {
final String requestBody = extractRequestBody(bufferedReader, httpRequestHeaders);
loginRequest(requestBody);
writeOutputStream(outputStream, http302Response("/index.html"));
} catch (RuntimeException exception) {
writeOutputStream(outputStream, http302Response("/401.html"));
}
return;
}
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri + ".html", ContentType.HTML));
return;
}

if ("/register".equals(extractedUri)) {
if ("POST".equals(extractedMethod)) {
try {
final String requestBody = extractRequestBody(bufferedReader, httpRequestHeaders);
registerRequest(requestBody);
writeOutputStream(outputStream, http302Response("/index.html"));
} catch (RuntimeException exception) {
writeOutputStream(outputStream, http302Response("/401.html"));
}
return;
}
writeOutputStream(outputStream, httpStaticFileResponse(STATIC_PATH, extractedUri + ".html", ContentType.HTML));
return;
}

writeOutputStream(outputStream, http302Response("/404.html"));
final FrontController frontController = new FrontController();
frontController.service(httpRequest, httpResponse);
outputStream.write(httpResponse.getHttpMessage().getBytes());
outputStream.flush();
} catch (IOException exception) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔! 👍

Comment on lines +12 to +13
public class HttpMessageReaderGetStub extends HttpMessageReader {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 stub까지 와우...
많이 배웁니다. 👍👍👍

//then
Assertions.assertThat(httpCookie.toValuesString()).isEqualTo("tasty_cookie=strawberry; JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46");
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 클래스 전반적으로 EOF가 없는 경우가 많습니다.
EOF가 있으면 좋은 이유

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자동 완성에 의존하다보니, 개행을 신경쓰지 못했네요
꼼꼼한 피드백 감사합니다 :)

@Livenow14
Copy link
Author

안녕하세요 에드!!
두번째 피드백 요청이에요. ㅎㅎㅎ
에드의 피드백으로 생각지도 못한 부분들에서 많은 최적화를 이룰 수 있었어요! 고마워요
이번에도 잘 부탁드립니다 ㅎㅎ

Copy link

@sjpark-dev sjpark-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검프~

Default 컨트롤러와 HttpSession 초기화까지 깔끔하게 구현하셨네요! 👍👍👍
몇가지 사소한 코멘트 남겼습니다. 🙂


public void service(HttpRequest httpRequest, HttpResponse httpResponse) {
final String requestPath = httpRequest.getPath();
final Controller controller = controllerMap.getOrDefault(requestPath, new RedirectController());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default controller 👍

Comment on lines +27 to +37
public HttpRequest(RequestLine requestLine, HttpHeaders httpHeaders, Parameters parameters) {
this(requestLine, httpHeaders, parameters, HttpSessions.getSession(httpHeaders.getSessionId()));
}

public HttpRequest(RequestLine requestLine, HttpHeaders httpHeaders, Parameters parameters, HttpSession httpSession) {
this.requestLine = requestLine;
this.httpHeaders = httpHeaders;
this.parameters = parameters;
this.httpSession = initializeSession(httpSession);
}

Copy link

@sjpark-dev sjpark-dev Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세션이 없다면 만드는 로직을 이렇게 깔끔하게 구현 할 수가 있네요 👍👍👍

@@ -89,7 +96,24 @@ void mainPage() {
}

@Test
void registerRequest() {
void 유저_로그인_테스트(){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestHandlerTest에서 이 테스트 메소드를 제외하고는 모두 실패합니다. 😢

나머지도 여기처럼 expected에 Set-Cookie 헤더를 넣으면 될 것 같아요!

Copy link
Author

@Livenow14 Livenow14 Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리스폰스 부분에서 생긴 예외였어요!!
구현 로직 수정 후 예외 해결 완료했습니다 😊

Comment on lines 10 to 14
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는 import!

@Livenow14
Copy link
Author

구현 로직 수정 및 코드 스멜 해결 완료햇습니다!! 감사합니다 🙇🏻‍♂️

@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 2, 2021

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

95.5% 95.5% Coverage
0.0% 0.0% Duplication

Copy link

@sjpark-dev sjpark-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검프~!! 마지막까지 피드백 반영하느라 정말 수고하셨습니다. 👍

1단계부터 3단계까지 검프의 클린 코드를 읽을 수 있어서 너무 좋았습니다.
리뷰를 하면서 정말 많은 인사이트를 얻었습니다. 감사합니다. 🙏

다음 미션도 화이팅~!!

@sjpark-dev sjpark-dev merged commit 952977a into woowacourse:livenow14 Sep 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants