1. @field: (annotation use-site target)
코드중 에너테이션 앞에 이런 접두어가 붙는 것을 보았다.
@JsonProperty 같은 에너테이션은 런타임시 리플렉션으로 이 에너테이션이 붙은 필드값을 찾는다.
따라서 필드에 붙어야한다. 그런데.
코틀린의 primary 생성자에 프로퍼티를 선언하고 사용하는 패턴을 많이들 쓴다.
그런데 이 위에 어떤 에너테이션을 붙이면, 예를들어
class Dto(
@JsonProperty
val name:String
)
이러게 되면, 컴파일된 자바코드로는 생성자의 파라미터에 에너테이션이 붙는다. 즉,
public final class Dto {
@NotNull
private final String name;
public Dto(@JsonProperty("name") @NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
}
}
하지만 이건 우리가 의도했던 바가 아니고,
우린 필드에 붙길 바란다.
그러려면 코드작성시 아래와 같이 @field: 를 붙이면 된다.
class Dto(
@field:JsonProperty
val name:String
)
public final class Dto {
@JsonProperty("name")
@NotNull
private final String name;
public Dto(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
}
}
https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets
2. getBy vs findBy
Jpa 의 select 하는 방법에는 위 두가지가 있다.
2.1 getById, findById
id 를 대상으로 한 경우는 특이하다.
getById 는 결과를 proxy로 가져오고 Lazy Loading 을 해서 가져온다.
findById 같은 경우 Eager 한 로딩을 한다.
그런데 Id 대상이 아닌 getByName 같은경우는 get 이라도 proxy 안쓴다.
2.2 select 한 결과가 하나도 없는 경우
- id 를 대상 즉 ~ById
getById -> EntityNotFoundException
findById -> Optional 반환
- id 대상 아닌 경우 즉 ~ByName .. && 단건조회인 경우 (반환형이 List<Object> 가 아닌 Object 인 경우)
getByName, findByName 둘다 -> NoResultException 발생 (리포지토리단 예외)
-> 서비스 로직에서 위 에러를 잡아서 EmptyResultDataAccessException 로 다시 던짐.
추가로, 단건조회인데 2건 이상 발견되면, NonUniqueResultException 가 발생.
- id 대상 아닌 경우 즉 ~ByName .. && 여러건 조회인 경우 (반환형이 List)
getByName, findByName 둘다 -> Empty 컬렉션 반환
위는 자바기준이고
코틀린에서 Nullable 하게 ? 붙이면 에러 안나고 null 반환.
근데 ? 안붙이면 EmptyResultDataAccessException 뜬다.
- All (리스트 반환하게 한 경우)
findAllByName(bookName: String): MutableList<Book>?
getAllByName(bookName: String): MutableList<Book>?
findAllByName(bookName: String): MutableList<Book>
getAllByName(bookName: String): MutableList<Book>
위의 모든 경우 size 가 0 인 리스트 반환
null 을 반환하진 않는다는것.
get, find 는 서로 호환성이 있어서 바꿔써도 결과에는 문제가 없다.
또, 서비스 로직에서의 경우는 의미적으로 나눌 수 있는데,
get 은 확실히 얻어온다는 느낌의 가져오는 로직 따라서 없으면 Exception 낼거같은?
find 는 null 일 수 있는 찾는 로직.
https://szymonkrajewski.pl/the-practical-difference-between-findby-and-getby-in-repositories/
3-3. JPA 가 던져주는 예외 정리
롤백되는 예외
javax.persistence.EntityExistsException | EntityManager.persist() 호출 시 같은 엔티티가 있으면 발생 |
javax.persistence.EntityNotFoundException | EntityManager.getReference(..) 호출하고 실제 사용 시 엔티티가 존재하지 않으면 발생. refresh(..), lock(..) 에서도 발생 |
javax.persistence.OptimisticLockException | 낙관적 락 충돌 |
javax.persistence.PessimisticLockException | 비관적 락 충돌 |
javax.persistence.RollbackException | EntityTransaction.commit() 실패 롤백이 표시되어 있는 트랜잭션 커밋시에 발생 |
javax.persistence.TransactionRequiredException | 트랜잭션이 필요할 떄 트랜잭션이 없으면 발생. 트랜잭션 없이 엔티티를 변경할 떄 주로 발생 |
롤백되지 않는 예외
javax.persistence.NoResultException | Query.getSingleResult() 호출 시 결과가 하나도 없을 때 발생 |
javax.persistence.NonUniqueResultException | Query.getSingleResult() 호출시 결과가 둘 이상일 떄 발생 |
javax.persistence.LockTimeoutException | 비관적 락에서 시간 초과 |
javax.persistence.QueryTimeException | 쿼리 실행 시간 초과 |
JPA 는 결국 Spring 에서 DB 접근을 쉽게 코드적으로 할 수 있게 도와주는 프레임워크다.
위의 예외들을 서비스에서 사용하는 것은, JPA 에 의존하는 것이므로
의존성을 떨어뜨리기 위해 스프링 예외로 잡아서 바꿔서 던져주기 위해서는,
PersistenceExceptionTranslationPostProcessor
를 빈으로 등록해야 한다.
편리하게도, 스프링 부트를 사용하면 이 빈이 자동으로 등록되기에, 바꿔서 던져지는 것을 볼 수 있다.
https://joont92.github.io/jpa/JPA-%EC%98%88%EC%99%B8/
3. JWT
멘토링중 Java 에서 jjwt 를 사용해서 JWT 를 만드는 경우의 이슈가 있었다.
JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey);
을 이용해서 만든 JWT 를 암호화하고 jwt.io 사이트에서 키로 복호화하면 Invalid 가 발생한다.
인자를 보면 알겠지만 key 를 base64 인코딩한 값으로 받는다.
base64 인코딩 방식은 아스키 128개중에 64개만 사용한 특정문자열로 바꾸는 인코딩방식이다.
그래서 저 코드를 까보면, 키값이 base64 인코딩된 값이라 가정하고 진행한다.
더 자세하게는, 키 string 값에서 base64 값이 아니면 빼는 로직이 있다.
따라서 키값을 "+123+" 로 설정하여
signWith(알고리즘, "+123+") 라고 썼다면,
실제적으로 키값은 "123" 으로 적용되며, 123 은 인코딩이 된 값으로 쓴다는 것이다.
base64 가 아닌 아스키코드를 쓰려면
즉, "+123+" 을 그대로 쓰려면
JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey);
를 쓰면 된다. 즉, byte[] 로 바꾸고 넣으면 된다.
'TIL' 카테고리의 다른 글
TIL) JPA 페이징, Json 응답시 Null 필드 제외, Envers (0) | 2021.10.18 |
---|---|
TIL) 깃 충돌, 예외 처리 전략, @CreatedDate vs @CreationTimeStamp (0) | 2021.10.17 |
TIL) 트래킹 브랜치, @RequestParam 디폴트, Kotlin any() (0) | 2021.10.15 |
TIL) 쿠키/세션, 코틀린 스트림 파이프라인, 정규식 (0) | 2021.10.14 |
TIL) 인터셉터 (0) | 2021.10.12 |