목차배경Java 진영에서는 꾸준히 LTS 버전을 출시하며 최신 트렌드와 기술적 요구사항을 준수하는 버전을 제공한다. 이번 글에서는 대표적인 Java LTS 버전인 Java 5, Java 8, Java 11, Java 17, Java 21에서 제공하는 대표적인 특징을 정리해본다. 간략히 정리해보자면 각 버전과 비교되는 대표적인 특징은 다음과 같다. Java 5: Generic, EnumJava 8: Lambda, Functioinal Interface, Stream API, OptionalJava 11: Lambda내 var 키워드 사용, HTTP Client API 표준화, ZGC 실험 도입Java 17: Record, Sealed ClassJava 21: Virtural Thread, Pattern M..
목차배경보통 분산 락 해제 로직은 Lua 스크립트로 작성하고 Eval을 날려서 원자적으로 실행한다. 왜냐하면 락 소유권 문제 때문이다.public boolean deleteIfValueEquals(String key, String expectedValue) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; DefaultRedisScript redisScript = new DefaultRedisScript(script, Long.class); return redisTemplate.execute(redisScript, Collection..
목차COW(Copy-on Write)메모리를 효율적으로 사용하여 데이터를 복제하는 기법. 예를 들어 아래와 같이 fork 시스템 콜을 통해 자식 프로세스를 생성하는 경우, 메모리 공간을 그대로 복제하지 않고 같은 공간을 참조하도록해 메모리를 효율적으로 사용한다.프로세스가 fork()를 통해 자식 프로세스를 생성하면 두 프로세스가 동일한 메모리 공간을 참조하다가 데이터 변경이 일어나면 서로 다른 메모리 공간을 바라보도록 한다. 쓰기 시점에 메모리 사용량이 발생한다.Redis에서의 COWCOW는 메모리를 효율적으로 사용하기 위한 기법이지만 Redis에서는 잘못 사용할 경우 메모리 사용량이 예상치 못하게 급증할 수 있어 주의가 필요하다. 예를 들어 위 프로세스에서, 부모 프로세스가 Page C의 데이터를 수정..
목차배경Redis는 HA를 위해 Replication를 제공한다. 이번 글에서는 Redis가 Replication를 제공하는 방법에 대해서 다룬다.복제(Replication)Master 노드와 Replica(= Slave) 노드로 구성하고, Master 노드의 변경 사항을 Replica 노드로 비동기로 반영한다. Master 노드의 장애가 발생하면, Replica 노드를 Master 노드로 승격하여(자동 Failover) HA를 제공한다. 이때 복제는 비동기로 진행하므로 승격 과정에서 데이터 유실이 발생할 수 있다.데이터 복제 동작 원리기본적으로 Replication stream을 통해 동기화한다. 부분적으로 이 Stream이 닫히는 경우 psync와 fsync로 동기화한다. 자세한 내용은 아래와 같다...
목차배경Java 또는 Kotlin 환경에서 백엔드 개발 업무를 하면서 그 전체 동작 구조를 이해하는 것은 필수적이다. 장애 발생시 빠른 대응이나 기술적 문제를 개선하여 서비스 성장에 기여할 수 있기 때문이다. 이번 글에서는 그 중 JVM의 Heap 영역을 다루는 프로세스인 GC를 Garbage Collection(GC)를 다룬다.Garbage Collection(GC)Java의 메모리 관리 방법 중의 하나, JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체(Garbage)를 모아 주기적으로 제거하는 프로세스이다. 주 목표는 개발자가 메모리 해제를 신경 쓰지 않으면서, 서비스에 방해가 안 되게, 서비스 영향 시간을 최소화하여 조용히 메모리를 관리하는 것이다..
목차배경Spring에서는 빈을 재생성할 수 있는 기능인 @RefreshScope를 제공한다. 빈 재생성을 통해 런타임 시점에 동적으로 설정 값을 바꿔 장애 발생시 배포 없이 Fallback을 수행하거나 Feature Flag로도 활용할 수 있다. 하지만 활용도가 높은만큼 Spring 컨테어너가 DB Connection이나 Transaction을 관리하는 방안을 제대로 이해하지 않고 사용하는 경우 빈을 재생성하는 과정에서 예상치 못한 장애를 일으킬 수 있어 주의가 필요하다.사용@RefreshScope은 아래와 같이 애노테이션 추가만으로 간편하게 사용할 수 있다.@RefreshScope@Componentpublic class PaymentClient { @Value("${external.payment...
목차배경단위 테스트 코드는 흔히 FIRST 원칙을 준수해야한다고 한다. 실행 속도가 빨라야하며(First) 외부로부터 격리(Isolated)되어 검증하고 하는 기능에 집중할 수 있어야하고 반복 가능(Repeatable)해야한다. 또한 사용자의 개입 없이 테스트 코드 스스로 검증(Self-validating)할 수 있어야하고 적절한 시점(Timeley)에 바로 작성할 수 있어야한다. 한편 실제 애플리케이션에서 기능을 작성할 때는 자연스레 데이터베이스 연결 등 외부 환경에 의존하게 된다. 이처럼 복잡한 애플리케이션에서 단위 테스트 원칙을 준수하여 효과적인 테스트 코드를 작성하기 위해서는 테스트 더블이 유용하게 사용된다. 특히 F와 I, R을 준수하기 쉽다.구성테스트 더블은 Dummy, Fake, Stub, ..
목차배경사이드 프로젝트를 진행하며 결제 API 연동 로직 개발에 많은 시간이 들었다. 결제 API는 금액이 걸려있고 엮여있는 비즈니스 로직(예를 들어 결제에 성공하면 재고 차감, 실패시 차감하지 않는다.)이 많아 연동 성공과 실패에 따른 케이스 처리가 중요했다. 사실 개발보다는 예외 처리가 더욱 중요했는데, 처음에는 발생할 것 같다고 예상되는 케이스를 먼저 구분하고 진행하려고 했다. 하지만 발생 가능한 예외 케이스와 예외 케이스별 적절한 처리 방식을 실제로 개발하기 전에 완벽히 예측하기는 거의 불가능했다. 개발이나 시스템 설계보다는 오히려 예외 케이스 구분에 더 많은 시간을 쏟게 되었다. 따라서 완벽한 결과물보다는 우선 개발하고나서 개선해보는 방향으로 진행했고 결과적으로 개발 소요 시간을 줄이면서 실제로..
목차배경애플리케이션을 개발하며 캐시는 특히나 성능 향상과 서비스 고가용성에 필수적인 부분이다. 하지만 무작정 캐시를 도입했다가, 캐시의 잘못된 동작으로 서비스 품질이 저하되고 경우에 따라 SPOF가 될 수 있다. 이번에는 Spring의 @Cacheable 애노테이션을 비롯한 Spring Cache 동작 원리와 캐시 Fallback 구성 방안을 다룬다.Spring Cache 계층 구조Spring에서는 캐시가 필요한 메서드에 key와 value를 지정하여 @Cacheable 애노테이션을 통해 캐시를 적용할 수 있다.@Cacheable(value = "memberCache", key = "#memberId")public Member getMember(Long memberId) { return member..
목차배경이커머스에서 상품 주문 중 재고 수량 체크와 차감은 필수적이다. 이때 동일한 상품에 대해 동시 재고 차감 요청이 발생할 수 있고, 이를 제어하기 위해서는 다양한 락 전략을 적용할 수 있다. 예를 들어 동시성 제어를 위해 비관적 락을 적용할 수 있다. 이때 요청이 무한정 대기하여 리소스를 소모하는 것을 방지하기 위해 락 획득 요청 유효시간을 설정할 필요가 있는데, 락 획득 요청 유효시간 테스트 과정에서 발생했던 DBMS별로 상이한 동작으로 인한 HikariCP와 Jpa의 처리 한계와 Spring을 통한 해결 방법을 다룬다.내용사용자가 주문을 요청하면, 요청 수량만큼 재고가 남아있는지 확인하고 가능하다면 재고를 차감하여 주문을 진행한다. 이는 아래와 같이 작성할 수 있다. (Kotlin + Spri..
목차배경동시성 이슈를 해결하는 방법에는 비관적 락을 비롯해 낙관적 락, 분산 락 등이 있다. 이번 사례에서는 JPA에서 제공하는 @Version 애너테이션을 활용해 낙관적 락으로 동시성 이슈를 방지하는 방법과 그 과정에서 느낀 비즈니스 로직 이해의 중요성을 정리한다. 낙관적 락을 선정한 이유는 그동안 비관적 락과 분산 락으로 동시성 문제를 해결해보았으며, 사이드 프로젝트를 통해 JPA 사용 역량을 높이고자 하기 때문이다.주문/결제 API예를 들어 다음과 같은 주문 결제 API가 호출하는 비즈니스 로직이 있다고 가정하자. 주문 결제 요청을 받으면 주문 상태를 진행 중으로 변경하고, PG사의 결제 API를 호출하여 고객의 카드 또는 계좌에서 주문 금액만큼 결제를 진행한다. 이후 결제가 성공하면 주문 상태를 ..
목차배경모듈간 직접적인 의존성 제거가 필요한 특정 상황에서 스프링의 EventListener를 사용하면 이벤트 기반으로 Loose Coupling을 구현하여 처리할 수 있다. 또한 비동기 처리가 필요한 경우에도 적절하게 사용할 수 있다.@EventListener예를 들어 회원 가입에 성공하면 사용자에게 환영 메일을 보내는 요구사항을 가정해본다. 가장 간단하게 다음과 같이 작성할 수 있다. 이는 두 모듈간 강한 의존성을 갖고 직접적으로 의존한다.@Serviceclass UserService ( private val userRepository: UserRepository, private val emailService: EmailService,) { fun register(userName: String, em..