라벨이 스프링(Spring)인 게시물 표시

About JVM Warm up

이미지
Class Loading JVM 프로세스가 시작하면, 필요한 모든 클래스들은 클래스로더가  세가지 단계를 거쳐 메모리로 로드 한다. 이 과정은 lazy loading 기반으로 동작한다. - Bootstrap Class Loading: Bootstrap Class Loader 가 Java Class 들을 올린다. java.lang.Object 와 같이 JRE/lib/rt.jar 에 있는 필수적인 클래스를 올린다. - Extension Class Loading: ExtClassLoader 는 java.ext.dirs 경로에 있는 모든 JAR 파일들을 책임진다. ㅎGradle 혹은 Maven 기반 application 이 아닌, 개발자가 수동으로 추가한 JAR 들이다. - Application  Class Loading: AppClassLoader 가 application class path 에 있는 모든 클래스들을 올린다. Execution Engine Java 는 Interpret 과 Compile 을 모두 사용하는 Hybrid 이다. Java code 를 javac 를 통해서 컴파일하면 platform 독립적인 bytecode 로 변환된다. 이를 실행 시 JVM 은 runtime 에 interpret 통해서 native code 로 실행한다. Just-In-Time Compiler 는 runtime 에 자주 실행되는 코드(method 전체)를 native code 로 compile 한다. 이후에 재실행 시 바로 compiled 된 native code 를 사용한다. 이를 hotspot 이라 부른다. C1 - Client Compiler 빠른 시작과 좋은 반응 속도에 중점을 두고 있다. 최적화 수준은 C2 에 비해 낮지만 컴파일 시간이 짧아 초기 실행 속도를 빠르게 하여 사용자에게 더 빠른 반응을 제공할 수 있다. 복잡하지 않은 최적화로 짧은 시간에 코드를 컴파일하여, 어플리케이션이 빨리 실행될 수 있도록한다. C2- Server ...

About idempotent

이미지
 이번글에서는 프로그래밍에서 멱등성에 대해 정리하고 실제 상황에서 어떻게 구현해야지 멱등성을 달성할 수 있는지 작성하겠다. 멱등성이란?  영어로는 idempotent. 사전적 정의로는 "연산을 여러 번 적용하더라도 결과가 최초 실행 결과가 그대로 보존되는 성질을 의미" 이다. 실제 서비스에서 일어날 수 있는 상황에 멱등성을 통해서 해결해보자.  고객이 아마존이나 쿠팡에서 상품을 구매하려고 한다. 고객은 상품을 구매하기 위해서 결제를 해야 한다. 이 때 결제는 두 번 이상 실행되어서는 절대 안된다.  1. 일명 "따닥" 으로 고객이 버튼을 빠르게 두 번 클릭하는 상황이 발생할 수 있다. 2. 고객이 첫번째 결제 요청을 하고 실제로 결제가 처리되었지만 네트워크 오류로 응답이 전달되지 못하여 고객이 버튼을 다시 클릭하는 상황이 발생할 수 있다.  만 원을 결제했는데, 실제 2만원이 결제되는 최악의 상황은 발생하지 않아야 한다.  데이터베이스 고유 키 제약조건 (unique key constraint)  1. 결제 요청을 받으면 테이블에 새 레코드를 넣으려고 시도한다. 2-1. 새 레코드 추가에 성공했다면 이전에 처리한 적이 없는 결제 요청이다. 2-2. 새 레코드 추가에 실패했다면 이전에 받은 적이 있는 결제 요청이다. 이러한 중복 요청은 처리하지 않는다. 일회성 토큰 Nonce(Number used Only Once) UUID 또는 timestamp 와 같은 값을 사용하여 정확히 한 번만 사용할 수 있는 장치를 마련한다. 행위에 대한 혹은 도메인(결제)에 대한 식별자 역할을 한다. 이미 처리된 동일한 Nonce 값이 들어온 경우 중복 요청으로 간주하고 처리하지 않는다. 이 때 한 가지 고려할 점은 이 값을 서버에서 제공하여 일회용 토큰으로써 사용하는 것이다. 클라이언트에서 값을 받는것은 언제든지 위조 될 수 있기 때문이다. 간단한 예제 코드 UUID 일회용 결제 토큰을 Redis 를 활용하여 사용 여부를...

