포스트

2023-05-11-TIL

2023-05-11-TIL

Today I Learned

Java Record를 쓸 때 부딪히는 문제

record는 불변 데이터를 담는 클래스를 짧게 선언하게 해 주지만, 기존 Java 관례와 부딪히는 지점이 있다. JEP 395에 따르면 record 클래스는 암묵적으로 final이고 추상 클래스가 될 수 없으며, 각 컴포넌트는 private final 필드가 된다. 여기서 몇 가지 문제가 생긴다.

  • JPA 엔티티로 쓸 수 없다. JPA는 기본 생성자와 프록시를 위한 상속, 필드 변경(더티 체킹)을 전제하는데 record는 모두 막혀 있다. 대신 조회 결과를 담는 DTO나 프로젝션으로는 잘 맞는다.
  • 접근자 이름이 getName()이 아니라 name()이라서 JavaBeans 규칙을 기대하는 라이브러리나 템플릿에서 값을 못 읽을 수 있다.
  • 불변성이 얕다. 필드에 List를 담으면 참조는 바꿀 수 없어도 리스트 내용은 바뀔 수 있으므로, 생성자에서 List.copyOf()로 방어적 복사를 해야 한다.

  • https://medium.com/javarevisited/how-to-resolve-3-common-java-records-issues-39a74689be63
  • JEP 395: Records

MapStruct에서 여러 소스 객체 매핑

여러개의 객체를 파라미터로 받을 때 일일이 @Mapping을 붙여주어야 하는가? 기본적인 매핑 대상 객체를 지정해두고, 나머지 일부 필드만 직접 매핑할 수 없나?

MapStruct 레퍼런스를 보면 두 질문 모두 답이 있다. 여러 소스 파라미터를 받는 매핑 메서드에서도 프로퍼티는 이름으로 자동 매핑되고, @Mapping이 꼭 필요한 경우는 같은 이름의 프로퍼티가 여러 소스에 있어 어느 쪽을 쓸지 모호할 때뿐이다. 모호함을 해결하지 않으면 컴파일 오류가 난다. 한 소스에만 있는 프로퍼티는 파라미터 이름을 생략해도 된다.

기본 소스를 정해 두는 방법으로는 target = "."이 있다. 레퍼런스 3.5절의 설명대로 @Mapping(target = ".", source = "record")라고 쓰면 해당 소스 객체의 모든 프로퍼티를 대상에 매핑하고, 나머지 필드만 개별 @Mapping으로 지정하면 된다.

1
2
3
4
5
6
@Mapper(componentModel = "spring")
public interface OrderMapper {
    @Mapping(target = ".", source = "order")
    @Mapping(target = "memberName", source = "member.name")
    OrderResponse toResponse(Order order, Member member);
}

record를 DTO로 쓸 때도 MapStruct는 생성자를 통해 값을 넣으므로 그대로 사용할 수 있다.

  • https://www.baeldung.com/mapstruct-multiple-source-objects
  • https://www.tutorialspoint.com/mapstruct/mapstruct_mapping_multiple.htm
  • https://dzone.com/articles/how-to-best-use-java-records-as-dtos-in-spring-boo
  • MapStruct Reference Guide

Java에서 안전한 임시 비밀번호 만들기

임시 비밀번호나 토큰처럼 추측되면 안 되는 값에는 java.util.Random이 아니라 java.security.SecureRandom을 써야 한다. Javadoc은 SecureRandom을 암호학적으로 강한 난수 생성기라고 설명한다. Random은 시드와 이전 출력으로 다음 값을 예측할 수 있는 선형 합동 생성기다. Baeldung 예제는 Passay, Apache Commons Text와 Lang3를 이용한 방법을 나란히 보여 준다. 그중 Commons 기반 예제는 대문자, 소문자, 숫자, 특수문자를 각각 정해진 개수만큼 뽑은 뒤 Collections.shuffle()로 섞어서, 비밀번호 정책을 만족하면서도 문자 위치가 고정되지 않게 한다. Passay를 쓰면 문자 규칙 정의와 비밀번호 검증을 같은 라이브러리로 처리할 수 있다.

  • https://www.baeldung.com/java-generate-secure-password
  • https://github.com/eugenp/tutorials/blob/master/core-java-modules/core-java-string-apis/src/main/java/com/baeldung/password/RandomPasswordGenerator.java
  • SecureRandom (Java SE 17)

Spring의 @Scheduled

@EnableScheduling을 켜고 메서드에 @Scheduled를 붙이면 주기적으로 실행된다. Spring 문서 기준으로 주요 속성은 다음과 같다.

  • fixedDelay: 이전 실행이 끝난 시점부터 간격을 잰다.
  • fixedRate: 이전 실행이 시작된 시점부터 간격을 잰다.
  • initialDelay: 첫 실행 전 대기 시간이다. 단위는 기본 밀리초이고 timeUnit으로 바꿀 수 있다.
  • cron: cron 표현식으로 실행 시각을 지정한다.

주의할 점은 스레드 수다. Spring Boot의 spring.task.scheduling.pool.size 기본값은 1이라, 스케줄 작업이 여러 개면 하나가 오래 걸릴 때 나머지가 밀린다. 또 서버를 여러 대 띄우면 각 인스턴스에서 같은 작업이 동시에 실행되므로 ShedLock 같은 분산 락이 필요하다.


Tech News

Prime Video 모니터링 서비스의 모놀리스 전환

Prime Video 기술 블로그 글은 실시간 스트림의 화질과 음질 결함을 감지하는 모니터링 서비스를 분산 서버리스 구조에서 단일 프로세스로 옮긴 사례다. 처음에는 AWS Step Functions로 오케스트레이션하고, 영상을 프레임 이미지로 쪼개 S3에 올린 뒤 결함 감지기들이 Lambda에서 내려받아 처리했다. 글에 따르면 스트림 1초마다 여러 번의 상태 전이가 일어나 Step Functions의 계정 한도에 금방 닿았고 상태 전이당 과금도 컸으며, S3 호출 비용도 높았다. 예상 부하의 약 5% 수준에서 확장 한계에 걸렸다고 한다.

그래서 모든 구성 요소를 하나의 Amazon ECS 태스크 안의 단일 프로세스로 모아 프레임을 메모리로 주고받게 바꾸었고, 인프라 비용이 90% 넘게 줄었다고 보고한다. 대신 감지기는 수직으로만 확장되므로, 감지기 묶음을 나눈 서비스 복제본을 여러 개 띄우는 방식으로 보완했다. 글의 결론은 마이크로서비스나 서버리스가 틀렸다는 것이 아니라 사용 사례마다 판단해야 한다는 것이다.

  • https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다