Conversation
삽입, 조회시 강제로 대문자로 수정해서, 대소문자를 구분하지 않는 로직을 구현했습니다.
#67 기존 들어온 헤더 필드를 강제로 대문자로 변경 후 저장하는 구조였는데, 기존 테스트 케이스가 깨지고, 예상값도 대문자로 해줘야 하는 불편함이 있었습니다. 따라서, Attribute의 맵에 값을 추가할 때 대소문자 관계없이 해당 원소가 존재하는지 맵을 순차적 O(n)으로 비교하고 없을 경우 추가하는 방식으로 로직을 수정했습니다.
| return attributes.get(k); | ||
| } | ||
| } | ||
| return null; |
There was a problem hiding this comment.
exception
null을 받아야할 이유가 있다면: optional
There was a problem hiding this comment.
getDefault가 있는데 굳이 null을 리턴해줄 필요는 없다고 보여집니다.
꼭 써야한다면 Optional로 제공해주는 것도 고려해볼 수 있을 것 같아요
There was a problem hiding this comment.
그리고 null을 명시적으로 리턴해준다면 get이 null인 케이스도 테스트에서 확인을 해봐야할 것 같습니다!
There was a problem hiding this comment.
이부분 작성하면서도 이상하다 생각했는데 감사합니다!
Dae-Hwa
left a comment
There was a problem hiding this comment.
고생 많으셨어요
생각하기 어려운 로직인데 잘 처리된 것 같네요
| } | ||
|
|
||
| public Attributes add(String key, String value) { | ||
| for (String k : attributes.keySet()) { |
There was a problem hiding this comment.
직접 사용할 변수면 명시적으로 이름을 짓는 것도 좋아보여요
eachKey currentKey 같은거라도 좋을 것 같고
그게 힘들면 매개변수 이름을 targetKey 같은 걸로 나눠줘도 좋을 것 같습니다.
| return attributes.get(k); | ||
| } | ||
| } | ||
| return null; |
There was a problem hiding this comment.
getDefault가 있는데 굳이 null을 리턴해줄 필요는 없다고 보여집니다.
꼭 써야한다면 Optional로 제공해주는 것도 고려해볼 수 있을 것 같아요
| for (String k : attributes.keySet()) { | ||
| if (k.equalsIgnoreCase(key)) { | ||
| return attributes.get(k); | ||
| } | ||
| } | ||
| return defaultValue; |
There was a problem hiding this comment.
코드 중복이 되는데 get이나 getOrDefault 둘 중 하나를 기준으로 중복 제거를 해볼 수 있겠네요
| Attributes statusLineAttributes = new Attributes(); | ||
|
|
||
| statusLineAttributes.put(METHOD_KEY, statusLine.get(0)); | ||
| statusLineAttributes.put(PATH_KEY, statusLine.get(1)); | ||
| statusLineAttributes.put(PROTOCOL_VERSION_KEY, statusLine.get(2)); | ||
| statusLineAttributes.add(METHOD_KEY, statusLine.get(0)); | ||
| statusLineAttributes.add(PATH_KEY, statusLine.get(1)); | ||
| statusLineAttributes.add(PROTOCOL_VERSION_KEY, statusLine.get(2)); |
There was a problem hiding this comment.
이 부분도 Attributes를 만들면서 자연스럽게 검증이 될 테니 Map으로 받아줘도 괜찮을 것 같아요
There was a problem hiding this comment.
그게 아니라 생성자로 Attributes를 넣어주는게 더 바람직하다고 생각하신거면 좋은 방법인 것 같습니다
만약 그렇다면 Map<String, String>을 받는 생성자를 완전히 대체해주는게 좋을 것 같습니다.
| assertAll( | ||
| () -> assertEquals("value", attributes.get("KEY")), | ||
| () -> assertEquals("value", attributes.get("key")), | ||
| () -> assertEquals("value", attributes.get("kEy")) | ||
| ); |
There was a problem hiding this comment.
한 번에 여러개의 케이스를 테스트 하는 것 보다는 매개변수화 시키는게 더 좋을 것 같습니다
There was a problem hiding this comment.
혹시 매개변수화 시켜서 테스트를 하면 어떤 장점이 있는건가요??
아직 정확하게 와닿지 못해서 이런 방식으로 작성했습니다!
There was a problem hiding this comment.
기본적으로 저는 assertAll은 같은 테스트에 대한 그룹을 묶어주는 것이라 생각했습니다. 예를 들어 검증해야 될 객체에 여러개의 필드가 들어있고 각각 검증을 해야 하는 상황이라면 묶어주는게 의미가 있을거라 생각합니다.
하지만, assertAll로 여러개의 테스트 케이스를 묶어주면 여러개의 테스트가 하나의 테스트 안에 들어가있는 느낌이라 좋지 못하다고 생각한 것 같습니다. 기본적으로 하나의 테스트는 한 가지 테스트를 수행하는게 가장 좋다고 알고 있기 때문입니다.
마지막으로 가독성에 대한건 개인 취향이긴 한데, RequestHandlerTest에 있는 케이스들을 저런 식으로 처리한다면 가독성이 매우 떨어질 것 같습니다.
| () -> assertEquals(expectedAttributes.get("key"), attributes.get("KEY")), | ||
| () -> assertEquals(expectedAttributes.get("KEY"), attributes.get("kEY")), | ||
| () -> assertEquals(expectedAttributes.get("kEy"), attributes.get("key")) |
There was a problem hiding this comment.
키가 중복돼서 들어가지 않는 케이스를 추가해주는게 좋을 것 같습니다
| return attributes.get(k); | ||
| } | ||
| } | ||
| return null; |
There was a problem hiding this comment.
그리고 null을 명시적으로 리턴해준다면 get이 null인 케이스도 테스트에서 확인을 해봐야할 것 같습니다!
|
추가적으로 Attributes의 Map을 계속 탐색하는 대신에 Key를 타입으로 분리해볼 수 있을 것 같습니다. 이런식으로 분리하다보면 나중에 Value 파트도 스프링의 MultiValueMap처럼 리스트 타입으로 해야 하거나 하면 쉽게 분리할 수 있을 것 같아요 |
# Conflicts: # src/main/java/webserver/http/statusline/StatusLine.java
# Conflicts: # src/main/java/webserver/http/startline/RequestLine.java # src/test/java/webserver/http/header/RequestHeaderTest.java # src/test/java/webserver/http/header/ResponseHeaderTest.java # src/test/java/webserver/http/startline/RequestLineTest.java # src/test/java/webserver/http/startline/StatusLineTest.java
Dae-Hwa
left a comment
There was a problem hiding this comment.
고생하셨습니다
아쉬운 부분이 있어서요 한 번만 얘기 더 해봐요 ㅎㅎ
| try { | ||
| return get(targetKey); | ||
| } catch (IllegalArgumentException e) { | ||
| return defaultValue; | ||
| } |
There was a problem hiding this comment.
try-catch를 안 쓰는 방법도 있지 않을까요??
| for (String currentKey : attributes.keySet()) { | ||
| if (currentKey.equalsIgnoreCase(targetKey)) { | ||
| return attributes.get(currentKey); | ||
| } | ||
| } | ||
| throw new IllegalArgumentException("일치하는 키가 없습니다."); |
There was a problem hiding this comment.
key에 해당하는 value가 null이라면 제대로 동작하지 않을 것 같아요
이런 경우를 막지 않아도 괜찮을까요?
그리고 예외를 발생시키면서까지 get을 사용해야할 경우가 있을까요?
getOrDefault로는 안되는 경우가 있는건지 궁금합니다
There was a problem hiding this comment.
key: "null",
value: null
인 상태에서 key값으로 "null"을 꺼내면 null을 반환 하는데
제대로 동작하지 않는다 게 어떤 부분인지 잘 모르겠습니다.
There was a problem hiding this comment.
f61aa7f 에서 try-catch를 제거하는 방식으로 수정했습니다.
혹시 생각하신 방식이랑 다르다면 말씀해주세요!
| public Attributes add(String key, String value) { | ||
| attributes.put(key, value); | ||
| public Attributes add(String targetKey, String value) { | ||
| for (String currentKey : attributes.keySet()) { |
| ()-> assertThat(attributes.get("null")).isNull(), | ||
| ()-> assertThat(attributes.get("null2")).isNull() |
There was a problem hiding this comment.
둘 다 null을 확인하는 별개의 테스트케이스 같은데, 따로 묶은 이유가 있으신가요?
| attributes.add("null2", null); | ||
|
|
||
| assertAll( | ||
| ()-> assertThat(attributes.size()).isEqualTo(2), |
There was a problem hiding this comment.
null을 확인하는 테스트 같은데 사이즈를 확인하는 이유가 어떤건가요?
close #67
대소문자를 구분하지 않는 Attributes 를 구현하기 위해
처음 시도했던 방법은 들어온 헤더 필드를 강제로 대문자로 변경 후 저장하는 구조였습니다.
다만, 기존 테스트 케이스가 깨지고, 예상값도 대문자로 해줘야 하는 불편함이 있어 개선이 필요했습니다.
따라서, Attribute의 맵에 값을 추가할 때
대소문자 관계없이 해당 원소가 존재하는지 맵을 순차적으로 비교하고
없을 경우 추가하는 방식으로 로직을 수정했습니다.