| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- 생성자
- 통신방식
- undefined
- BDD
- 모델링
- Polling
- 테스트코드
- 티스토리챌린지
- TDD
- jest
- Long Polling
- Node.js
- java
- 코딩 컨벤션
- Web push
- Spring
- EventEmitter
- JavaScript
- callback
- node js
- 오블완
- emit
- waterfall
- Nodejs
- DB
- null
- Service Worker
- 노드
- 코드관리
- kotlin
- Today
- Total
목록전체 글 (97)
Will Of Rough
개요서비스에서 오늘 접속한 유니크 사용자 수(DAU) 를 세는 건 너무 흔한 요구사항이라, 지금까지는 별 고민 없이 Set 에 밀어넣고 크기를 세면 끝이라고 생각했다.사실 중복을 제거하고 센다는 게 그것 말고 다른 방법이 있긴 한가? 싶었고, Redis 에 HyperLogLog 라는게 있다는 건 알고 있었지만 이름부터 어려워 보이는데다 정확하지도 않은 카운트를 굳이 써야하나 싶기도 했고.그런데 사용자가 수억 규모가 되면 "세기 위해 전부 기억하는" Set 방식은 카운트 하나에 기가바이트를 지불하게 되고, HyperLogLog 는 — 원소를 기억하는 대신 가장 희귀한 기록 하나로 전체 규모를 역산한다 — 는 발상으로 이걸 12KB 에 해결한다는걸 알게되었다.가끔씩 HyperLogLog 를 설명하는 글을 보..
개요실시간 알림이나 채팅 기능을 만든다고 하면 다들 반사적으로 WebSocket 부터 꺼낸다.사실 나도 실시간이면 무조건 WebSocket 아닌가? 싶었고, SSE 는 이름만 들어봤지 폴링보다 조금 나은 반쪽짜리 기술 정도로 이해하고 있었다, 어차피 양방향이 되는 WebSocket 이 있는데 굳이 단방향밖에 안 되는 기술을 배워야하나 싶기도 했고.그런데 막상 서버에서 클라이언트로 내려주기만 하면 되는 기능을 구현하려고 찾아보니, 실시간이라는 요구사항은 하나가 아니라 — 데이터가 흐르는 방향이 프로토콜을 결정한다 — 는걸 알게되었다.가끔씩 SSE 와 WebSocket 을 비교하는 글을 보면 표 하나 그려두고 채팅이면 WebSocket, 알림이면 SSE 하고 끝~인 경우가 많아, 왜 그렇게 갈리는지 그 밑바..
개요선착순 쿠폰이나 타임세일처럼 순간적으로 트래픽이 몰리는 기능에서, 재고 차감은 UPDATE 문 한 줄이면 되는 단순한 작업처럼 보인다.처음에는 @Transactional 안에서 재고를 조회하고 차감하면 끝 아닌가? 싶었지만, 수만 명이 같은 상품 하나를 동시에 노리는 순간 모든 트랜잭션이 그 상품 row 하나의 락 앞에 줄을 서고, 락 대기가 길어지면 커넥션 풀이 마르고, 커넥션 풀이 마르면 재고와 상관없는 다른 API 까지 같이 죽는걸 보고 나서야 이게 쿼리의 문제가 아니라 구조의 문제라는 생각이 들었다.파보니 답은 차감을 DB 밖으로 꺼내는 것이었는데 — 차감은 Redis 가 메모리에서 원자적으로 받아내고, DB 반영은 뒤로 미루고, 그 사이에 벌어진 어긋남은 정산으로 갚는 구조 — 라는걸 알게되..
개요2편에서 메시지가 지나가는 길과 Ack, prefetch 까지 구조를 훑었다.처음에는 구조를 알았으니 쓰는 데는 문제 없다고 생각했었다, 그런데 브로커에 맡긴 메시지가 정말 한 번은 도착하냐고 물으면 대답이 막혔고, 실패한 메시지는 어디서 몇 번을 재시도해야 하는건가? 싶기도 했고 브로커 노드가 통째로 죽으면 큐에 있던 메시지는 어떻게 되는건가 싶었다.파보니 이 질문들의 답은 하나로 이어져 있었는데 — 유실을 막는 장치들은 유실을 중복으로 바꿀 뿐이고, 그 중복까지 삼키는 것이 설계의 완성 — 이라는걸 알게되었다.가끔씩 전달 보장 글을 보면 confirm 켜라, durable 켜라 나열하고 끝~인 경우가 많아, 그 장치들이 정확히 어느 시점에 무엇까지 보장하는지와 켜고 난 뒤에 남는 중복·순서·재시도..
개요1편에서 RabbitMQ 는 소비하면 지우는, 브로커가 메시지별 장부를 적는 쪽의 메시지 큐라고 정리했다.처음에는 Exchange 니 Binding 이니 하는 용어가 그냥 큐에 붙는 옵션 정도인 줄 알았고, 코드로도 큐 이름 하나 적어서 보내면 되는거 아닌가? 싶었는데 왜 예제마다 굳이 Exchange 를 선언하고 바인딩을 거는건가 싶기도 했고 Connection 을 놔두고 Channel 은 또 왜 따로 있는건가 싶었다.파보니 이 등장인물들은 전부 — Producer 와 Consumer 가 서로를 몰라도 되게 만드는 간접층이고, 층마다 자기만의 함정을 하나씩 갖고 있는 것 — 이라는걸 알게되었다.가끔씩 RabbitMQ 구조 글을 보면 Exchange 타입 네 가지 그림만 나열하고 끝~인 경우가 많아,..
개요서버 두 대가 데이터를 주고받는 가장 단순한 방법은 HTTP 로 직접 호출하는 것이다.처음에는 그걸로 충분한데 왜 다들 메시지 큐를 두는건가 싶었고, RabbitMQ 와 Kafka 도 그냥 같은 물건의 브랜드 차이 정도로 이해하고 넘어갔는데, 그러면 주문 서버가 호출한 알림 서버가 죽어 있으면 주문까지 같이 실패해야 하는건가? 싶기도 했고 둘 중 하나를 골라야 한다면 무슨 기준으로 고르는건가 싶었다.파보니 두 브로커는 같은 물건의 성능 차이가 아니라 — 소비한 메시지를 지우는 브로커와, 로그로 남겨두는 브로커라는 철학이 다른 물건이고, 각자의 강점이 그대로 각자의 약점이 되는 구조 — 라는걸 알게되었다.가끔씩 두 브로커를 비교하는 글을 보면 처리량 숫자 표 하나 두고 Kafka 가 더 빠르다 하고 끝..
개요Spring 에서 @Transactional 이나 @Async 를 붙이면, Spring 이 프록시 객체를 만들어 실제 객체 대신 호출을 받는다.처음에는 프록시면 다 같은 프록시인 줄 알았고, JDK Dynamic Proxy 와 CGLIB 두 가지가 있다는 건 어디선가 봤지만 인터페이스가 있으면 전자, 없으면 후자 정도로 이해하고 넘어갔는데, 그러면 왜 Spring Boot 는 인터페이스가 멀쩡히 있어도 CGLIB 를 기본으로 쓰는건가? 싶기도 했고 final 메서드에 붙인 @Transactional 이 조용히 무시되는건 또 왜인가 싶었다.파보니 두 방식은 — 하나는 인터페이스를 구현한 형제고 하나는 클래스를 상속한 자식이라, 족보가 다르면 못 하는 것도 다르다 — 는걸 알게되었다.가끔씩 두 프록시를 ..
개요참조라고 하면 변수에 담는 그 참조 하나뿐인 줄 알았고, Soft·Weak·Phantom 은 면접 대비 목록에서 이름만 외웠었다.사실 이런 특수 참조는 라이브러리 만드는 사람들의 물건이지 애플리케이션 코드와는 상관없다고 생각했었다, 왜냐하면 몇 년을 개발하면서 new WeakReference 를 직접 써본 적이 한 번도 없었기 때문이다.그런데 Netty 가 쓰는 다이렉트 메모리에서 OutOfMemoryError: Direct buffer memory 를 만나 원인을 파다 보니, 그 메모리의 해제가 다름 아닌 PhantomReference 기반의 Cleaner 로 일어난다는걸 발견했고 — 참조의 강도라는게 결국 "GC 에게 이 객체를 어느 조건에서 회수해도 되는지 알려주는 신호 체계" 이며, GC 의 ..
