포스트

2023-03-30-TIL

2023-03-30-TIL

Today I Learned

운영 환경에서 Swagger UI 끄기

API 문서 UI는 개발 중에는 편하지만 운영에서 열어 두면 엔드포인트 목록과 요청 형식이 외부에 그대로 드러난다. springdoc-openapi를 쓴다면 프로퍼티로 끌 수 있다. 문서에 따르면 springdoc.api-docs.enabled=false는 OpenAPI 문서 엔드포인트를, springdoc.swagger-ui.enabled=false는 UI를 끈다. 프로필별 설정 파일(application-prod.yml)에 넣으면 운영에서만 꺼진다. Springfox 시절에는 Swagger 설정 빈에 @Profile("!prod")를 붙여 운영 프로필에서 빈 자체를 만들지 않는 방식을 많이 썼다.

  • https://www.baeldung.com/swagger-ui-turn-off-in-production
  • https://stackoverflow.com/questions/37794571/how-do-you-turn-off-swagger-ui-in-production
  • https://jkoder.com/how-to-disabled-swagger-ui-in-production-in-spring-boot-project/
  • https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
  • springdoc-openapi 문서

JMH(Java Microbenchmark Harness)

JMH는 OpenJDK 프로젝트가 만든 벤치마크 도구로, 저장소 설명대로 나노에서 매크로 단위까지의 벤치마크를 작성하고 실행하고 분석하는 하네스다. System.nanoTime()으로 직접 재면 JIT 컴파일 전 워밍업 구간, 결과를 쓰지 않는 코드를 JIT가 지워 버리는 dead code elimination, 상수 접기 같은 최적화 때문에 측정값이 왜곡되기 쉽다. JMH는 워밍업 반복과 측정 반복을 나누고, 결과를 Blackhole에 넘겨 제거를 막고, 별도 JVM을 fork해 실행하는 식으로 이런 함정을 줄인다. 벤치마크 메서드에 @Benchmark를 붙이고 @Warmup, @Measurement, @Fork로 실행 조건을 정한다. JUnit 같은 테스트 라이브러리처럼 쓰는 도구가 아니라는 점도 README에 명시되어 있다.

  • https://ysjee141.github.io/blog/quality/java-benchmark/
  • https://javabom.tistory.com/75
  • openjdk/jmh

JPA 관련 도서

JPA를 더 깊게 보려고 찾은 책 목록이다. JPA 명세 자체를 다루는 책(Pro JPA 계열, Mastering Java Persistence API)과 Hibernate 입문서 추천 글이 섞여 있다. 명세를 다루는 책은 영속성 컨텍스트, 엔티티 생명주기, 락과 캐시처럼 프레임워크를 바꿔도 남는 개념을 설명한다는 점에서 먼저 볼 만하다.

  • https://www.amazon.com/Mastering-Java-Persistence-API-JPA/dp/9355511264
  • https://www.amazon.com/Pro-JPA-Mastering-Persistence-Technology/dp/1430219564
  • https://medium.com/javarevisited/top-5-books-to-learn-hibernate-for-java-developers-b2cb4b16ccd6
  • https://www.java67.com/2017/02/2-best-books-to-learn-hibernate-for-Java-Developers.html
  • https://javarevisited.blogspot.com/2018/08/top-5-hibernate-books-for-java.html#axzz7xW9aDXkz
  • https://www.pinterest.co.kr/pin/270145677636991392/

R2DBC

R2DBC는 관계형 데이터베이스를 논블로킹으로 다루기 위한 리액티브 드라이버 명세이고, Spring Data R2DBC는 그 위에서 리포지토리 추상화를 제공한다. 링크한 예제 저장소는 Spring Data JPA, JDBC, R2DBC를 나란히 비교한다. README는 JPA의 N+1 문제와 해결 방법(@EntityGraph, join fetch, @BatchSize)을 보여 주고, R2DBC는 WebFlux로 끝까지 리액티브한 애플리케이션을 만들 수 있지만 연관관계 매핑 같은 ORM 기능은 없다고 정리한다. 지연 로딩이나 더티 체킹을 기대하고 R2DBC로 옮기면 안 된다는 뜻이다.

  • https://github.com/evgeniy-khist/spring-data-examples

