Replies: 8 comments
-
티키 : 계층의 명확한 분리를 위해서는 Controller가 요청으로 받는/응답으로 뱉는 dto, Service에서 요청/응답 dto가 서로 나뉘어야 하지만, 두 Dto의 파라미터 집합이 서로 동일해 한 쪽으로 흡수되었다고 이해함 타카 : 링크 : woowacourse/spring-roomescape-member#61 (comment) 상황 : 인증 객체를 Request 객체에 저장하는 상황 리뷰어 : 현재 어플리케이션 규모에서 ThreadLocal 혹은 @requestScope를 사용하지 않아도 된다는 것에는 동의하는데 HttpServletRequest를 컨트롤하는 것은 지양하는 편이 좋아요. HttpServletRequest는 실제 request에서 들어오는 값들이 담긴 것으로, 여기서 뭔가 값을 추가하게되면 실제 http request에서 더 추가가 되는 것이기 때문에 이걸 만약에 쓸 수도 있는 다른 객체에서 request를 오인할 수 있어요. 그리고 HttpServletRequest와 같은 클래스들을 커스텀하다 에러 트러블슈팅할 일이 생기게 될 수 있는데 그때보면 HttpServletRequest에서 값을 읽거나 쓰는 동작이 왜 지양되는지 확인할 수 있을 것 같아요. 그래서 반영하지 않아도된다라는 뜻은 저 두가지가 아닌이상 사실 반복 로직을 타는게 낫다라는 생각입니다. |
Beta Was this translation helpful? Give feedback.
-
reviewee: GET은 body를 가질 수 없나요? 만약 그렇다면, 일반적으로 RequestParam을 사용하나요? reviewer: 리소스를 구체적으로 url 에 명시하면
요청 데이터를 body에 숨기게 되면
이런 차이가 있어요. 그래서 용도에 맞게 사용하는 것이 좋은데요. reviewee:
link: woowacourse/spring-roomescape-member#3 (comment) reviewee: date, varchar 중 어떤 것을 사용할지에 대한 기준이 있으신가요? reviewer:
link: woowacourse/spring-roomescape-member#81 (comment) dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
} reviewer: reviewee:
'io.jsonwebtoken:jjwt-api' 만 implemets로 선언하고 나머지 의존성은 runtimeOnly로 선언된 이유는 다음과 같습니다. link: woowacourse/spring-roomescape-member#100 (comment) reviewer: 그래서 일관된 데이터가 필요할 경우 한꺼번에 조인을 통해서 가져오는 것이 더 적절한 상황이 있을수도 있을 것 같아요 예를 들면 방탈출 예약서비스 일 때 조회가능한 시간을 먼저 조회하고, 테마를 조회하고~.. 또 다른 걸 조회하고 있을 때 |
Beta Was this translation helpful? Give feedback.
-
// Reservation 객체 안에서 BadRequestException 반환하는 상황
private void validateName(String name) {
if (name == null || name.isBlank()) {
throw new BadRequestException("예약자 이름은 비어있을 수 없습니다.");
}
} reviewer : 도메인에서 이 예외가 "요청"이라는 걸 알 필요가 있을까요? link: woowacourse/spring-roomescape-member#21 (comment) reviewee: 잘못된 요청(bad request)에 대한 예외들 역시 로그로 남겨야 하는 이유가 무엇인가요 ?? 처음에는 서버 애플리케이션의 문제가 아니라면 예외에 대한 로그를 기록할 필요가 없다고 생각하여 남기지 않았는데 로그를 남기는 게 좋은 이유가 궁금합니다! reviewer: 서비스를 운영하다보면 고객 cs가 들어올 때도 있고, 버그가 있는 경우 평소보다 에러가 늘기도 합니다. 이런 경우에 로그가 없다면 그 원인을 추적하기 쉽지 않겠죠. link: woowacourse/spring-roomescape-member#21 (comment) @Transactional
public void delete(Long id) {
ReservationTime reservationTime = reservationTimeRepository.findById(id)
.orElseThrow(() -> new NotFoundException("해당 ID의 예약 시간이 없습니다.")); reviewer: delete 동작의 핵심을 "지우는" 것이 아닌 "자원이 없는 상태"라고 생각한다면 에러를 던지지 않고 메서드를 종료할 수도 있겠네요. link: woowacourse/spring-roomescape-member#21 (comment) reviewer: Spring validation을 사용한 것과 도메인에서 검증하는 것은 어떤 차이가 있을까요? reviewee: 우선 Spring validation은 비즈니스 계층 전에 빠르게 검증할 수 있습니다. Spring validation을 거친 요청 데이터를 더 비즈니스 계층에서 전반적으로 안전하게 사용할 수 있습니다. 다만 요청이 아니더라도 도메인을 생성할 수 있기 때문에 항상 안전한 도메인을 생성하기 위해서는 도메인에서 검증해야 합니다. link: woowacourse/spring-roomescape-member#92 (comment) public class TokenExtractor {
private static final String TOKEN_COOKIE_KEY = "token";
public static String extract(HttpServletRequest servletRequest) {
Cookie[] cookies = servletRequest.getCookies();
if (cookies == null) {
throw new AuthorizationException("토큰이 존재하지 않습니다.");
} reviewer: 이런 부분도 고민해볼만 할 것 같긴 하네요.
객체 입장에서 이런저런 고민을 해보면 좋을 것 같습니다. reviewee: TokenExtractor가 토큰 추출의 역할을 한다고 한정하면 예외를 던지기 보다 Optional으로 토큰을 반환하는게 더 적절할 것 같습니다. '토큰이 있어야만 한다'는 인증에 대한 검증을하는 LoginMemberArgumentResolver나 AdminAuthorizationInterceptor에서 확인하고 예외를 던지는게 더 합리적이라고 생각해서 변경하였습니다! link: woowacourse/spring-roomescape-member#92 (comment) reviewer: Interceptor와 ArgumentResolver는 어떤 차이가 있을까요? reviewee: Interceptor는 controller의 요청과 응답을 가로채어 동작하고 ArgumentResolver는 요청(HttpServletRequest)으로 controller의 파라미터를 조작할 수 있습니다. 따라서 Interceptor는 권한(관리자 권한 api 및 페이지)을 다룰 때 ArgumentResolver는 인증된 사용자(Member)를 주입받을 때 유용합니다. link: woowacourse/spring-roomescape-member#92 (comment) if (themeRepository.hasTheme(new Name(themeAddRequest.name()))) {
throw new ClientException("이미 존재하는 테마 이름입니다.");
} reviewer: 멀티 스레드 환경에서 같은 이름을 add하는 요청이 여러번 빠르게 올 경우 이 검증 로직을 통과할 수 있습니다. reviewer: 커스텀 예외가 아닌 일반 예외들의 메세지가 클라이언트에 노출되면 어떤 일이 일어날 수 있을까요? reviewee: 커스텀 예외가 아닌 표준 예외는 외부 라이브러리와 같이 의도하지 않은 장소에서도 발생할 수 있습니다. 먼저 애플리케이션의 내부 구조나 로직에 대한 정보를 노출할 수 있을 것이고, link: woowacourse/spring-roomescape-member#56 (comment) reviewer: 어노테이션에 기반한 argument resolve는 어떤 장단점이 있을까요? reviewee: HandlerMethodArgumentResolver의 supportsParameter() 메서드를 오버라이딩 할 때, 어떤 파라미터에 값을 넣어줄지 결정하기 위해 파라미터 타입을 사용하는 방법과 어노테이션을 사용하는 방법 이 있습니다.
link: woowacourse/spring-roomescape-member#91 (comment) reviewer: name과 role 등이 토큰에 있을 때의 장단점이 어떻게 될까요? reviewee: (길어서 링크로 대체합니다.) link: woowacourse/spring-roomescape-member#91 (comment) reviwer: util이란 패키지명에는 어떤 클래스들이 위치하나요? reviewee: 특정 도메인에 종속적이지 않으면서, 프로젝트 전반적으로 사용하는 클래스이 util 패키지에 모여 있어야 한다고 생각합니다. 물론 지금은 로그인/인증 부분에서만 CookieParser를 사용하고 있기는 하지만, '키 이름을 전달받아 해당하는 키의 쿠키 값을 파싱하는 기능' 은 도메인에 종속적이라기보다는 프로젝트 전반에서 사용해야 할 유틸리티로 판단했어요. reviwer: common, util 등의 네이밍은 꽤나 위험합니다. 사람마다 이런 포괄적인 네이밍에 대한 생각이 조금씩 다르기 때문에, 또는 프로젝트 내부의 의존성 문제 때문에 이런 패키지들이 커지는 건 순식간이에요. 클래스나 패키지 등등 어떤 네이밍을 하든 구체적으로 이름을 짓는 습관을 가지면 좋을 것 같습니다. 존재하지 않는 요구사항에 맞춰 코드를 구현하기 보다는 현재 상황에 적절한 구현을 하면 좋을 것 같아요. 패키지를 추가하고 바꾸는 건 그리 어려운 일이 아닙니다. 공통적으로 사용하는 부분이 생기면 그 때 변경해도 될 것 같네요. |
Beta Was this translation helpful? Give feedback.
-
Not Found(404)는 찾는 자원(=URI)이 없을 때 응답하는 상태 코드라고 알고 있습니다. 위 경우는 URI가 아닌 DB의 데이터가 없는 경우라 클라이언트가 보낸 요청 파라미터가 잘못된 경우라고 판단했고, 그래서 Bad Request(400) 상태 코드를 응답하도록 설정하였습니다. 그렇게 알고 있었는데… 더 찾아 보니 404에서 말하는 자원이 꼭 경로만 의미하는 것이 아니고, DB의 자원을 의미한다고 하기도 하네요.😅 미르는 400, 404 코드를 각각 언제 사용하시는 편인가요?
400은 Client 요청 오류 (문법 오류)로 활용하고 있어요. Http Status Code는 프론트와 협업을 진행할 때 중요하다고 생각해요. 가끔 422을 사용하는 팀도 있습니다. woowacourse/spring-roomescape-member#104 (comment)
id에 해당하는 row가 있는지 확인하는 로직을 작성하려고 하다 보니 자연스럽게 exists()를 떠올리게 되었는데요, 미르의 코멘트를 보고 exists(), count() 두 함수의 특징을 학습해 보았습니다. exists(): 검색하려는 row가 존재할 경우 True를 반환하고 그렇지 않으면 False를 반환합니다. 조건에 일치하는 row를 찾으면 즉시 결과를 반환하기 때문에 특정 row의 존재 여부만 확인해야 할 경우 빠른 성능을 제공합니다. woowacourse/spring-roomescape-member#17 (comment)
이미지 포함이라 URL로 대체 : woowacourse/spring-roomescape-member#17 (comment) |
Beta Was this translation helpful? Give feedback.
-
Reviewer : (1) 전체가 동일하게 사용하는 테스트 데이터가 테스트 코드의 가독성을 저해하는 요소로 작용할 수 있으니 반드시 필요한 경우에만 사용하시는 것을 권장드립니다. (2) 전체적으로 모든 테스트가 test.sql에서 insert된 데이터에 의존하는 것 같습니다. (3) 테스트 픽스쳐를 이용하는 것은 좋은것 같습니다. 저는 테스트 데이터는 최소한의 범위로 제공하는 것이 좋다고 생각하는 편입니다. 테스트 픽스쳐 세부사항에 영향을 받지 않도록 테스트를 개선해보시면 좋을것 같습니다. woowacourse/spring-roomescape-member#13 (comment) Reviewer : 하나의 단언문 lambda에 하나의 메서드(생성자 포함)만 수행되도록 해보시면 좋을것 같습니다. woowacourse/spring-roomescape-member#13 (comment) Reviewer : test에서 exception 타입만 검사하는 것으로는 안정적인 테스트가 힘들 수 있습니다. exception만 검사했을때 검증하는 대상이 발생시키는 exception이 여러개인 경우, 그리고 여러개의 exception중 중복되는 exception이 있는 경우 정확히 검증하고자 하는 부분이 검증되었는지 확인이 어렵습니다. woowacourse/spring-roomescape-member#31 (comment) Reviewer : woowacourse/spring-roomescape-member#22 (comment) 테스트에서의 복잡한 의존성 설정 Reivewer : 의존성 설정을 하는 과정이 너무 힘들다면 의존성 관계를 간단하게 개선하여 애플리케이션 문제를 해결할 수 있는지 확인해보고 다음을 생각해보면 좋을것 같습니다. 위 고민이 끝난 상태에서 개선을 해야한다면 프레임워크가 의존성 관계에 따른 객체주입을 하여 운영 환경에서 제공을 하듯, 테스트 환경에서도 해당 행동을 대신해줄 수 있는 테스트 픽스쳐를 제공해보면 좋을것 같습니다. woowacourse/spring-roomescape-member#31 (comment) DDL 에서의 cascade 설정 Reviewer : cascade를 활용하는 경우 reservation이 삭제되지 않아야하는데 삭제되는 경우 같이 삭제되면서 장애로 전파될 수 있습니다. 설령 reservation이 삭제되는 경우 reservation_list가 삭제되어야 하는 정책이 있다 하더라도, 그러한 정책을 인프라스트럭쳐 레이어에게 책임을 맡기는 것은 외부 구현 세부사항에 도메인이 의존하는 모양이 되는것 같습니다. 가능한 해당 설정은 지양하고 작성해보시면 좋을것 같습니다. woowacourse/spring-roomescape-member#31 (comment) JWT 검증 여부 및 방법 Reviewee : jwt 구조에서 payload는 BASE64로 인코딩 되어 누구나 볼 수 있는 것으로 알고 있습니다. 클라이언트 측에서 토큰을 수정하거나 위조할 수 있기에, 페이로드의 내용을 검증함으로써 토큰이 변조되지 않았는지 확인할 수 있습니다. Reviewer : 일반적인지에 대한 답변은 조금 어려운것 같습니다. 아시다시피 Base64는 인코딩 기법이기 때문에 보안에 큰 이점은 없습니다. 따라서 payload를 서버에서 검증하는 것 또한 좋은 방법입니다. 저라면 특정 암호화 기법을 이용해 payload에 대한 암호화와 payload의 hash 값을 함께 검증하는 방식을 취할 것 같습니다. (제가 속한 팀은 이러한 방식으로 통신하는 경우가 많습니다.) |
Beta Was this translation helpful? Give feedback.
-
리뷰어: 응답은 Collection이 아닌 Object 형식으로 내려주는게 유연한 설계이다. woowacourse/spring-roomescape-member#67 (comment) 리뷰어: IllegalArgumentException과 같은 표준 예외를 사용할 경우 의도치 않게 다른 라이브러리의 오류 메세지가 유저단까지 노출될 수 있다. |
Beta Was this translation helpful? Give feedback.
-
🏃♂️🦆🔸 테스트 코드에서
|
Beta Was this translation helpful? Give feedback.
-
리뷰이 : Fail-Fast 원칙에 따르면 Dto 단의 검증이 유의하다고 생각합니다. 저는 이번 프로젝트에서 DTO와 객체에게 모두 검증 책임을 주었습니다. 그러나, 전체 코드를 기준으로 보았을 때 중복 검증코드란 생각도 들었습니다. 저는 DTO와 도메인 객체는 재사용성으로 고려해 최대한 독립적으로 모듈화된 객체들로 바라보고 싶습니다. 리뷰어 : 저도 비슷한 생각입니다. domain은 web / controller 를 알지 못하는 상황이니 validation을 가지는게 당연하고, web / controller는 자명한 사실에 대해 validation할 수 있겠죠. 중복으로 볼수도 있겠으나 이야기한대로 중복이 아닐수도 있겠죠. ref : woowacourse/spring-roomescape-member#79 (comment) 리뷰어 : id가 같다고 하면 같은 entity일순 있겠지만, entity가 update될 가능성이 있다고 가정하면 id가 같다고 해서("동일"하다고 해서) "동등" 하지는 않는것 같아요.(id를 통해 동등성을 비교하는 메서드에 대해서 ref : woowacourse/spring-roomescape-member#26 (comment) 리뷰어 : .http엔 {{someValue}}로 manipulation이 가능합니다 리뷰이 : 환경변수 사용해서 host 지정하도록 수정해주었습니다. 추가로, session값은 http client가 자동으로 전달해주기에 기존의 Cookie: ~ 라인도 전부 지워주었습니다. ref : woowacourse/spring-roomescape-member#144 (comment) 리뷰어 : 만약 검색 traffic이 거대하다면 (그럼 jpa같은걸 쓰던지 es같은걸 쓰겠지만..) maximum preparedStatement 를 오버해서 쿼리수행이 안될수 있습니다.(여러 조건절을 추가하는 쿼리에 대해서) 알아볼 것 : maximum preparedStatement 리뷰어 : 리뷰이 : 이와 관련해 스프링 docs에선 원칙적으론 stateful bean은 프로토타입 스코프를 사용하고, stateless bean은 싱글톤 스코프를 사용해야 한다고 명시하고 있습니다. 지금 제 코드는 stateful bean을 싱글톤 스코프로 사용하고 있습니다. 이게 틀린 건 아니지만 분명 조심해야 합니다. ref : woowacourse/spring-roomescape-member#144 (comment) |
Beta Was this translation helpful? Give feedback.
-
.
Beta Was this translation helpful? Give feedback.
All reactions