sneak peek jitpack

이미지
 생산성을 위해서 여러 프로젝트에서 반복적으로 사용되고 있거나 앞으로 사용될 프로젝트 코드를 효과적으로 관리하기 위해서 라이브러리로 관리하는 방법을 선택할 수 있다. 그렇다면 라이브러리를 어떻게 관리하는 것이 좋을까? java, kotlin 을 사용하여 개발을 할 경우, maven, gradle 을 통해서 의존성을 관리한다. 이 때 의존성들의 저장소는 Maven Central 혹은 jCenter(현재는 지원 중단) 처럼 공개 레포지토리일 수도 있고, 단체에서만 사용하는 사설 레포지토리일 수도 있다. 이러한 레포지토리를 지원하는 여러 툴이 있다.  Maven Central 에서 아티팩트를 올리기 위해서는 요건이 엄격하다. 따라서 JitPack 을 사용하여 찍먹해보겠다. 먼저 github 레포지토리 2개를 판다. 하나는 라이브러리용이고, 나머지는 라이브러리를 사용한다. 라이브러리 :  https://github.com/ndgndg91/hello-jitpack 아래는 build.gradle.kts 파일이다. maven-publish 플러그인을 사용해야 하며, publishing 을 설정해야한다. 아래는 재사용할 코드를 간단하게 작성해보았다. 그리고 git tag 를 통해 버전을 관리한다. https://github.com/ndgndg91/hello-jitpack/releases/tag/0.0.2 사용할 라이브러리를 작성하고 git tag 를 땄으면 1차 준비는 완료했다. 다음은 https://jitpack.io  에 가서 내가 작성한 github repository 를 검색한다. 0.0.2 이라는 git tag 를 확인할 수 있다. 그리고 build Log 를 확인할 수 있다. 여기까지 성공했다면 다음은 쉽다. https://jitpack.io/com/github/ndgndg91/hello-jitpack/0.0.2/build.log 사용:  https://github.com/ndgndg91/use-jitpack build....

Spring Boot Actuator readiness, liveness probes on k8s

이미지
Spring Boot 를 사용하여 서버 어플리케이션을 개발하고 Kubernetes 상에서 운영할 때 Container 의 상태를 확인하고 복구가 불가능한 경우 재시작 시켜야 되는지 알 필요가 있다. 또한 Container 가 트래픽을 받아들일 준비가 되었는지 상태를 알아야한다. 이때 각각 liveness, readiness probes 를 사용한다. 아래와 같이 Kubernetes deployment 에 liveness 와 readiness probes 를 설정한다. initialDelaySeconds 는 Container 가 실행되고 90초 이후에 설정된 path 에 Get 요청을 통해서 정상상태를 확인한다. 200 ~ 399 status code 를 5초이내에 응답 받으면 성공으로 확인한다. periodSeconds 는 10초 주기로 확인한다. 연속해서 3번 비정상 응답을 받을 경우 liveness 는 실패로 돌아가고 Kubelet 은 Container 를 재시작시킨다. 이번엔 Spring Boot 어플리케이션을 보자. gradle 을 사용할 때 아래와 같이 의존성을 추가한다. 그리고 application.yaml 에 아래와 같이 설정한다. 유의해야할 점은 exposure.include 에 * 를 쓰게되면 불필요한 정보가 모두 노출되어 보안에 취약해진다. 예를 들어, heapdump 또는 shutdown 같은 기능을 노출하게 되면 외부에서 공격점이 될 수 있다. /actuator path 요청시 아래와 같이 응답을 받는다.  exposure 에 health, info 만 설정해서 두가지만 확인할 수 있다. /actuator/health 를 확인해보자. endponint.health.probes.enabled=true 로 설정해서 liveness, readiness 를 지원한다. /actuator/health/liveness 와 /actuator/health/readiness 를 확인해보자.  spring boot application 에...

How to prevent replay attack?