Liquibase vs Flyway

둘 다 스키마 변경을 버전으로 관리하는 DB 마이그레이션 도구다. 변경 이력을 DB 안의 기록 테이블에 남기고, 애플리케이션이 시작될 때 아직 적용되지 않은 변경만 순서대로 실행한다.

  • Flyway: V<VERSION>__<NAME>.sql 형식의 SQL 파일을 버전 순서로 적용한다. Spring Boot 문서 기준 기본 위치는 classpath:db/migration이다. SQL을 그대로 쓰므로 단순하다.
  • Liquibase: 변경을 changeset 단위의 changelog(XML, YAML, JSON, SQL)로 기술한다. DB 종류에 독립적인 형식으로 쓸 수 있고 롤백 정의를 함께 둘 수 있다.

Spring Boot는 둘 중 하나가 클래스패스에 있으면 시작 시 마이그레이션을 자동 실행한다. Boot 문서는 이런 도구를 쓸 때 schema.sql 기반 초기화와 섞지 말고 단독으로 쓰라고 권한다.

  • https://www.baeldung.com/liquibase-vs-flyway
  • https://341123.tistory.com/20
  • https://frogand.tistory.com/93
  • https://www.baeldung.com/database-migrations-with-flyway
  • https://wildeveloperetrain.tistory.com/210
  • https://flywaydb.org/documentation/usage/plugins/springboot
  • https://backtony.github.io/spring/2021-10-22-spring-db-1/
  • Database Initialization (Spring Boot How-to)

MySQL EXPLAIN 읽기

EXPLAIN은 옵티마이저가 고른 실행 계획을 테이블별 한 행으로 보여 준다. 먼저 볼 컬럼은 다음과 같다.

  • type: 접근 방식이다. MySQL 문서는 대략 system, const, eq_ref, ref, range, index, ALL 순으로 좋은 것부터 나열하고, ALL(풀 테이블 스캔)은 보통 좋지 않다고 설명한다. index는 테이블 대신 인덱스 트리를 전부 읽는다는 뜻이라 이름과 달리 빠르지 않을 수 있다.
  • key, rows, filtered: 실제로 쓴 인덱스, 검사해야 한다고 추정한 행 수, 조건으로 걸러지고 남는 비율의 추정치다.
  • Extra: Using index는 인덱스만으로 결과를 만들었다는 뜻(커버링 인덱스)이고, Using filesort는 정렬을 위한 추가 단계, Using temporary는 임시 테이블 사용을 뜻한다. 뒤의 둘은 보통 튜닝 대상이다.

  • https://planetscale.com/blog/how-read-mysql-explains
  • EXPLAIN Output Format (MySQL 8.0)

Feign vs Retrofit, RestTemplate vs WebClient

Feign은 RestTemplate을 대체하기 위한 용도로 사용할 수 있는가?

HTTP 클라이언트는 호출 코드를 쓰는 방식과 블로킹 여부로 나눠 볼 수 있다.

  • RestTemplate: 동기, 블로킹, 템플릿 메서드 방식이다. Spring 6.1 Javadoc에는 동기 호출용으로 더 현대적인 API인 RestClient가 추가됐고 새 기능은 RestClient에 집중된다는 안내가 붙어 있다.
  • WebClient: Spring WebFlux의 논블로킹 클라이언트다. 리액티브 스택이 아니어도 쓸 수 있지만 Reactor 타입을 다뤄야 한다.
  • Feign(Spring Cloud OpenFeign), Retrofit: 인터페이스에 애너테이션으로 HTTP 호출을 선언하면 구현을 만들어 주는 선언형 클라이언트다. 호출 코드가 짧아지고 서비스 간 API를 인터페이스로 표현할 수 있다.

