본문 바로가기
반응형

전체 글202

[MongoDB] group 쿼리를 사용하여 Array<Object> 데이터 그룹화하기 MongoDB 를 사용하다보면 Array 형식인 데이터를 그룹화 해야할때가 있습니다. 예를들어 아래와 같은 데이터가 있고 user 의 type 별 평균값을 알고싶다고 가정해봅시다. { "id" : 1, "userId" : 1, "items" : [ {"type" : 1, "value" : 1}, {"type" : 2, "value" : 2}, ] }, { "id" : 2, "userId" : 1, "items" : [ {"type" : 1, "value" : 3}, {"type" : 2, "value" : 4}, ] } 눈으로 보면 굉장히 쉬워보입니다. userId 로 group 한 후 타입별로 평균값을 내기만하면됩니다. 이 쉬워보이는 작업을 필자는 오랜시간 삽질한후에 결과를 낼 수 있었습니다. 혹시나 .. 2022. 7. 3.
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.
2021년 하반기 회고 상반기에는 어떤 목표를 가졌었지 🤔 2021 상반기에는 “성장"과 “강의제작을 위한 공부" 를 목표로 삼았었다. 각각의 Item 에 노력한 점수를 매겨보자면 성장에 대한 노력은 5점! 강의제작을 위한 공부는 2점! 을 줄 수 있다. (5점 만점이다) 성장을 위해서는 퇴근 후, 주말에도 꾸준히 공부하며 노력했기에 조금의 고민끝에 5점을 줄 수 있었다. 강의제작을 위한 노력은 업무가 우선이다 라는 핑계 아닌 핑계로 부족했다고 생각하기에 2점밖에 주지 못했다. 하지만 긍정적인 성과도 있었다! 어떤 강의인지 조차도 명확하지 않았던 상반기에 비해 제작하고 싶은 강의를 정했다! 어떤 주제인지는 강의제작을 시작한다면 블로그를 통해 공개하도록 하겠다. 처음 맡는 기능 개발 🔥 5월에 이직해서 5~6월 기간에는 온보딩 .. 2022. 1. 6.
[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.
2021년 상반기 회고 목표 이직과 성장을 2021 년 상반기의 목표로 설정했다. 강의제작을 2021년의 목표로 설정했다. 이직 이직을 2021년 상반기의 목표로 설정했다. 그 이유는 여러가지가 있었을 것 같다. 가장 큰 이유는 더 이상 성장할 수 없다고 느낀게 컸던것 같다. 그렇다면 성장할 수 없다고 느낀 이유를 알아봐야 할 것 같다. Middle, Senior 개발자의 부재 재직하던 회사에서는 주로 신입만 채용했다. 그렇기에 20명 가까이 되는 개발자중 5년차(앱개발 3년 + 웹개발 2년) 1명, 7년차 1명, CTO 1명을 제외하고는 주니어 개발자로 구성되어 있었다. 아직 1~2년차밖에 안됐던 필자는 성장하는 것을 중요시 하였고 가장 빠르게 성장하는 방법은 잘못된 방향으로 가는 것을 누군가 잡아주는 것이라고 생각했다. M.. 2021. 8. 27.
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.