-
Notifications
You must be signed in to change notification settings - Fork 310
[1단계 - Tomcat 구현하기] 낙낙(이낙헌) 미션 제출합니다. #561
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
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.
안녕하세요 낙낙쓰~~~😀 러쉬입니다🏈 1단계임에도 불구하고, 클래스별 책임분리가 매우 잘 되어있군요ㅎㅎ 보고 배울점이 많은 코드였습니다. 아마 나머지 단계는 금방 구현하실 것 같아요.
간단한 질문사항만 남겨두습니다.
다음 코드까지 진행한 뒤 이야기할 것이 많아질 것 같아서 바로 Merge 드립니다.
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
WebContentInterceptor noCacheInterceptor = new WebContentInterceptor(); |
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.
미리 구현된 인터셉터를 활용하셨군요👍🏿
@HandlebarsHelper | ||
public class VersionHandlebarsHelper { | ||
@Component | ||
public class VersionHandlebarsHelper implements Helper<Object> { |
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.
기존 Handlebars가 작동하지 않아, 어떻게 해결해야하는지 궁금했는데 배워갑니다ㅎㅎ
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.
해당 부분은 #520 에서 참고했습니다!!
출처는 페드로에욥 ㅎㅎ
@@ -67,7 +66,9 @@ private void process(final Socket connection) { | |||
return; | |||
} | |||
var processor = new Http11Processor(connection); | |||
new Thread(processor).start(); | |||
Thread thread = new Thread(processor); | |||
thread.setDaemon(true); |
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.
우선 확인해보니 스레드는 자신을 생성한 스레드의 데몬 상태를 상속한다고 합니다!
따라서 setDaemon(true)
를 안 해줘도, 자동으로 데몬 스레드가 되겠네요 😅
불필요한 코드였습니다!
제가 데몬 스레드로 설정하고자 했던 의도는 다음과 같습니다!
JVM은 일반 스레드가 하나라도 존재하면 종료되지 않습니다.
즉 메인 스레드가 종료되어도 다른 일반 스레드가 살아 있다면 JVM은 종료하지 않고 계속 실행됩니다.
따라서 Http11Processor
스레드가 일반 스레드였다면 메인 스레드가 죽어도 JVM이 죽지 않아 모든 로직을 다 실행할 것이고, 저는 이 상황이 정상 상황이 아니라고 생각하였습니다. 메인 스레드가 죽는 경우는 예외 상황이라고 생각했거든요!
저희 코드에서 테스트 해보니 메인 스레드를 inturrupt 로 갑자기 죽였을 때, ServerSocket을 close 하지 못해서 Http11Processor
스레드가 계속 살아있더라구요.
따라서 데몬 스레드로 설정하려고 하였습니다!
} | ||
|
||
public static String probeContentType(Path path) throws IOException { | ||
String mimeType = Files.probeContentType(path); |
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.
ContentType을 찾아주는 메서드를 활용하셨군요👍🏿
public class HttpRequestInfo { | ||
|
||
private final HttpMethod method; | ||
private final String urlPath; |
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.
HttpUrl 이라는 클래스를 두셨던데, 여기에서 활용하지 않는 이유가 있는지 궁금합니다!
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.
HttpRequestInfo
는 HttpHandlerMapper
에서 key 로 활용하기 위해 만든 클래스입니다!
어떤 핸들러를 호출할지 결정하는 것은 요청의 url path + method 이기 때문에 위와 같이 하나의 객체로 묶었는데,
HttpUrl
에는 쿼리 파라미터도 포함되어 있기 때문에 여기서 활용하지 않았습니다..!
다만 러쉬가 말씀해 주신대로 Info 라는 이름은 명확하게 의도를 드러내지 못하는 것 같네요!!
리팩토링 하면서 더 좋은 이름을 지어 보겠습니닷!!
private final HttpMethod method; | ||
private final HttpUrl url; |
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.
이부분도 HttpRequestInfo라는 클래스를 활용하지 않은 이유가 있는지 궁금합니다ㅎㅎ
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.
위에서 말씀 드린대로 HttpRequestInfo
는 HttpHandlerMapper
의 key로 활용하기 위해서 였습니다..!
|
||
import java.util.Objects; | ||
|
||
public class HttpRequestInfo { |
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.
Info
라는 네이밍은 흔히 의미가 불분명한 불용어라고 알려져있는데요.
Avoid words like Manager, Processor, Data, or Info in the name of a class. - (출처. 클린코드)
만약 해당 클래스를 HttpRequest에서도 활용하게 된다면, rfc 공식문서에서 사용하는 용어를 사용해보는건 어떨까 제안드려요~
List<String> headerLines = new ArrayList<>(); | ||
|
||
String headerLine = bufferedReader.readLine(); | ||
while (!headerLine.isEmpty()) { |
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.
while (!headerLine.isEmpty()) { | |
while ((headerLine = bufferedReader.readLine()) != null && !headerLine.isEmpty()) { |
null 검사도 추가하면, 예상치 못한 상황에 대해서도 안전하게 파싱할 수 있을 것 같습니다ㅎㅎ
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 java.util.List; | ||
import org.apache.coyote.http11.message.HttpHeaders; | ||
|
||
// TODO: BufferedReader와 BufferedInputStream 같이 사용했을 때 문제가 발생하지 않는지 확인하기 |
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.
BufferedInputStream를 같이 사용하신 이유가 있을까요?? BufferedReader만을 활용해서, body까지 파싱할 수 있을 것 같아서요!
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.
BufferedReader
는 문자 단위로 읽기 때문에, read 메소드에 전달되는 len이 바이트의 길이가 아닌 문자의 길이를 의미합니다.
그런데 UTF-8 인코딩에서 한글이나 특정 특수문자는 문자 하나당 2~4바이트의 크기를 가지는데요!
이 때문에 문자의 길이와 바이트의 길이가 같지 않는 문제가 발생하게 됩니다.
다른 인코딩 방식을 사용할 때에도 문제가 되구요!
따라서 원하는 크기의 바이트만 읽을 수 있는 BufferedInputStream
을 사용하게 되었습니다.
물론 BufferedReader
를 사용해서 문자를 하나씩 읽고 해당 문자가 가지는 바이트 크기를 계산하면, Content-Length 만큼 읽는 것이 가능하긴 합니다. 하지만 계산 방식도 복잡하고 매번 문자를 하나씩만 읽어오는 것이 성능 상으로도 좋지 않을 것 같아 BufferedInputStream
을 사용하기로 결정하였습니다.
또한 문자가 아닌 바이너리 데이터가 바디에 담기는 경우도 고려했구요!
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.
하지만 확인해 보니 BufferedReader
와 BufferedInputStream
을 같이 사용하면 한 쪽 버퍼에 미리 담기게 되어 문제가 발생하더라구요!
따라서 전부 BufferedInputStream
을 사용하는 쪽으로 바꾸게 될 것 같습니다..!
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.
오 해당 내용은 처음 알게된 내용이네요~ UTF-8 인코딩에서는 문제가 될 수 있겠군요ㅎㅎ
안녕하세요 러쉬쓰~~~~
DM으로는 2단계까지 구현한다 했지만, 1단계까지밖에 구현하지 못했습니다..!
다만 구현하다보니 3단계의 일부분을 구현해서, 이후의 PR 양이 엄청 많아지지는 않을 것 같습니다.
보시면서 의문이 드시는 부분이나, 개선할만한 부분이 있다면 편하게 리뷰 주세요!!