그래서 질문에 대한 답은 블로킹 동기 호출이라는 점에서 Feign이 RestTemplate의 자리를 대신할 수 있다는 것이다. 선언형 방식이 필요한지, 논블로킹이 필요한지가 선택 기준이 된다.

  • https://medium.com/@MonkeyPatch/feign-vs-retrofit-1-getting-started-1298b57a254
  • https://www.baeldung.com/spring-boot-feignclient-vs-webclient
  • https://tecoble.techcourse.co.kr/post/2021-07-25-resttemplate-webclient/
  • https://www.baeldung.com/spring-webclient-resttemplate

Heisenbug

Heisenbug는 조사하려고 하면 사라지거나 동작이 바뀌는 버그를 가리킨다. Wikipedia는 이 이름이 관찰 행위가 대상을 바꾼다는 관찰자 효과를 빗댄 말장난이라고 설명한다. 디버거를 붙이거나 로그를 넣으면 타이밍이 바뀌어 재현되지 않는 경쟁 상태, 최적화 수준에 따라 달라지는 동작, 초기화되지 않은 메모리 읽기가 대표적인 원인이다.

  • https://en.wikipedia.org/wiki/Heisenbug

QueryDSL vs jOOQ

둘 다 문자열 대신 타입 안전한 코드로 쿼리를 작성하게 해 주지만 출발점이 다르다. QueryDSL은 주로 JPA 엔티티에서 Q 클래스를 생성해 JPQL을 타입 안전하게 쓰는 용도로 쓰이고, JPA의 영속성 컨텍스트와 함께 동작한다. jOOQ는 데이터베이스 스키마에서 코드를 생성하고 SQL 자체를 Java DSL로 옮긴 형태라 윈도 함수나 DB 고유 문법처럼 JPQL로 표현하기 어려운 SQL을 쓰기 쉽다. 엔티티 중심 도메인 모델이면 QueryDSL, SQL 중심의 조회나 리포팅이면 jOOQ가 잘 맞는다는 것이 링크한 비교 글들의 대체적인 결론이다.

  • https://groups.google.com/g/jooq-user/c/VNyevvq6MmY
  • https://sightstudio.tistory.com/54
  • https://stackoverflow.com/questions/7242388/comparing-querydsl-jooq-jequel-activejdbc-iciql-and-other-query-dsls
  • https://stackshare.io/stackups/jooq-vs-querydsl
  • https://stackoverflow.com/questions/26639057/is-it-possible-to-combine-mybatis-and-querydsl-jooq
  • https://blog.jooq.org/tag/querydsl/

페이지네이션 기반 무한 스크롤

무한 스크롤은 사용자가 목록 끝에 다다르면 다음 묶음을 불러온다. 서버 쪽에서는 전체 개수가 필요 없으므로 Spring Data의 Slice가 잘 맞는다. Javadoc 설명대로 Slice는 다음이나 이전 묶음이 있는지만 알려 주므로 Page와 달리 count 쿼리를 실행하지 않는다. 데이터가 많으면 offset 대신 마지막으로 본 ID 이후를 조회하는 커서 방식이 깊은 페이지에서도 빠르다. 클라이언트 쪽에서는 스크롤 이벤트를 직접 계산하기보다 Intersection Observer API로 목록 끝의 감시 요소가 화면에 들어오는 순간을 비동기로 감지해 다음 요청을 보낸다.

  • https://velog.io/@do_ng_iill/Spring-Pagination
  • https://data-make.tistory.com/677
  • https://github.com/ReturnReason/ToyProject/tree/main/intersectionObserver
  • https://velog.io/@reasonz/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-Intersection-Observer-API
  • Intersection Observer API (MDN)
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다