도도한 개발자

[TIL] 2023/08/12 본문

TIL

[TIL] 2023/08/12

Kiara Kim 2023. 8. 12. 23:40

🐈‍⬛ 오늘 공부한 내용

  • JwtException
  • Reflection
  • @PageableDefault()의 default size
  • 부모클래스 필드까지 build하기 -> super()와 @Builder
  • Fixture 바꾸지 않기

 🐾 느낀 점 & 배운 점

우리는 프로젝트에 일찍이 JWT를 도입했다. 토큰이 들어오면 디스패처 서블릿으로 가기 전 필터(Filter)에서 해당 토큰이 유효한 지 아닌 지 걸러준다. 필터는 인증 확인과 토큰의 유효성을 동시에 검증했다. 그런데 토큰을 검증하는 부분이 인증에 있는 것보단 토큰을 관리하는 클래스에서 하는 것이 좋을 것 같다고 해서 로직을 수정했다. 토큰에서 claims를 가져오는 과정에서 발생할 수 있는 예외를 다음과 같이 잡아줬다.

public Long extract(String token) {
    try {
        return Jwts.parser()
                .setSigningKey(secret.getBytes())
                .parseClaimsJws(token)
                .getBody()
                .get("id", Long.class);
    } catch (SecurityException e) {
        throw new JwtException(SIGNITURE_NOT_FOUND.message());
    } catch (MalformedJwtException e) {
        throw new JwtException(MALFORMED_TOKEN.message());
    } catch (ExpiredJwtException e) {
        throw new JwtException(EXPIRED_TOKEN.message());
    } catch (UnsupportedJwtException e) {
        throw new JwtException(UNSUPPORTED_TOKEN.message());
    } catch (IllegalArgumentException e) {
        throw new JwtException(INVALID_TOKEN.message());
    }
}

 

Reflection...으로 가기까지 오늘 몇시간을 썼지. 한 3시간 썼나? 근데 이 마저도 내가 알아낸게 아니라 또박(또 박스터)이 알려줘서 테스트를 성공시켰다. 이번에 구현하는 기능인 리뷰 테이블엔 생성일 컬럼과 수정일 컬럼이 있고 리뷰 조회의 응답엔 그것과 더불어 수정여부 컬럼이 있다. 처음엔 엔티티에도 수정 여부 컬럼을 뒀었지만 관리 포인트가 늘어나는 것을 방지하기 위해 엔티티에선 빠지고 대신 응답으로 매핑할 때 생성일과 수정일에 차이로 수정여부를 결정한다. 문제는 컨트롤러 테스트에서 발생하는데 mock 객체로 값을 가져오니 DB를 거치지 않아 @CreatedDate와 @LastModifiedDate로 생성일, 수정일을 저장하지 못 한다. 그래서 이 mock 객체가 response dto로 매핑 할 때 수정여부를 결정할 날짜 데이터들이 다 null로 비어있기 때문에 계속해서 

{
    "exceptionCode":0,
    "message":"Cannot invoke \"java.time.LocalDateTime.isAfter(java.time.chrono.
         ChronoLocalDateTime)\" because the return value of \"com.carffeine.carffeine.
         station.domain.review.Review.getUpdatedAt()\" is null"
}

이런 예외가 터졌다. 생성일과 수정일 필드는 둘 다 부모클래스의 필드라 직접 @SuperBuilder를 쓰지 않는 이상 build를 할 수 없어서 곤란했다.

이를 해결 할 방법으로 Reflection을 사용했는데 아주그냥 깔끔하게 해결됐다. 지금은 다른 방법을 알아내서 다음 기능 구현할 때 슬쩍 제거하겠지만 새 개념을 알아가서 힘들고 좋았다.  코드는 아래가 전부다. 

private static void setReflection(Page<Review> reviews) {
    ReflectionTestUtils.setField(
        reviews.get().toList().get(0),
        BaseEntity.class,
        "createdAt",
        LocalDateTime.of(23, 8, 12, 19, 30, 18),
        LocalDateTime.class
    );
}

궁금한 분들은 다음 블로그에서 확인하면 될 것 같아용.

 

@PageableDefault의 경우 처음에 적용하려다가 디폴트 size가 10이었고 다행히 나와 가브리엘이 결정한 페이지 당 리뷰 요청 개수가 10개였기 때문에 굳이 적용하지 않아도 된다고 생각했다. 그래서 컨트롤러의 파라미터로 @PageableDefault를 정하지 않고 서비스에서 최신 날짜로 내림차순을 하기 위해 PageRequest를 사용했다. 이때 디버깅을 하니 page의 size가 20이라 사이즈를 10으로 맞췄다. (지금 생각하면 왜 했는가 싶고..) 코드 리뷰를 받아보고 난 후에야 이마를 탁 쳤고 다시 컨트롤러로 가서 @PageableDefault에 size는 생략하고 id로 내림차순하는 것만 지정했다.

@PageableDefault(sort = "id", direction = DESC) Pageable pageable

내림차순도 그래. 날짜로 ordering하는 것보다 id로 하는게 좋다는 생각을 왜 못했을까? 

 

아 왜 글이 안끝나지.

 

super()와 @Builder

위의 컨트롤러 테스트 이슈를 해결하기 위해서 클래스와 super 클래스에 @SuperBuilder를 붙였었는데 그러면 도메인의 생성자에서 하는 검증을 안거치는 현상이 발생한다. 그래서 super 클래스에 @AllArgsConstructor를 붙이고 클래스의 생성자 위에 @Build 어노테이션을 붙여서 클래스의 생성자가 검증로직을 실행할 수 있도록 변경했다. (또박) 생성자로 생성일과 수정일을 받아 super()에 인자로 넣어주면 위의 Reflection을 사용하지 않아도 된다.

 

Fixture 바꾸지 않기

(티스토리 네비게이션 어떻게 하는거야..-_-)

이 부분에 대해선 짧게 글을 써봤다.

https://kiarakim.tistory.com/123

 

누가 도메인 테스트에서 Fixture를 사용하였는가

🐾 배경 설명 @Test void 인증된_멤버일_경우_정상_응답한다() { //given 댓글을_등록한다(요청, 토큰, 충전소); // when var patch응답 = 댓글을_수정한다(수정_요청_1개, 토큰, 리뷰); var 해당_댓글 = 해당_댓

kiarakim.tistory.com

💭 감정회고

감정 회고? 오늘을 그냥 하루종일 테스트 빨간줄만 해결하느라 화가 잔뜩 나 있었다. 똑똑해지자. 화이팅

'TIL' 카테고리의 다른 글

[TIL] 2023/08/14  (0) 2023.08.14
[TIL] 2023/08/13  (0) 2023.08.13
[TIL] 2023/08/11  (0) 2023.08.11
[TIL] 2023/08/10  (1) 2023.08.10
[TIL] 2023/08/09  (2) 2023.08.09