포스트

2023-07-01-TIL

2023-07-01-TIL

Today I Learned

JPA

자바 리플렉션으로 프레임워크에 대한 의존을 줄일 수 있다.

JPA 구현체는 리플렉션으로 엔티티를 다룬다. 기본 생성자로 객체를 만들고, 필드나 접근자에 직접 값을 넣고 읽으며, 지연 로딩을 위해 엔티티를 상속한 프록시를 만든다. 그래서 엔티티는 특정 베이스 클래스를 상속하거나 프레임워크 인터페이스를 구현하지 않아도 되는 평범한 Java 객체로 남을 수 있다. 메모의 의미는 이 방식을 응용하면 도메인 객체가 프레임워크 타입을 알지 않아도 바깥에서 객체의 상태를 읽고 바꾸는 기능을 만들 수 있다는 것이다. 대가는 컴파일 시점 검사가 사라지고, 필드 접근 규칙(기본 생성자, final 금지 등)이 암묵적인 제약으로 남는다는 점이다.


Memento Pattern

메멘토 패턴을 JPA 더티체킹에 적용해볼 수 있을까?

메멘토 패턴은 객체의 캡슐화를 깨지 않고 내부 상태를 스냅샷(memento)으로 저장해 두었다가 나중에 그 상태로 되돌릴 수 있게 하는 GoF 행위 패턴이다. 상태를 가진 Originator가 memento를 만들고, Caretaker는 내용을 모른 채 보관만 한다. 실행 취소(undo)가 대표적인 쓰임새다.

메모의 질문은 흥미로운데, Hibernate의 더티 체킹이 실제로 비슷한 구조다. 엔티티를 영속성 컨텍스트에 올릴 때 로딩 시점의 값을 스냅샷으로 따로 저장해 두고, flush 시점에 현재 값과 스냅샷을 비교해 바뀐 엔티티에 UPDATE를 만든다. 차이는 스냅샷을 엔티티가 아니라 영속성 컨텍스트가 만들고, 목적이 복원이 아니라 변경 감지라는 점이다.

  • https://www.baeldung.com/java-memento-design-pattern
  • https://medium.com/nerd-for-tech/understanding-memento-design-pattern-5c4f09be639

Observer Pattern

옵저버 패턴은 한 객체(subject)의 상태가 바뀌면 그 객체를 구독한 여러 옵저버에게 자동으로 알리는 패턴이다. subject는 옵저버 인터페이스만 알고 구체 타입은 모르므로, 새 반응을 추가해도 subject를 고치지 않는다.

Spring에는 이 패턴이 이벤트 기능으로 들어 있다. Spring 문서도 ApplicationEvent와 ApplicationListener를 통한 이벤트 처리가 본질적으로 표준 옵저버 패턴이라고 설명하고, Spring 4.2부터 애너테이션 기반 모델이 생겼다고 적는다. ApplicationEventPublisher.publishEvent()로 이벤트를 발행하고 @EventListener 메서드가 받는다. 기본은 발행한 스레드에서 동기로 실행되므로, 리스너가 느리면 발행 쪽도 느려지고 리스너 예외가 발행 쪽 트랜잭션에 영향을 준다. 비동기가 필요하면 @Async를, 커밋 이후에 처리하려면 @TransactionalEventListener를 쓴다. 목록의 여러 글은 Java 기본 구현, AOP와 커스텀 애너테이션으로 옵저버를 붙이는 방법, .NET의 모범 사례 등 같은 패턴의 다양한 구현을 보여 준다.

  • https://www.baeldung.com/java-observer-pattern
  • https://sevrain-chea.medium.com/how-to-implement-observer-pattern-using-springboot-events-106d80b9ea05
  • https://codingstrain.com/java-observer-pattern-spring-aop-and-a-custom-annotation/
  • https://stackoverflow.com/questions/44688680/how-to-register-a-observer-using-spring-boot
  • https://www.theserverside.com/news/1364408/Spring-Loaded-Observer-Pattern
  • https://springframework.guru/gang-of-four-design-patterns/observer-pattern/
  • https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=gngh0101&logNo=221337447578
  • https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern-best-practices
  • https://meatba11.medium.com/design-pattern-the-observer-pattern-482c6041b279
  • https://madooei.github.io/cs421_sp20_homepage/observerExercise/
  • http://underpop.online.fr/w/web-service-patterns/web-service-patterns-092.html
  • https://howtodoinjava.com/design-patterns/behavioral/observer-design-pattern/
  • https://erpsolutions.oodles.io/developer-blogs/Guide-and-Real-world-example-for-Observer-Design-Pattern-in-Java/
  • https://siditaduli.com/en/code-in-java/observer-design-pattern/
  • Additional Capabilities of the ApplicationContext (Spring Framework Reference)

@DomainEvents

Spring Data는 옵저버 패턴을 DDD의 애그리거트에 맞춘 @DomainEvents를 제공한다. Spring Data Commons 문서에 따르면 애그리거트 루트의 메서드에 @DomainEvents를 붙이면 save(), saveAll(), delete() 같은 리포지토리 메서드가 호출될 때 그 메서드가 반환한 이벤트가 발행되고, 모두 발행된 뒤 @AfterDomainEventPublication 메서드가 호출되어 이벤트 목록을 비울 수 있다. AbstractAggregateRoot를 상속하면 registerEvent()로 이벤트를 쌓는 코드를 직접 짤 필요가 없다. 이벤트를 발행하려면 리포지토리의 save()를 명시적으로 호출해야 하므로, 더티 체킹만으로 수정하는 코드에서는 이벤트가 나가지 않는다는 점에 주의한다.


Polling vs Pub/Sub

클라이언트가 새 데이터를 받는 방법 두 가지를 비교한 글이다. 폴링은 클라이언트가 주기적으로 서버에 물어보는 방식이고, long polling은 새 데이터가 생길 때까지 서버가 응답을 붙잡아 두어 빈 응답을 줄인다. Pub/Sub은 발행자가 채널에 메시지를 보내면 구독자에게 푸시된다. Redis Pub/Sub을 쓸 때 알아 둘 점은 Redis 문서가 밝히듯 전달 보장이 at-most-once라는 것이다. 구독자가 연결되어 있지 않거나 처리 중 실패하면 메시지는 사라진다. 메시지 보존과 재처리가 필요하면 Redis Streams나 Kafka 같은 로그 기반 도구를 써야 한다.


AWS DynamoDB

AWS 문서는 DynamoDB를 서버리스, 완전 관리형 분산 NoSQL 데이터베이스로 소개한다. 테이블의 각 항목은 파티션 키(필요하면 정렬 키와 함께)로 식별되고, 파티션 키 값에 따라 데이터가 여러 파티션에 분산된다. 관계형 DB와 달리 조인이 없으므로, 먼저 어떤 접근 패턴으로 읽을지를 정하고 그에 맞춰 키와 보조 인덱스를 설계해야 한다. 키로 찾는 Query는 효율적이지만 테이블 전체를 읽는 Scan은 비싸다는 점이 설계의 출발점이다.


ETC

ByteByteGo는 시스템 설계 면접 책으로 알려진 Alex Xu가 운영하는 사이트로, 대규모 시스템 설계 주제를 다이어그램 중심으로 설명하는 글과 뉴스레터를 낸다.

  • https://bytebytego.com/
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다