이미지
Replay Attack 이란? replay attack 은 공격자가 유효한 네트워크 데이터 패킷을 가로채서 이후에 다시 사용하는 네트워크 공격의 유형이다. 데이터를 다시 전송하여 시스템이 정상적인 데이터로 처리하도록 한다. replay attack 은 실제로 정상적인 요청으로 보이기 때문에 탐지가 어렵다. 덧붙여 원래 전송이 암호환된 경우에도 성공할 수 있다. replay attack 은 반복적인 요청을 통해 시스템에 과부하를 줄 수 있다. 이로 인해 시스템의 정상적인 작동을 방해할 수 있다. 공격자는 그림과 같이 데이터 전송이 시작될 때까지 기다린다. 이후에 통신 채널을 스니핑하여 데이터를 추출한다. 공격자는 데이터를 입수하여 목적에 따라 데이터를 수정해서 다시 사용할 수도 있다. 수신자는 변조된 데이터를 받았지만 정상적인 데이터로 취급한다. 대표적인 4가지 유형이 있다. 네트워크, 무선, 세션, HTTP 가 있다. 네트워크 replay attack 은 공격자가 네트워크 트래픽을 가로챈 후 나중에 다시 전송한다. Wireshark 또는 tcpdump 와 같은 도구를 사용한다. 무선 replay attack 도 동일하게 무선 통신을 가로챈 다음 다시 전송한다. 세션 replay attack 은 두 당사자 간의 세션을 가로챕니다. HTTP replay attack 은 공격자가 HTTP 요청과 응답을 캡처하여 HTTP replay attack을 실행한다. 실제 예시 앨리스가 웹을 사용하여 온라인 뱅킹 계좌에 로그인하려고 한다고 가정한다. 앨리스가 로그인 자격 증명을 입력하고 제출 버튼을 클릭하면 로그인 요청이 인터넷을 통해 은행 서버로 전송된다. 공격자 밥은 네트워크를 모니터링하여 로그인 요청이 전송되는 것을 캡처한다. 그런 다음 밥은 앨리스가 계정에서 로그아웃할 때까지 기다렸다가 캡처한 로그인 요청을 은행 서버로 재전송한다. 로그인 요청이 유효하므로 서버는 이를 수락하고 밥에게 앨리스의 계정에 대한 액세스 권한을 부여한다. 어떻게 하면 Replay Attack...

Time Business Logic Testing on Spring (Kotlin, Java)

이미지
올해에 너무 글쓰는 것을 소홀히 했던것을 반성하며 남은 한 달 동안 글을 몇 개라도 조금 써보려고한다! Java, Kotlin 기반 Spring 프레임워크 환경에서 개발을 하다보면 시간과 관련된 로직이 필요할 수 있다. 이 때 어떻게 해결하면 좋을 지 테스트 코드는 어떻게 구성하면 좋을지 정리하려고 한다. 예를 들어, 특정 기간동안만 액션이 가능한 요구사항이 있다고 가정하자. 해당 기간이 시작하기전에 액션을 진행하지 못하도록 막아야 되고, 기간인 경우 가능하도록 그리고 기간이 끝났을 때 더이상 액션이 불가능하도록 막아야 한다. 이 예제를 바탕으로 간단한 샘플 코드를 정리해보았다. - Kotlin - Spring Boot 2.7.5 - Junit 5 우선 아래와 같이 zoneId 를 인자로 받는 now 함수를 작성하였다. CHANGEABLE_CLOKC 이 null 이 아닐경우 해당 Clock 을 기준으로 LocalDateTime 인스턴스를 생성한다. 그리고 now 를 테스트하기 위한 코드. CHANGEABLE_CLOCK 이 null 인 경우와 null 이 아닌 경우 두 가지 테스트 케이스이다. null 인경우에는 zoneId 에 따라서 인스턴스를 생성하기 때문에 빈 인자인 경우 UTC 기준, Kst ZoneId 제공시 kst 시간 인스턴스를 생성하여 9시간 차이를 확인하는 코드이다. 두번 째는 Clock 을 2025 년 1월 1일로 설정하여 now 함수 호출 시 CHANGEABLE_CLOCK 기준으로 인스턴스를 생성하게 되어있다. 자 그렇다면 기간과 관련된 로직과 해당 로직을 테스트하는 코드를 작성해보자. 아래와 같이 property 에서 기간을 주입받는 Service 를 test code 를 작성해보자. ReflectionTestUtils 를 통해서 Mocking 하는 service 에 기간을 주입한다. 그리고 CHANGEABLE_CLOCK 을 통해서 now() 함수가 기간에 속할 때와 아닐 때로 지정하여서 올바른 값을 반환하는지 검증한다. 또 다른 방법중 하...

