반응형 개발127 nested objectId serialize, deserialize 했을 때 값이 달라지는 이슈 해결하기 ObjectId 를 프로퍼티로 가지고 있는 class 를 serialize 한 후 deserialize 하면 값이 달라지는 현상이 일어난다. class RedisSerializerTest { @Test fun test() { val objectMapper = ObjectMapper() val serializer = Jackson2JsonRedisSerializer(ErrorExample::class.java) .also { it.setObjectMapper(objectMapper) } val id = ObjectId("6239341381210e5903548df7") val actual = ErrorExample(id) val serialize = serializer.serialize(actual) val.. 2022. 4. 23. 간단한 distributed-lock 구현하기 개발을 하다보면 데이터 정합성이 굉장히 중요한경우들이 발생한다. 간단한예로는 1인당 구매수량 제한이 걸려있는 상황이 있을 수 있다. 1인당 상품 1개밖에 구매하지 못한다고 했을때 2개이상을 구매하게 되면 절대 안된다. 우리는 아래와 같은 코드를 작성할 수 있을 것이다. fun order(userId: Long, productId: Long) { val alreadyBuy = orderRepository.findByUserId(userId, productId) if(alreadyBuy) { throw AlreadyBuyException("$userId is already buy") } orderRepository.save(userId, productId) } 언뜻 보기에는 위코드에는 전혀 문제점이 없어보인.. 2022. 4. 10. KSP 란 ? KSP 란 ? KSP (Kotlin Symbol Processing) 은 경량 컴파일러 플러그인을 만드는데 사용할 수 있는 API 이다. 왜 KSP 인가요 ? Kotlin 은 자체적으로 Annotation Processing 이 불가능했습니다. Java 의 Annotation Processing 을 사용하려면 KAPT 의 도움을 받아야 했습니다. KSP 는 설계 시점부터 kotlin 단에서 어노테이션 프로세싱이 어떻게 동작할지 고려했습니다. 그 결과 KSP 가 KAPT 보다 약 2배정도 빠르게 동작합니다. 무엇보다 kapt의 기능 개발이 중지되었습니다. 출처 : https://kotlinlang.org/docs/ksp-overview.html Kotlin Symbol Processing API | Kot.. 2022. 3. 6. IntelliJ 를 활용하여 KSP(Kotlin Symbol Processing API) 디버깅 하는방법 Terminal 을 이용하여 아래 명령어를 실행합니다. ./gradlew build --no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=y" 위의 명령어를 실행하면 아래와 같은 상태가 됩니다. IntelliJ 에서 Run > Attach to Process 를 클릭합니다 정상적으로 실행됐다면 아래와 같은 항목이 있을겁니다. 클릭해줍니다. 브레이크 포인트에서 정상적으로 멈추는 것을 확인할 수 있습니다. 2022. 3. 1. [Spring Batch] Chunk 기반의 Job 은 어떤 기준으로 작업을 끝낼까 ? 필자는 Spring Batch 를 이용하여 배치 프로그램을 만드는 업무를 시작하였다. 아래와 같은 동작을 하는 간단한 배치프로그램이었다. Reader 로 데이터읽기 Processor 로 데이터 처리하기 Writer 로 데이터 적재하기 굉장히 쉬울줄만 알았던 업무였으나 예상하지 못한 곳에서 막히고 말았다. Job 을 끝내야 하는데 어떤기준으로 끝나는지 알지 못했다. 그래서 Spring Batch 를 간단하게나마 분석하다가 알게된 내용을 공유하고자 이 글을 쓴다. Chunk 기반의 Job 은 별다른 설정을 하지 않는다면 SimpleChunkProvider 에 의해 동작하게 된다. SimpleChunkProvider.provider -> SimpleChunkProvider.read() -> SimpleChun.. 2021. 12. 29. Disk I/O 부하인데 메모리 증설을 한다고? 서비스를 운영하다보면 시스템에 부하가 발생하는 경우가 존재한다. 부하의 종류에는 다양하게 있는데 이번 글에서는 I/O 부하에 관련된 주제에 대해 다뤄보도록 하겠다. 필자는 Disk I/O 부하가 발생한다면 HDD → SSD 업그레이드를 하는것만이 해결방법이라고 생각했었다. 그러던 도중 “대규모 서비스를 지탱하는 기술” 이라는 책을 읽으면서 I/O 부하가 발생할 때 메모리를 증설하는것도 하나의 방법이 될 수 있다는 것을 알게되었다. 그렇다면 어떠한 원리로 Disk I/O 부하일 때 메모리를 늘리는 것이 부하를 줄이는 방법이 되는지 알아보겠다. 이 방법은 OS 캐시와 관련이 있다. (이 글은 Linux 를 기준으로 작성되었다.) OS 는 페이지 캐시라는 캐시구조를 가지고 있다. > 페이지 캐시란? OS 가 .. 2021. 12. 19. 선착순 쿠폰 이벤트는 어떻게 구현을 해야할까 ? (1) 이 게시글은 주니어개발자의 시야로 작성된 글입니다. 이 방법보다 좋은방법이 무수히 많을 수 있습니다. 비난보다는 좋은방법을 댓글로 알려주신다면 고민해보고 공부할 수 있을 것 같습니다 🙂 선착순 쿠폰이벤트 설계 는 서버개발자 면접을 보다보면 쉽게 마주칠 수 있는 문제중 하나이다. 면접뿐만 아니라 실제로 업무를 하다가도 한번쯤은 마주칠 문제이다. 이 때 중심적으로 봐야할건 크게 2가지가 있다고 생각한다. 동시성 트래픽 대부분의 선착순 쿠폰이벤트는 선착순 x개 를 주는 이벤트이다. 그렇다보니 특정시간대에 접속자(요청량)가 늘게 되고 이것을 버틸 수 있는 인프라가 준비되어야 할것이다. 두번째는 정확하게 x개의 쿠폰만 발급 되어야 하기 때문에 동시성을 고려하여 설계해야한다. 동시성이 고려되지 않는다면 x개 이상의.. 2021. 11. 9. MSA 환경에서 분산 트랜잭션 처리하는 방법 공부중인 주니어개발자가 작성하는 글입니다. 틀린 내용이 존재할 수 있으며 무조건적인 신뢰보다는 참고정도만 해주시면 감사하겠습니다. 틀린부분을 지적해주셔도 좋습니다. MSA 환경에서는 트랜잭션 처리가 중요하면서 까다롭습니다. MSA 환경에서는 각 서비스마다 저장소에 대한 접근권한을 따로 가지고 있을 확률이 높습니다. 이것이 무슨뜻인지 풀어서 설명해보겠습니다. User Service 와 Point Service 가 있다고 가정해보겠습니다. User Service 에서 사용하는 저장소는 User Service 에서만 접근할 수 있습니다. 또한, Point Service 에서 사용하는 저장소는 Point Service 에서만 접근할 수 있습니다. User Service 와 Point Service 가 엮여있는 .. 2021. 10. 4. MongoDB 여러개의 Document 한번에 insert 하기 다수의 도큐먼트를 insert 할때 1개씩 넣어주는것보다 한번에 여러개를 넣어주는것이 효율적이다. 100개의 도큐먼트가 있다고 가정할때 100번의 네트워크 비용보다 1번의 네트워크 비용이 저렴하기 때문이다 MongoDB에는 다수의 Document를 한번에 저장할 수 있는 insertMany 라는 기능을 지원한다. Spring-data-mongo 에서는 insertMany 를이용하여 여러개의 Document를 한번에 저장할 수 있는 saveAll 이라는 Method 를 제공한다. 아래는 saveAll 메소드의 구현내용이다. 내용을 본다면 전부 새로운 Document 여야 saveAll 기능을 효과적으로 이용할 수 있다는 것을 확인할 수 있다. 주의하도록 하자! public List saveAll(Itera.. 2021. 8. 22. [Spring] Kotlin 을 이용하여 MongoDB Query를 Type-safe 하게 작성하기 Spring data mongo 를 사용하게 되면 아래와 같은 방식으로 쿼리를 작성할 수 있다. Query() .addCriteria( Criteria.where("id").isEqualTo(1L) ) 이렇게 코딩하게 하게 된다면 문제점이 무엇일까 ? 크게 2가지로 나누어볼 수 있을 것이다. Type-safe 하지 않다. 만약 id가 Long 타입이 아니라 다른타입이라면 ? 바로 오류로 이어질 것이다. 오타로 인해 오류가 발생할 수 있다. id를 잘못하여 di 로 적는순간 오류로 이어질 것이다. id는 짧기에 이런일이 없겠지만 필드명이 길어지는 순간 위험도는 증가한다. Kotlin을 이용하여 Spring data mongo 를 이용한다면 이러한 문제점을 해결할 수 있다. org.springframewor.. 2021. 7. 30. [Spring] Interceptor postHandle이 동작을 안해요. Interceptor 란 ? 특정요청에 대한 요청을 가로채서 수행하기 전, 수행한 후 추가적인 작업을 할 수 있도록 해주는 것 preHandle : 실제 handle 이 실행되기 전 수행 postHandle : 실제 handle 이 수행된 후 실행 afterCompletion : 요청이 완료된 후 실행 ( View Render 가 완료된 후 ) 적용 방법은 간단하다. 아래와 같은 Interceptor 를 만든 후 등록해준다. @Component class DemoInterceptor : HandlerInterceptor { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Bool.. 2021. 7. 2. [ReactiveCrudRepository] 데이터 삭제가 안되는 이슈 굉장히 어이없는 실수였지만 같은 이슈로 고생하시는 분이 계실까 공유합니다. ReactiveCrudRepository 를 학습할 때 발생할 수 있는 이슈입니다. 필자는 webflux 와 ReactiveMongoRepository 를 이용하여 간단한 게시판 예제를 만들어 보고자 했습니다. 입력까지는 잘 되었으나 Delete가 되지 않는 이슈가 발생하였습니다. 코드는 아래와 같습니다. 보고 무엇이 문제인지 아시는 분은 뒤로가기를 클릭하셔도 됩니다. suspend fun delete(deleteRequest: ReplyDeleteRequest) { val replyId = ObjectId(deleteRequest.replyId) val requestId = deleteRequest.requestId Mono.j.. 2021. 5. 7. 이전 1 2 3 4 ··· 11 다음