포스트

2021-03-29-TIL

2021-03-29-TIL

CRDT

CRDT(Conflict-free Replicated Data Type)는 여러 복제본이 서로 조율하지 않고 각자 수정한 뒤 나중에 합쳐도 항상 같은 상태로 수렴하도록 설계한 자료구조다. 병합 연산이 교환법칙, 결합법칙, 멱등성을 만족하게 만들어서, 업데이트가 어떤 순서로 몇 번 도착하든 결과가 같다. 증가만 하는 카운터(G-Counter), 추가와 삭제를 함께 다루는 집합(OR-Set) 등이 대표적이다. 링크된 글은 엣지 서버처럼 지리적으로 흩어진 노드가 중앙 조율 없이 상태를 공유해야 하는 서버리스 환경에서 CRDT를 쓰는 이유를 설명한다. 공동 편집 도구도 같은 원리를 쓴다.

  • https://www.serverless.com/blog/crdt-explained-supercharge-serverless-at-edge

JPA 즉시로딩과 지연로딩 (FetchType.LAZY or FetchType.EAGER)

즉시 로딩(EAGER)은 엔티티를 조회할 때 연관 엔티티도 함께 가져오고, 지연 로딩(LAZY)은 연관 엔티티 자리에 프록시를 두었다가 실제로 사용할 때 쿼리를 보낸다. JPA 명세의 기본값은 @ManyToOne, @OneToOne이 EAGER, @OneToMany, @ManyToMany가 LAZY다. EAGER는 예상하지 못한 조인이나 JPQL 조회 후 연관 엔티티마다 추가 쿼리가 나가는 N+1 문제를 일으키기 쉬워서, 모든 연관관계를 LAZY로 두고 함께 필요할 때만 fetch join이나 엔티티 그래프로 가져오는 방식이 흔히 권장된다.

  • https://ict-nroo.tistory.com/132

MySQL DateTime and TimeStamp

MySQL 문서에 따르면 두 타입은 범위와 시간대 처리가 다르다.

타입범위시간대 처리
DATETIME1000-01-01 00:00:00 ~ 9999-12-31 23:59:59저장한 값 그대로
TIMESTAMP1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC저장할 때 현재 시간대에서 UTC로, 조회할 때 다시 현재 시간대로 변환

그래서 세션 시간대를 바꾸면 TIMESTAMP 값은 다르게 조회되고 DATETIME은 그대로다. TIMESTAMP는 2038년 이후 값을 담을 수 없다는 점도 선택할 때 고려해야 한다.

  • https://nesoy.github.io/articles/2020-02/mysql-datetime-timestamp

JPA Cascade Type

영속성 전이는 부모에 대한 persist, merge, remove 같은 작업을 자식에게도 적용한다. 링크된 글이 강조하듯 기본으로는 아무 작업도 전이되지 않으므로 필요한 타입을 명시해야 한다. CascadeType.REMOVE와 orphanRemoval = true는 비슷해 보이지만, 전자는 부모를 지울 때 자식을 지우고 후자는 부모의 컬렉션에서 빠진 자식까지 지운다는 차이가 있다.

  • https://www.baeldung.com/jpa-cascade-types
  • https://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/#:~:text=There%20is%20no%20default%20cascade,default%20no%20operations%20are%20cascaded.&text=%3D%20%22EMPLOYEE_ID%22%20)-,private%20Set%20accounts%3B,be%20only%20merged%20and%20refreshed.

JPA Bidirectional

양방향 매핑에서는 외래 키를 관리하는 연관관계의 주인을 정하고, 반대편에는 mappedBy를 둔다. 주인이 아닌 쪽의 컬렉션에만 객체를 추가하면 DB에는 반영되지 않으므로 양쪽 참조를 함께 맞춰 주어야 한다. 양방향 참조는 toString()이나 JSON 직렬화에서 무한 순환을 만들기 쉽다는 점도 함께 주의한다.

  • https://dublin-java.tistory.com/51
  • https://pasudo123.tistory.com/350

@Transient

이름이 같아도 적용 범위가 다르다. JPA의 @javax.persistence.Transient는 필드를 DB 컬럼에 매핑하지 않게 하고, Jackson의 @JsonIgnore는 JSON 직렬화와 역직렬화에서만 필드를 뺀다. 계산용 필드는 DB에 저장하지 않되 응답에는 포함하고 싶다면 @Transient, DB에는 저장하지만 응답에서 숨기고 싶다면(비밀번호 등) @JsonIgnore를 쓴다. Java 키워드 transient는 또 다른 개념으로, Java 직렬화에서 필드를 제외한다.

https://stackoverflow.com/questions/29762328/jsonignore-vs-transient-difference/29762521


Multiple error handling from the same ExceptionHandler

하나의 애플리케이션이 HTML 페이지와 JSON API를 함께 제공하면, 같은 예외라도 브라우저 요청에는 에러 페이지를, API 요청에는 JSON 에러 응답을 보내야 한다. 링크된 질문들의 답은 요청의 Accept 헤더나 URL 경로로 구분하는 방법이다. @ExceptionHandler 안에서 요청 정보를 보고 ModelAndView나 ResponseEntity를 골라 반환하거나, @ControllerAdvice를 HTML 컨트롤러용과 @RestController용으로 나눠 적용 범위(annotations, basePackages)를 지정한다.

  • https://stackoverflow.com/questions/32455828/multiple-error-pages-from-the-same-exceptionhandler
  • https://stackoverflow.com/questions/23582534/how-to-handle-exceptions-in-spring-mvc-differently-for-html-and-json-requests

Spring Data JPA Pageable

Spring Data는 페이지 번호, 크기, 정렬을 Pageable로 받아 쿼리에 적용한다. 리포지토리 메서드에 Pageable 파라미터를 추가하면 Page<T>를 돌려주고, Page는 내용과 함께 전체 개수와 전체 페이지 수를 담는다. 전체 개수를 알기 위해 count 쿼리가 하나 더 실행되므로, “더 보기”처럼 다음 페이지 존재 여부만 필요하면 Slice<T>를 반환형으로 써서 count 쿼리를 피할 수 있다. 컨트롤러에서는 ?page=0&size=20&sort=createdAt,desc 같은 요청 파라미터를 Pageable로 바로 받을 수 있고, 페이지 번호는 0부터 시작한다.

1
2
3
Page<Question> findByAuthor(User author, Pageable pageable);

Page<Question> page = repository.findByAuthor(user, PageRequest.of(0, 20, Sort.by("createdAt").descending()));
  • https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-seven-pagination/
  • https://gunju-ko.github.io/spring/2018/05/01/Spring-Data-JPA-Paging.html
  • https://www.baeldung.com/spring-data-jpa-pagination-sorting
  • https://bezkoder.com/spring-boot-pagination-filter-jpa-pageable/

DB 책 추천

데이터베이스 공부용 책 추천 글들이다.

  • https://yeolco.tistory.com/51
  • https://okky.kr/article/311050
참고한 자료외부 출처 4

외부 출처

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다