-
Notifications
You must be signed in to change notification settings - Fork 309
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
[톰캣 구현하기 3, 4단계] 썬샷(오진택) 미션 제출합니다. #479
Conversation
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.
안녕하세요 썬샷!
바쁜 일상 속에서 매우 빠르게 구현하셨군요...대단합니다
간단하게 코멘트 남겨봤습니다!
반영해주시면 좋을 것 같은 부분은 아래와 같아요.
- Tomcat과 비슷한 Controller, AbstractController로 구조를 바꿔보면 좋을 것 같습니다.
- jwp, apache 패키지가 있는데 비즈니스와 WAS의 기능적인 기준으로 패키지 위치를 바꿔보면 좋을 것 같습니다.
- 전반적으로 상수화를 하거나 열거형으로 휴먼에러를 줄이면 더 가독성이 좋아질 것 같아요.
- 시간적 여유가 되신다면 테스트 코드도 작성해보는 것은 어떨까요...?
원하시는 부분만 반영해서 리뷰 요청 주셔도 좋습니다!
화이팅...!
tomcat/src/main/java/nextstep/jwp/commandcontroller/AbstractController.java
Outdated
Show resolved
Hide resolved
import nextstep.jwp.request.HttpRequest; | ||
import nextstep.jwp.response.HttpResponse; | ||
|
||
public class AbstractController implements Controller { |
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.
미션에서 준 AbstractController
는 Java EE의 HttpServlet을 투영한 것 같아요.
따라서 이번 미션에서는 doPost
, doGet
등도 추가한 구조로 구현해보면 어떨까 싶습니다!
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.
doPost()와 doGet()을 사용하는 방식으로 바꾸어주었습니다!!
@Override | ||
public void service(HttpRequest request, HttpResponse response) throws Exception { | ||
response.setStatusLine(StatusLine.of(request.getHttpVersion(), HttpStatus.NOT_FOUND)); | ||
response.addHeader("Content-Type", ContentType.HTML.getType()); |
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.
헤더 값을 각 컨트롤러에서 하드코딩해주고 있는데 휴먼 에러의 위험이 있지 않을까요??!
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.
Http Header도 메서드나 상태 코드와 같이 enum으로 관리해주도록 변경해주었습니다
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.
비즈니스적인 커스텀 헤더를 추가하려고 하면 ENUM 타입의 KEY를 사용할 때 문제가 생길 수 있을 것 같아요.
Enum 제공 측은 톰캣 같은 범용적인 모듈이기 때문에 개발자가 그 Enum에 추가할 수도 없구요!
이러한 경우는 어떻게 하는 것이 좋을까요??!
public void service(HttpRequest request, HttpResponse response) { | ||
final Map<String, String> logInfo = Arrays.stream(request.getRequestBody().split("&")) | ||
.map(input -> input.split("=")) | ||
.collect(Collectors.toMap(info -> info[0], info -> info[1])); | ||
|
||
response.setStatusLine(StatusLine.of(request.getHttpVersion(), HttpStatus.FOUND)); | ||
final Optional<User> savedUser = InMemoryUserRepository.findByAccount(logInfo.get("account")); | ||
if (savedUser.isPresent()) { | ||
final User user = savedUser.get(); | ||
if (user.checkPassword(logInfo.get("password"))) { | ||
response.addHeader("Location", "index.html"); | ||
final Session session = new Session(UUID.randomUUID().toString()); | ||
session.setAttribute("user", user); | ||
SessionManager.add(session); | ||
response.addCookie("JSESSIONID", session.getId()); | ||
return; | ||
} | ||
} | ||
response.addHeader("Location", "401.html"); | ||
} | ||
} |
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.
이 부분도 휴먼 에러를 줄이고, 분기를 줄이는 식으로 리팩토링할 수 있을 것 같아요!
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.
메서드 분리하고 상수화 좀 해주긴 했는데 만족스럽게 된 것 같진 않네요
private static final String DELIMITER = "\r\n"; | ||
private final Map<String, String> cookies = new LinkedHashMap<>(); | ||
|
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.
상수와 객체는 개행해줘도 좋을 것 같아요!
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.
감사합니다~
} | ||
|
||
public void addCookie(final String name, final String value) { | ||
cookies.save(name, value); | ||
} | ||
|
||
public String toResponse() { |
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.
httpResponse.toResponse()
라고 호출할 것 같은데 조금 어색하다고 생각이 됩니다!
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.
HttpResponse를 String으로 바꾸는 거라 toString()을 오버라이드할까도 생각해봤지만, toString()의 의도와 다르게 쓰이는 것 같기도 해서 convertToString()
으로 바꾸어주었는데 어떤가요?
시간이 좀 더 있었으면 테스트코드를 짰을 텐데 너무 늦게 보내면 안 되니 나머지들만 반영했어요... |
Kudos, SonarCloud Quality Gate passed! 0 Bugs 0.0% Coverage The version of Java (11.0.20.1) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17. |
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.
쉽지 않은 리팩토링 과정이였을 것 같은데 썬샷 고생하셨습니다!!
개발자의 관심과 HTTP 처리용 컴포넌트들을 잘 분리하신 것 같아요.
최대한 도움되도록 리뷰를 남기려고 했는데 도움이 되셨을 지는 모르겠네요ㅠㅠ
추가로 생각해봤으면 하는 내용들을 코멘트로 남겨봤습니다.
원하시면 머지한 이후에도 코멘트나 오프라인으로 같이 고민해볼 수 있을 것 같아요!!
protected void doPost(HttpRequest request, HttpResponse response) throws Exception { /* NOOP */ } | ||
protected void doGet(HttpRequest request, HttpResponse response) throws Exception { /* NOOP */ } | ||
|
||
protected String readResponseBody(final String requestUri) throws IOException, URISyntaxException { |
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.
공통적으로 사용할 수 있게 만들기 위해서 추상 클래스에 만들어뒀군요!
IS-A 관계를 잘 사용하신 것 같아요!
final Optional<User> savedUser = findUserByLogIn(authInfo); | ||
if (savedUser.isPresent()) { | ||
final User user = savedUser.get(); | ||
response.addHeader(HttpHeader.LOCATION.getName(), "index.html"); |
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.
index.html
등의 정적 자원 이름이 바뀌면 여기저기 다 수정해줘야 할 것 같아요.
수정하지 않아도 컴파일 타임에서 오류가 나는 것도 아니구요!
어떻게 관리하면 좋을 지 궁금해요.
response.addHeader(HttpHeader.LOCATION.getName(), "401.html"); | ||
} | ||
|
||
private Optional<User> findUserByLogIn(final Map<String, String> logInfo) { |
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.
비즈니스 로직을 서비스 레이어로 분리하면 재사용성이 훨씬 높아지겠군요!
private AbstractController errorPageController; | ||
|
||
public RequestMapping(final List<AbstractController> controllers, final AbstractController errorPageController) { | ||
this.controllers.addAll(controllers); |
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.
의존성을 생성자 주입하고 있는데 할당하는 방식이 아닌 기존 상태를 변경하는 이유가 있을까요?
public class RequestMapping implements Controller { | ||
|
||
private final List<AbstractController> controllers = new ArrayList<>(); | ||
private AbstractController errorPageController; |
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.
재할당을 허용하는 이유가 궁금합니다!
import org.apache.coyote.request.HttpRequest; | ||
import org.apache.coyote.response.HttpResponse; | ||
|
||
abstract class AbstractController implements Controller { |
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.
이 추상 클래스는 HttpServlet
과 비슷한 것 같아요.
명세에 가깝게 느껴져서 패키지 위치가 웹앱 쪽이 맞을 지 의문입니다!
outputStream.flush(); | ||
} catch (IOException | UncheckedServletException | URISyntaxException e) { | ||
} catch (Exception e) { |
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.
타이트한 처리를 최상위 예외로 변경하셨군요!
아마 시간이 부족해서 그렇지 않았을까 생각해봅니다.
- 정의하신 커스텀 예외들을 어떤 용도로 사용하면 좋을 지
- 어떤 구조로 처리할 수 있을 지
- 위의 두 가지 모두 아니라면 커스텀 예외를 쓰는 의미는 무엇일 지
생각해보면 좋을 것 같아요!
이번 리뷰도 잘 부탁드려요 연어!!
1, 2단계 때부터 3단계의 내용을 일부 고려하면서 해서 (다들 그렇겠지만) 1, 2단계보다는 좀 더 쉬웠던 거 같네요
Request를 이용해서 적절한 Handler를 매핑하도록 하는 부분이 재밌었던 거 같아요. 원래 예시로 주어졌던
AbstractController
에는 doGet()이나 doPost() 메서드가 있어서 Http 메서드에 따라 호출하는 거 같은데 처음에 '이 메서드들은 무슨 역할이지?' 생각만 하고 찾아보지는 않아서 현재 구조가 되었습니다.스레드나 동시성 같은 부분은 학습 테스트를 안 하고 힌트만 보고 구현해서 아직 이해가 부족한 채로 구현한 상태입니다.