About Spring AOP JDK Dynamic Proxy and CGLIB Dynamic Proxy

이미지
개요  Spring Framework 를 사용할 때 AOP 는 필수라고 말해도 과언이 아니다. Logging, Transaction Management, Caching, Security 등등 에서 주로 사용된다. 그렇다면 Spring AOP 는 어떻게 동작할까? 이번 글에서 정리를 해보려고 한다.  Spring 에서 AOP 는 JDK Dynamic Proxy 혹은 CGLIB Dynamic Proxy 를 사용하여 Proxy 를 생성하여 관리한다.  Proxy target object 가 하나 이상의 interface 를 구현하는 경우에는 JDK Dynamic Proxy 가 사용된다. 반대로 target object 가 interface 를 구현하지 않는다면 CGLIB proxy 가 생성된다. 하지만 proxy-target-class=true 로 설정한다면 interface 를 구현하더라도 CGLIB proxy 가 사용된다. CGLIB Proxy 를 사용할 때 고려해야 할 몇가지가 있다. - final method 는 overriding 이 불가능하기 때문에 advice 적용이 불가능하다.  - target class 가 final 일 경우 불가능하다. - Spring 3.2 version 전의 경우 CGLIB 와 관련된 jar 를 classpath 에 추가해주어야 한다. 3.2 version 부터는 org.springframework.cglib 로 repackaged 되어 spring-core jar 안에 포함이 되었다. - Spring 4.0 version 전의 경우 proxy target object 의 생성자는 두 번 호출되었다. 또한 기본 생성자가 필수였다. 4.0 version 이후부터는 Objenesis 를 통해서 proxy instance 가 생성될 때 더이상 생성자가 두 번 호출되지 않게 되었다. 일반적이지 않은 경우이지만 4.0 version 전에서는 proxy target object 의 생성자에 주요한 로직을...

Optimistic Concurrency Control VS Pessimistic Concurrency Control - What should i choose?

이미지
 Race Condition 즉 동시성 문제는 하나의 트랜잭션이 다른 트랜잭션에서 동시에 변경한 데이터를 읽거나 두 트랜잭션이 동시에 같은 데이터를 변경하고자 할 때 나타난다. 동시성 문제로 발생하는 버그는 타이밍에 운이 없을 때 발생하기 때문에 일반적인 테스트로 발견하기 어렵고, 타이밍 이슈는 간할적으로 발생할 수 있으며 해당 이슈를 재현하기 힘들고 추론도 힘들다. 어플리케이션 개발은 한 번에 한 사용자를 가정하고 개발하기도 어려운데 동시 사용자가 많다면 당연히 더 어렵다. 하지만 현실세계에서 동시성이 없는 어플리케이션이 없다고 봐도 무방할 만큼 동시성을 고려하지 않을 수 없다. 재고가 얼마 남지 않은 상품을 동시에 여러 구매자가 구매할 때, 하나의 회의실을 서로가 먼저 예약하려고 할 때, BTS 콘서트 티켓을 예매할 때.. 등등 Optimistic Concurrency Control 우리나라말로 해석하면 "낙관적 동시성 제어" 이다. 무엇이 낙관적일까? 위험한 상황이 발생할 가능성이 있을 때 Transaction 을 막는 대신 모든것이 괜찮아질 것이라고 희망을 갖고 계속 진행한다는 의미이다. 여기서 위험한 상황이란 다른 Transaction 이 현재 진행 중인 Transaction 이 변경하려고 하는 데이터와 동일한 데이터를 변경하고자 하는 상황을 의미한다. 그리고 막는다는 표현은 Database 관점에서 Exclusive Lock 을 통해서 Transaction 이 순서로 진행되는 것을 의미한다. 막지 않는 대신 각각의 Transaction 은 Commit 전에 Transaction 내에서 읽었던 Data 가 변경되지 않은것을 확인한다. 이는 이른바 MVCC(Multi Version Concurrency Control) 기법을 통해서 이루어진다. 만약 Data 의 변경이 감지된다면 해당 Transaction 은 Rollback 하고 재시작을 한다. Pessimistic Concurrency Control 낙관적의 반대인 비관적이라는 말은...