본문 바로가기

All

(62)
[개발생각] apache temporal을 이용한 예약푸시 기능 예약푸시 기능을 일반적으로는 redis zset을 이용해서 구현을 많이 한다.예약시간으로 소트된 푸시대상을 zset에서 꺼내서 전송하는 방식이다. 그리고 예약푸시의 취소기능은 해당 zset에서 해당 예약푸시 대상을 제거하는 방식으로 구현을 한다. 여기서 문제가 하나있는데, 취소기능을 구현하려면, zset하나만으로는 안되고, 해당 payload를 저장하고 있는 별도의 set구조가 하나더 필요하다. 조금 복잡해지는 감이 있다. 최근에 temporal이라는 오픈소스를 알게됬고, 이 예약푸시 기능을 temporal을 이용하면, 훨씬 간단해진다는것을 알았다. Workflow.sleep(Duration.ofSeconds(3)) 이 명령어 한줄이면 예약푸시는 끝난다... Workflow.await(delay) { c..
[개발생각] LMAX Disruptor에 대해서 한때 코인이 엄청나게 인기가 있을때, 거래소를 만들고 싶어서 많은 공부를 했던 기억이 있다.거래소를 만들기 위해서는 여러 기술등이 필요했고, 고민들이 필요했다. 거래소에서 거래 기능은 ms를 다투는 초저지연으로 기능이 구현되어야 한다.거래라고 하면, 비트코인 매도가 올라오면, 해당 매도 수량에 대해서 동시에 많은 구매자가 매수를 시도하게 되고,해당 기록은 디비에 남겨지고, 그 매수량에 따라 각각의 구매자의 수량을 더해줘야하고,매도자의 수량을 빼줘야하는 동시성이 아주 중요한 기능을 ms단위로 수행을 해야한다. 이기능을 단순히 db로 구현하게 되면,몇명만 동시에 거래를 수행해도, db가 맛이 가게되거나, 정합성이 맞지 않게 된다.여기서 해결책은 db에 로우락을 거는 방법인데,이러면, 동시처리 성능이 현저하게..
[개발생각] AI사용에 대한 전략 주말에 Google Antigravity2.0를 사용해서, 예전에 개인적으로 만든 앱의 UI가 너무 별로여서, 화면을 바꿔봤다.(아래 앱인데.. 디자인 너무 구려... 못볼지경 ㅠㅠ) 경제 정보들을 크롤링을 해서 보여주는 웹앱인데...저거 UI 내가 한땀한땀만들었던 기억이 나는데..만들고보니, 영, 디자인이 별로였다.앱 출시할까? 생각하다가 그냥 버려둔 웹앱인데.. Google Antigravity2.0 설치하고 Agent모드로 장난삼아, 여기여기 이렇게좀 바꿔줄래? 라고 했는데..오호!! 할정도로 화면을 바쭤주는것이다.. 저 그래프가 압권인데..크롤링한 차트이미지를 픽셀분석하여, 좌표 추출해서 그래프로 보여달라고 요청을 하니, 그렇게 구현을 해주었다.픽셀 좌표 추출 분석 기능이라, 직접 구현하려면..
[개발생각] 성능테스트에 대해서.. 프로젝트가 마무리에 접어들었고,이제는 성능 테스트를 통해, 서비스가 잘 제공될수있도록 해야하는 시점이 왔다.먼저 성능테스트를 위한 플랜을 작성해야하는데, 아래와 같이 진행을 하려고 한다. 1) 서비스하는 화면에서 평균적으로 호출하는 API의 갯수를 파악함 (평균 5개정도)2) 회원별로 API를 호출하도록 함.3) 내부적으로 캐시하는 데이터가 활용되지않도록, 파라미터등을 바꿀수있으면 랜덤으로 바꾸면서 캐시히트가 되지않는 방식으로 호출함.4) 대기열을 통한 완충되는 성능 측정 위 4개 사항을 감안해서 테스트를 진행할 예정이고,특히 내부적으로 캐시하는 데이터들도 존재하기에 캐시 히트가 최대한 되지않게 하여,잘못된 성능지표가 나오지 않도록 주의하려고 한다.또한 대기열 기능을 넣었는데, 해당 대기열이 서비스 부하..
[개발생각] Kotlin JDSL사용을 해야 했어.... 이번 프로젝트에서jpa를 적극적으로 사용하되, 배치등에서는 mybatis를 사용하고, 어드민에서는 querydsl을 사용하는것으로 결정을 했었다. api프로젝트에서는 jpa를 적극적으로 사용하여, 쿼리의 중복이나 엔티티의 관리를 철저히하는 방향으로 갔고,배치등에서는 배치잡의 특성상 조인과 조건등이 복잡한 쿼리가 다수존재하고,해당 조건들을 jpa로 풀어낼수가 없는 경우도 많아서, mybatis를 사용하기로 했다. 하지만, 지금 생각하면, 아쉬운 결정을 한것같다. api쪽은 jpa를 통해서 엔티티를 관리하는 방법을 사용했기에, 몇몇 통계데이터를 jpql쿼리로 사용하는것 말고는 거의 대부분을 Repository인터페이스를 통해 구현을 했기에, 일관성을 유지하고 중복을 제거하는 효과를 보았다. 하지만 배치쪽..
[개발생각] 미션 보상 기능을 코틀린 DSL 사용하면.. 미션이 여러개 있고, 그 미션의 조건들에 따라 보상을 다양하게 주어야하는 보상시스템을 만들어야 한다고 했을때, 가장 먼저 떠오르는방법은 전략패턴을 이용하여 각각의 미션 전략을 만들고, 그것들을 관리하고 동작하도록 하는 방법일것이다. 여기서 중요한 부분은 그 전략들을 선언할때, 인터페이스를 이용하여 구현을 하게 되는데,이 인터페이스가 때로는, 코드를 읽거나 흐름을 파악하기가 어렵게 숨어서 동작하게 되는 케이스가 있다. 로직이 복잡하여,공통 로직을 수행하고, 여러 다양한 추가 로직을 수행하는것들이 더덕더덕 붙을때가 이런 경우중에 하나이다. 이렇게 좀더 흐름을 쉽게 이해하고 한눈에 보이게 하려면, DSL을 구현하여 사용하면 좀더 깔끔하게 코드를 구성할수있다. DSL의미가 도메인 특화 언어(문법)이기때문에,도..
[개발생각] FSM을 사용하자. 미션 보상 서비스를 급하게 하나 만들어야 하는데... 하.. 만들지말까..정말..(오픈준비를 하는 시점에, 기획 해달라고 예전부터 요청한걸 묵혔다가, 기획 정리도 안된 예전 기획서 대로 그냥 만들어달라고하여, 꼭지가 돈다) 하지만, 아무리 그래도, 개발은 해야할듯하여, (개발자 직업을 택한 내가 죄인이다...ㅠ)주말에 어떻게 구현을 할것인가를 생각을 해봤는데..미션 보상 조건이 상당히 복잡하다.. 하지만 좀더 정리를 해보니, 여러조건이 순서대로 일어나고, 해당 순서대로 조건이 부합될때 보상을 해주는 흐름인것으로 파악을 했고, 순서가 있는 여러조건의 흐름에 맞는 설계는 FSM을 이용한 설계가 적합하지 않을까 생각이 들어서, 시스템 설계를 좀 해봤다. 우선, 기본적인 유저의 행동에 대한 이벤트는 이미 구현..
[개발생각] 다양한 객체를 하나의 컨슈머로 발행하여 사용하고 싶을때 다른 형태의 객체를 같은 토픽으로 발행하고, 해당 타입에 따라, 별도로 기능을 동작시키고 싶을때, objectMapper를 잘사용하면, 간단하게 구현을 할수가 있다. 아래와 같이 UserEvent객체는 각각,UserEventTypeEnum.AttendanceDayOnce 일때는 - UserEvent의 event필드는 AttendanceDayOnceEvent객체가 들어가고,UserEventTypeEnum.UserRecommend 일때는 - UserEvent의 event필드는 UserRecommendEvent객체가 들어간다. 그리고 UserEvent는 같은 토픽으로 이벤트 발행이 됬을때,UserEvent를 RawUserEvent형태로 deserialize를 한다. eventType를 판단하여,각 이벤트..
[개발생각] 프로와 아마추어의 차이 초등학교 2학년 겨울방학, 옆짝꿍이 얇은 문제집을 쉬는시간에 풀고있는데, 영어가 쏼라쏼라 있어서 호기심에 들여다 봤다.뭔지 도통 몰라서 짝꿍한테 물어보니,컴퓨터 프로그래밍 gw-basic이라는거란다.코드를 작성하면, 별표(*)가 사각형으로 출력하고, 삼각형으로 출력되고, 별모양도 출력된단다. 그 모양을 출력하려면 코드를 작성해야하는데 그게 프로그래밍이라는거란다.. 충격이였다..... 이상한 알수없는 문자를 입력하면, 저렇게 출력이 가능하다니, 멋있어보였다.(초등학교때 A,B,C,D 알파벳 처음봄...ㅋㅋㅋ)뭔가 존재하지 않는 존재를 창조하는 신이 된것같은, 창조자처럼 그당시에는 느껴졌다. "엄청 멋있다.." 그날 바로 엄마에게, 컴퓨터학원에 보내달라고, 내 생애 처음이자 마지막으로 이야기를 했다.(컴퓨터..
[개발생각] 도메인에서 코틀린으로 컴포지션 표현 도메인에서, 비즈니스 로직을 구현하는경우, 도메인서비스를 나누고, 해당 서비스를 조합하여, 비즈니스 로직을 구현하는 케이스가 많다.여기서 코드가 깔끔하지 않게 되는 부분이 존재한다. 아래 예제로 코드를 보면,class UserRegisterService ( val canUserJoinValidator:CanJoinTestUserValidator, val validUserChecker:ValidTestUserChecker, val rewardUserChecker:RewardTestUserChecker) { fun register(user:User) { with(user) { canUserJoinValidator.canJoinCheck(this); //can..