DEVELOPER NOTES
글로 정리하는 개발 노트
Astro, TypeScript, 그리고 웹 플랫폼에 대한 실험과 기록.
Network · HTTP
주소창 한 줄의 여정 - 밑단을 잇는다
주소창에 한 줄 치고 엔터를 누르면, 이름을 주소로 바꾸고 기계를 찾고 문을 두드리고 자물쇠를 채운 뒤에야 HTTP가 오간다. 지금까지의 밑단을 통째로 이어 그 여정을 따라가며, 어디서 느려지는지까지 짚고 시리즈를 닫는다.
Network · NAT
내 컴퓨터는 왜 직접 안 닿나 - NAT
집 안 기기 수십 개가 공인 IP 하나를 나눠 쓴다. 공유기가 나갈 때 주소를 바꿔치기하고 돌아올 때 되돌리는 NAT 덕이다. 이게 IPv4 부족을 버티게 하는 동시에, 왜 밖에서 내 기계를 직접 못 부르는지도 설명한다.
Network · UDP
그냥 던진다 - UDP
TCP의 악수와 재전송은 신뢰성을 주지만 그만큼 느리다. UDP는 그 안전장치를 다 버리고 그냥 던진다. 도착 보장도 순서 보장도 없는 대신 빠르고 단순한 이 방식이, 왜 영상·게임·HTTP/3에 맞는지.
Network · TCP
안 끊기고 순서대로 오게 - TCP
인터넷은 패킷을 잃기도 뒤섞기도 한다. 그런데 웹페이지는 한 글자도 안 틀리고 순서대로 도착한다. 그 신뢰성을 맡는 TCP - 먼저 악수로 연결을 맺고, 조각마다 받았다는 확인을 주고받으며, 잃은 건 다시 보낸다.
Network · Port
한 기계 안에서 어느 프로세스로 - 포트
IP 주소는 기계까지만 데려다준다. 그 기계 안에는 웹 서버, DB, SSH가 함께 도는데, 도착한 패킷이 어느 프로세스로 가야 하나. 그걸 정하는 문 번호가 포트다. 소켓과 파일 디스크립터로 어떻게 프로세스에 이어지는지까지.
Network · DNS
이름을 주소로 바꾼다 - DNS
우리는 142.250.72.14가 아니라 google.com을 친다. 그 이름을 IP 주소로 바꿔주는 게 DNS다. 이름을 뒤에서부터 단계로 찾아 내려가는 조회, 그리고 캐싱과 TTL이 왜 DNS 변경을 늦게 퍼지게 하는지.
Network · IP
패킷은 어느 기계로 가나 - IP 주소
편지를 보내려면 주소가 필요하듯, 패킷도 목적지 기계의 주소가 필요하다. IP 주소가 무엇이고, 패킷이 어떻게 라우터를 하나씩 건너 목적지까지 가는지 - 라우터가 전체 길을 모르고도 배달이 되는 이유.
Network · Layers
HTTP 밑에 무엇이 있나 - 네트워크 계층
HTTP는 네트워크의 맨 위 한 겹일 뿐이다. 그 밑에 데이터를 실어 나르는 TCP가 있고, 또 그 밑에 기계를 찾아가는 IP가 있다. 봉투 안의 봉투처럼 층으로 쌓인 이 구조를 보면, 왜 각 층을 따로 갈아끼울 수 있는지가 풀린다.
OS · Blocking IO
스레드가 왜 멈춰 서 있나 - 블로킹 I/O
파일이나 네트워크를 기다리는 동안 스레드는 그 자리에 멈춰 선다. 이 블로킹이 왜 스레드 풀과 연결 풀의 크기를 정하고, 비동기가 왜 나왔는지 - 밑바닥의 OS를 다시 백엔드 개념으로 이으며 시리즈를 닫는다.
OS · File Descriptor
파일·소켓·연결이 다 같은 번호다 - 파일 디스크립터
파일도, 네트워크 소켓도, DB 연결도 프로그램은 작은 정수 하나로 가리킨다. 파일 디스크립터라는 이 번호가 무엇이고, 왜 유한하며, 안 닫으면 왜 새는지 - 연결 풀이 반납에 그토록 예민한 이유가 여기 있다.
OS · System Call
프로그램이 OS에 일을 시키는 법 - 시스템 콜
프로그램은 파일을 직접 읽거나 네트워크로 직접 보내지 못한다. OS에게 부탁해야 한다. 유저 모드와 커널 모드라는 두 세계, 그 경계를 넘는 창구인 시스템 콜, 그리고 그 넘나듦이 왜 공짜가 아닌지.
OS · Context Switch
CPU 하나로 어떻게 동시에 도나 - 컨텍스트 스위치
CPU 코어는 몇 개뿐인데 스레드는 수천 개다. OS는 스레드를 아주 빠르게 번갈아 태워 동시에 도는 것처럼 보이게 한다. 그 바꿔 태우기에 드는 비용이, 왜 스레드가 많을수록 손해인지를 설명한다.
OS · Thread
스레드는 프로세스와 뭐가 다른가 - 스레드
한 프로세스 안에서 여러 일을 동시에 하는 실행 흐름이 스레드다. 같은 주소공간을 공유하되 자기 스택만 따로 갖는다. 이 공유가 왜 스레드를 싸게 만들면서 동시에 공유 상태 문제를 낳는지.
OS · Stack
지역변수와 new는 어디에 사나 - 스택과 힙
같은 주소공간 안에서도 지역변수와 직접 할당한 객체는 사는 곳이 다르다. 함수마다 쌓였다 자동으로 치워지는 스택, 직접 얻어 오래 두는 힙 - 스택 오버플로와 메모리 누수가 왜 생기는지가 여기서 갈린다.
OS · Virtual Memory
왜 내 메모리를 남이 못 건드리나 - 가상 메모리
두 프로그램이 똑같은 메모리 주소를 써도 서로 안 부딪힌다. 각 프로세스가 보는 주소는 가짜고, OS가 진짜 물리 메모리로 몰래 바꿔치기하기 때문이다. 이 한 겹이 격리를 공짜로 만든다.
OS · Process
프로그램이 실행되면 무엇이 되나 - 프로세스
디스크의 파일 하나가 실행되면 프로세스가 된다. 자기만의 메모리, 열어둔 자원 목록, 어디까지 실행했는지 - 프로세스가 받는 세 가지를 보면, 왜 프로그램끼리 서로의 메모리를 못 건드리는지가 풀린다.
Event-Driven · DLQ
실무의 이벤트 - 언제 꺼내고 무엇을 조심하나
패턴을 안다고 다 써야 하는 건 아니다. 이벤트 스키마라는 계약, 순서와 독약 메시지, 배포 후 안 보이는 흐름을 트레이싱으로 잇기, 그리고 애초에 이벤트가 답이 아닌 자리까지 짚으며 시리즈를 닫는다.
Event-Driven · Event Sourcing
상태 대신 이벤트를 진실로 - 이벤트 소싱
보통 DB엔 지금 상태만 남고 어쩌다 그렇게 됐는지는 덮어써져 사라진다. 상태 대신 일어난 일의 목록을 저장하고 필요하면 재생해 상태를 얻는 이벤트 소싱, 감사와 되감기가 공짜로 딸려오는 대신 치르는 값.
Event-Driven · CQRS
쓰는 모델과 읽는 모델을 가른다 - CQRS
쓸 때 좋은 데이터 모양과 읽을 때 좋은 모양은 다르다. 한 모델로 둘 다 하려면 한쪽이 고생한다. 명령과 조회를 아예 갈라 각자 최적의 모양을 갖게 하는 CQRS, 그 대가인 동기화 지연까지.
Event-Driven · Saga
흐름을 한곳에서 - 사가: 오케스트레이션
코레오그래피는 흐름이 코드 어디에도 없어 커지면 안 보인다. 지휘자 하나를 두고 각 단계에 명령을 보내 흐름을 한곳에 쥐는 오케스트레이션, 그리고 둘을 언제 고르나.
Event-Driven · Saga
여럿에 걸친 일을 잇는다 - 사가: 코레오그래피
주문 하나가 결제·재고·배송을 건드리는데, 서비스가 갈려 한 트랜잭션으로 못 묶는다. 여러 로컬 트랜잭션을 이벤트로 잇고 실패하면 보상으로 되돌리는 사가, 그중 코디네이터 없이 서로 반응하는 코레오그래피.
Event-Driven · Outbox
저장과 발행을 한 묶음으로 - 아웃박스
DB에 저장하고 이벤트를 발행하는 사이에 프로세스가 죽으면, 저장은 됐는데 발행은 안 된 어중간한 상태가 남는다. 두 시스템을 한 트랜잭션으로 못 묶는 문제를, 발행을 DB에 먼저 적어 푼다.
Event-Driven · Idempotency
같은 메시지가 두 번 와도 - 멱등 소비자
메시지 시스템은 확실히 전달하려다 중복을 만든다. 받는 쪽이 두 번 처리하면 결제가 두 번 된다. 처리한 걸 기억하거나 연산 자체를 멱등하게 만들어, 두 번 받아도 한 번처럼 삼키는 법.
Event-Driven · Architecture
이으면 생기는 문제들 - 이벤트 패턴 지도
쪼개고 비동기로 이으면 편해지는 대신 새 문제가 생긴다. 중복·발행 누락·분산 트랜잭션·복잡한 조회 - 벽마다 정해진 패턴이 있다. 그 이름들의 지도를 먼저 편다.
Testing · CI
실무의 테스트 - 믿을 수 있는 테스트를 유지한다
테스트는 짜는 것보다 믿을 수 있게 유지하는 게 어렵다. 빠름·독립·결정적이라는 좋은 테스트의 성질, CI에서 자동으로 도는 이유, 무엇을 자동화하고 무엇은 사람이 하는지로 시리즈를 닫는다.
Testing · TDD
TDD - 실패하는 테스트부터 쓴다
테스트를 나중이 아니라 먼저 쓴다. 빨강-초록-리팩터 사이클, 왜 먼저 쓰면 요구가 고정되고 설계 압력이 생기는지, 그리고 TDD를 둘러싼 오해와 안 맞는 자리까지.
Testing · Integration Test
통합과 E2E - 진짜를 붙여 본다
단위가 다 초록인데 합치면 안 돈다. 이음매는 진짜를 붙여야 보인다. 통합 테스트와 계약 테스트, E2E가 왜 느리고 불안정한지, 플래키를 어떻게 다루는지까지.
Testing · Coverage
무엇을 테스트하고 무엇은 안 하나
다 테스트하려다 다 못 한다. 구현이 아니라 행동을, 정상 경로가 아니라 경계를 노린다. 커버리지 숫자의 함정과 깨지기 쉬운 테스트, 그리고 테스트하지 않아도 되는 것까지.
Testing · Test Double
테스트 더블 - 진짜 대신 가짜를 끼운다
단위 테스트를 빠르게 하려면 진짜 DB·외부 API를 떼고 가짜로 갈아끼워야 한다. dummy·stub·spy·mock·fake가 어떻게 다른지, 상태 검증과 행위 검증의 차이, 과한 목의 함정까지.
Testing · Test Pyramid
테스트 피라미드 - 무엇을 얼마나 짜나
테스트는 단위·통합·E2E 세 층으로 나뉜다. 아래로 갈수록 빠르고 싸서 많이, 위로 갈수록 느리고 불안정해서 적게. 뒤집히면 아이스크림콘 안티패턴이 되는 이유까지.
Testing · Unit Test
왜 테스트하나 - 확신이 변경을 두렵지 않게 한다
테스트를 귀찮은 숙제로 여기면 손해는 나중에 온다 - 고칠 때마다 무서워지는 코드로. 테스트가 회귀 안전망이자 실행되는 문서이자 설계 압력이 되는 이유를 본다.
Observability · OpenTelemetry
세 기둥을 하나로 - 실무의 관측가능성
로그·메트릭·트레이스가 따로 놀면 반쪽이다. trace ID가 셋을 하나로 잇고, OpenTelemetry가 한 규격으로 심게 한다. 무엇을 남길지의 비용 문제와 함께 시리즈를 닫는다.
Observability · Alerting
헬스체크와 알림 - 터지기 전에, 터졌을 때
신호를 모았으면 언제 사람을 부를지 정해야 한다. 살아있음과 준비됨의 차이, 증상에 알리고 원인은 대시보드에 두는 원칙, SLO와 에러 예산, 그리고 알림 피로까지.
Observability · Tracing
분산 트레이싱 - 요청 하나를 끝까지 따라간다
여러 서비스를 지나는 요청 하나가 어디서 느렸는지, 로그와 메트릭은 못 답한다. 스팬과 트레이스, 폭포수로 병목을 짚는 법, 서비스 경계를 넘는 상관ID까지.
Observability · Metrics
메트릭 - 지금 시스템이 어떤가
메트릭은 시간에 따른 수다. 카운터·게이지·히스토그램 세 유형, 라벨이 비용이 되는 카디널리티 함정, 그리고 평균이 아니라 분포로 봐야 하는 이유까지.
Observability · Logging
로그 - 무슨 일이 있었나
로그는 사건의 기록이다. 문자열로 찍으면 사람만 읽고 기계는 못 읽는다. 구조화 로깅으로 필드를 남기고, 상관ID로 흩어진 줄을 한 요청으로 꿰는 법까지.
Observability · Monitoring
관측가능성이 푸는 문제 - 터졌는데 어디가 문제인지 모른다
서비스가 여러 조각으로 나뉘면 '느리다' 신고 하나에 범인 후보가 수십이다. 모니터링과 관측가능성이 어떻게 다른지, 세 기둥(로그·메트릭·트레이스)이 각자 어떤 질문에 답하는지 본다.
DevOps · Deployment
무중단 배포 - 안 끄고 갈아끼운다
구버전을 끄고 신버전을 켜는 사이에는 공백이 생긴다. 그 공백을 없애는 전략들(롤링·블루그린·카나리)이 각각 무엇을 사고 무엇을 파는지 본다.
Scaling · Resilience
서킷 브레이커와 백프레셔 - 번지는 것을 끊는다
한 서비스의 장애는 그걸 부르는 쪽으로 번지고, 빠른 생산자는 느린 소비자를 무너뜨린다. 그 번짐을 반대 방향에서 끊는 두 장치를 본다.
Cryptography · Password
비밀번호는 암호화하지 않는다 - 되읽을 게 아니라 맞춰볼 것이다
서버는 비밀번호를 되읽을 필요가 없다. 그래서 암호화가 아니라 해시로 저장한다. 솔트를 섞고, 일부러 느린 해시를 쓰는 이유까지.
Cryptography · Digital Signature
디지털 서명과 인증서 - 이 공개키가 진짜 네 것인가
개인키로 서명하고 공개키로 검증한다. HMAC과 달리 부인을 못 한다. 그리고 그 공개키가 진짜 그 사람 것임은 인증서와 신뢰 사슬이 보증한다.
Cryptography · Hash
해시와 HMAC - 바뀌었는지를 안다
해시는 되돌릴 수 없는 지문이다. 충돌은 반드시 있지만 못 찾을 뿐. 그런데 해시만으론 위조를 못 막아서, 비밀키를 섞은 HMAC이 필요하다.
Cryptography · Public Key
공개키 암호와 하이브리드 - 열쇠를 둘로 나눈다
잠그는 열쇠는 공개하고 여는 열쇠만 감춘다. 그래서 키 분배 문제가 풀린다. 대신 느려서, 실제 통신은 공개키로 대칭키만 건네는 하이브리드로 간다.
Cryptography · AES
대칭키 암호 - 열쇠 하나로 잠그고 연다
AES는 블록 단위로 감춘다. 같은 블록이 같게 나오면 새는 이유, 열쇠 길이가 안전을 정하는 법, 그리고 남는 단 하나의 문제 - 열쇠를 어떻게 건네나.
Cryptography · Encryption
암호화가 푸는 문제 - 무엇을 감추고 무엇은 못 감추나
암호화·해싱·인코딩은 다른 일을 한다. 되돌릴 수 있느냐로 셋을 가르고, 암호가 지키는 세 가지와 못 지키는 것까지 본다.
DevOps · CI/CD
CI/CD - 사람이 안 하게 만든다
배포 절차를 사람이 기억하면 언젠가 한 단계를 빠뜨린다. 이 블로그를 실제로 배포하는 워크플로를 재료로 각 단계가 왜 있는지 본다.
DevOps · Nginx
리버스 프록시와 Nginx
앱 앞에 하나를 세우면 라우팅·TLS 종료·정적 서빙·버퍼링이 앱 밖으로 나간다. 로드 밸런서와 어떻게 다른지까지.
DevOps · Kubernetes
쿠버네티스 - 누가 돌보나
해야 할 일을 시키는 대신 원하는 상태를 적어둔다. 그 한 가지 발상이 자가 치유와 스케일링을 같이 만들어낸다.
DevOps · Docker
Docker - 이미지를 어떻게 만드나
Dockerfile 한 줄이 레이어 하나가 된다. 캐시가 어디서 깨지는지 알면 빌드 시간과 이미지 크기가 같이 잡힌다.
DevOps · Container
컨테이너 - 프로세스를 싸 들고 다닌다
OS를 통째로 복제하지 않고도 환경을 같이 옮기는 방법. 격리·이미지·레이어를 VM과 비교해서 잡는다.
DevOps · Container
내 컴퓨터에선 되는데
로컬에선 멀쩡한 코드가 서버에서만 죽는다. 그 차이가 어디서 오는지 보면 컨테이너가 왜 나왔는지가 보인다.
Architecture · Consistency
나누면 정합성을 잃는다
두 서비스의 숫자가 다르다. CAP는 셋 중 둘을 고르라는 말이 아니고, 실무의 선택은 이분법이 아니라 눈금이다.
Architecture · Event-Driven
Pub/Sub과 이벤트 기반
일을 시키는 대신 일어난 일을 알린다. 듣는 쪽을 몰라도 되는 대신, 흐름이 코드에서 사라진다.
Architecture · Message Queue
메시지 큐 - 기다리지 않게 만든다
지금 안 해도 되는 일을 쌓아두고 나중에 처리한다. 대신 실패가 나중에 오고, 중복이 오고, 순서가 흔들린다.
Architecture · Microservices
서비스끼리 어떻게 부르나
동기 호출은 가장 단순한 답이지만, 한 곳의 느려짐이 전체로 번지는 통로이기도 하다. 타임아웃·재시도·차단기로 그 통로를 좁힌다.
Architecture · Microservices
나누면 무엇이 달라지나
배포·장애·확장 단위를 갈라놓는 대신 네트워크가 끼어든다. 얻는 것과 새로 지는 비용을 나란히 놓는다.
Architecture · Monolith
모놀리스가 먼저다
한 덩어리로 만드는 건 아직 못 나눈 상태가 아니라 정상적인 출발점이다. 무엇이 쉬워지고, 언제부터 아파지는가.
Rate Limiting · Scaling
Rate Limiting - 넘치면 막는다
더 받는 법으로는 막을 수 없는 유입이 있다. 토큰 버킷 하나를 깊게 보고, 무엇을 기준으로 세고 서버가 여러 대일 때 카운터를 어디에 두는지까지 간다.
CDN · Cache
CDN - 사용자 가까이 둔다
서버를 아무리 늘려도 거리는 안 줄어든다. 사본을 사용자 근처에 두는 층이 무엇을 해결하고, 공유 캐시라서 무엇이 위험한지 본다.
Load Balancer · Scaling
로드 밸런서 - 앞에서 나눠준다
서버를 여러 대 두면 누가 어디로 보낼지 정해야 한다. 분배 알고리즘보다 중요한 건 어느 층에서 보는가, 죽은 서버를 어떻게 아는가, TLS를 어디서 푸는가다.
Scaling · Architecture
수직이냐 수평이냐
서버를 키우는 것과 늘리는 것은 난이도가 다르다. 늘리는 쪽이 어려운 이유는 서버가 아니라 서버가 들고 있는 상태에 있다.
Cache · Redis
캐시가 프로세스 밖으로 나가면
서버마다 캐시가 달라지는 문제를 공용 캐시로 푼다. 대신 네트워크·직렬화·단일 장애점·스탬피드라는 계산서가 새로 온다.
Cache · Performance
캐시 - 같은 답을 두 번 만들지 않는다
한 번 만든 결과를 들고 있다가 다시 준다. 쉬운 이야기라서 위험한데, 진짜 알맹이는 무엇에는 캐시를 붙이면 안 되는가다.
Performance · Scaling
무엇이 먼저 무너지나
부하가 늘면 전부가 고르게 느려지는 게 아니라 가장 좁은 한 곳이 먼저 막힌다. 도구를 고르기 전에 그 자리를 찾는 이야기.
Security · Browser
CSRF - 브라우저가 쿠키를 알아서 보낸다
응답을 못 읽어도 요청은 나간다. 동일 출처 정책이 원래 막지 않던 틈에서 위조 요청이 성립하는 조건과, 토큰·SameSite로 그 조건을 깨는 방법.
Security · Browser
XSS - 남의 스크립트가 내 페이지에서 돈다
데이터로 넣은 글자가 코드가 되는 순간 출처는 이미 같아져 있다. 동일 출처 정책이 왜 못 막는지, 방어가 왜 출력 자리에 있는지.
Security · Browser
CORS - 막은 게 아니라 안 알려주는 것
서버 로그는 200인데 브라우저는 에러를 낸다. CORS는 동일 출처 정책을 뚫는 구멍이 아니라 서버가 예외를 승인하는 절차이고, 애초에 공격을 막는 물건이 아니다.
Security · Browser
같은 출처만 믿는다
브라우저가 출처를 스킴·호스트·포트로 가르고, 다른 출처의 응답을 스크립트에 넘기지 않는 이유. 이 규칙 하나에서 CORS·XSS·CSRF가 갈라져 나온다.
Authorization · RBAC
권한을 어떻게 표현하나
역할과 속성, 그리고 스코프. 권한을 무엇으로 적을지와 어디서 검사할지를 함께 정한다.
OAuth2 · Authorization
OAuth 2.0 - 열쇠를 주지 않고 권한만 넘긴다
구글로 로그인이 내 비밀번호를 받지 않는 이유. Authorization Code + PKCE 흐름 하나를 끝까지 따라간다.
Session · JWT
세션이냐 토큰이냐
같은 문제에 정반대 답을 낸 두 방식을 네 개의 축으로 견준다. 틀린 선택이 아니라 다른 선택이다.
JWT · Token
JWT - 서버가 기억하지 않는다
토큰 안에 신원을 담고 위조만 막는다. 세 조각 구조와 검증 절차, 그리고 서명은 암호화가 아니라는 사실.
Session · Cookie
쿠키와 세션 - 서버가 기억한다
로그인 결과를 서버가 들고 있고 브라우저는 번호표만 든다. 쿠키·세션 ID·저장소·만료를 장면으로 따라간다.
Authentication · Authorization
인증과 인가는 다른 질문이다
로그인은 됐는데 403이 뜬다. 누구인가와 무엇을 할 수 있나는 서로 다른 질문이고, 실패했을 때 답도 다르다.
API · Versioning
버저닝과 하위 호환
v2를 냈는데 v1을 못 내리는 상황을 어떻게 피하나. 깨지 않고 바꾸는 방법이 먼저이고, 버전을 가르는 건 그다음이다.
API · GraphQL
GraphQL - 필요한 것만 받는다
화면 하나에 API를 다섯 번 부르는 문제를 GraphQL은 어떻게 다르게 푸나. REST와의 차이와 그 대가만 본다.
API · JSON
JSON - 왜 이게 표준이 됐나
타입이 여섯 개뿐인 형식이 어떻게 API의 기본이 됐나. 큰 정수·날짜·null이라는 세 함정과 스키마 없음의 대가.
API · REST
상태 코드와 에러 응답
200 OK인데 본문에 error가 있는 응답은 왜 문제인가. 상태 코드를 고르는 기준과 에러 본문을 한 벌로 통일하는 법.
API · REST
Endpoint 설계
같은 목록을 주는 URL이 여럿이 되지 않게 하는 법. 컬렉션과 단건, 중첩 기준, 필터·정렬·페이징 파라미터와 목록 응답 모양을 정한다.
API · REST
REST - 자원으로 말한다
/getUserList는 왜 REST가 아닌가. 동사를 URL에서 걷어내고 자원과 메서드로 말하는 방식을 잡는다.
API · Design
API는 약속이다
내부 코드는 언제든 고치지만 공개한 API는 남이 이미 그 모양에 맞춰 코드를 짜뒀다. API를 계약으로 보는 관점을 잡는다.
WebSocket · HTTP
WebSocket - 요청 없이 받는다
서버가 먼저 말을 걸 수 없다는 전제를 뒤집는 방법들. 폴링부터 SSE, 그리고 프로토콜을 바꾸는 WebSocket까지.
HTTPS · TLS
HTTPS - 무엇을 감추고 무엇을 못 감추나
자물쇠가 떴다고 다 가려진 게 아니다. HTTPS가 실제로 감추는 것과, 감싸고도 그대로 노출되는 것을 가른다.
HTTP · Web
캐시 - 안 보내는 게 가장 빠르다
배포했는데 사용자 화면이 안 바뀐다. 그 사고에서 출발해 Cache-Control·ETag·조건부 요청이 무엇을 정하는지 본다.
HTTP · Web
연결을 매번 새로 맺지 않는다
요청을 보내려면 먼저 연결부터 맺어야 한다. 그 준비 비용을 줄이려는 시도가 HTTP 버전을 올려온 이유다.
HTTP · Web
상태를 안 갖는다는 것
서버는 방금 로그인한 사람도 다음 요청에서 못 알아본다. 그 불편을 일부러 감수한 이유와, 그래서 상태를 어디에 두게 됐는지.
HTTP · Web
요청과 응답 - 한 번의 왕복에 무엇이 오가나
주소창에 한 줄 쳤을 때 실제로 오가는 건 글자 몇 줄이다. 그 글자가 어떻게 생겼고 어디를 지나는지 본다.
Connection Pooling · Database
Connection Pooling
스레드가 DB를 부르려면 연결이 필요한데, 연결은 만드는 것도 무제한도 비싸다. 미리 열어 재사용하되, 연결은 DB가 상한을 정하는 자원이다.
Thread Pool · Concurrency
Thread Pool
스레드는 만드는 것도 무제한도 비싸다. 미리 만들어 재사용하고, 그 개수로 동시 실행을 제한하는 것 - Thread Pool.
Concurrency · Thread
Concurrency
서버는 요청을 동시에 처리해야 한다. 그런데 여러 스레드가 같은 것을 건드리면 조용히 어긋난다 - 경쟁 상태와 그걸 막는 법.
Pagination · Database
Pagination
100만 건을 한 번에 줄 순 없다. 나눠 주는 두 방법 - offset과 cursor, 그리고 왜 offset이 뒤로 갈수록 느려지고 밀리는가.
Idempotency · HTTP
Idempotency
네트워크는 못 믿는다. 같은 요청이 두 번 와도 한 번 한 것과 같게 - 멱등성과, POST를 멱등하게 만드는 Idempotency Key, 그리고 그 키를 누가 어떻게 만드는가.
JPA · ORM
Persistence Context
save()를 안 불렀는데 UPDATE가 나간다. JPA가 엔티티를 관리하는 공간, 영속성 컨텍스트로 그 수수께끼를 푼다.
ORM · JPA
ORM
자바 객체와 DB 테이블 사이를 손으로 나르지 않는다. JPA로 개념을 잡고, ORM이 만드는 SQL까지 본다.
Middleware · Spring
Middleware
핸들러마다 반복되는 공통 처리를 요청과 응답 사이 한 줄로 세운다. Spring의 Filter로 개념을 잡는다.
Active Record · Design Pattern
Active Record
객체가 자기를 저장할 줄 안다. Repository와 정반대 선택이고, 틀린 선택이 아니라 다른 선택이다.
Aggregate · Design Pattern
Aggregate
어디까지가 한 덩어리인가. 함께 지켜야 할 것을 묶고, 그 경계를 트랜잭션과 저장소가 따라간다.
Value Object · Design Pattern
Entity와 Value Object
무엇으로 같음을 판단하나. 식별자로 같은 것과 값이 같으면 같은 것, 그 둘을 갈라 쓰는 이유.
Domain Model · Design Pattern
로직을 어디에 둘 것인가
업무 규칙을 절차에 늘어놓을 것인가, 객체에 넣을 것인가. 트랜잭션 스크립트와 도메인 모델, 그리고 둘을 가르는 기준.
Unit of Work · Design Pattern
Unit of Work
저장할 때마다 DB에 쓰면 업무의 반쪽만 남을 수 있다. 변경을 한 단위로 모았다가 한 번에 반영하는 패턴.
DTO · Design Pattern
DTO
엔티티를 그대로 내보내면 DB 구조가 곧 API 계약이 된다. 계층을 건너는 전용 그릇을 따로 두는 패턴.
Service Layer · Design Pattern
Service Layer
컨트롤러에 업무 절차가 쌓인다. 절차를 서비스 계층으로 옮겨, 입구가 여럿이어도 규칙은 한 곳에 있게 만드는 패턴.
Repository Pattern · Design Pattern
Repository Pattern
비즈니스 로직에서 DB 코드를 걷어낸다. 데이터 접근을 인터페이스 뒤로 숨겨, 구현을 갈아끼워도 로직은 그대로 두는 패턴.
GoF · Behavioral
Interpreter
작은 언어의 문법을 객체 트리로 표현하고 그 트리를 재귀로 평가하는, 드물지만 발상이 또렷한 행위 패턴.
GoF · Behavioral
Visitor
객체 구조는 그대로 두고, 그 위를 도는 새 연산을 바깥의 방문자로 추가하는 행위 패턴.
GoF · Behavioral
Memento
객체의 내부 상태를 캡슐화를 깨지 않고 스냅샷으로 저장해뒀다가 되돌리는 행위 패턴.
GoF · Behavioral
Mediator
객체들이 서로 직접 참조하며 얽히지 않도록, 중재자를 하나 세워 소통을 그리로 몰아주는 행위 패턴.
GoF · Behavioral
Iterator
컬렉션의 속을 열어 보지 않고 원소를 순서대로 훑을 때. 순회를 캡슐화해 내부 구조와 순회 코드를 떼어 놓는 행위 패턴.
GoF · Behavioral
Chain of Responsibility
누가 처리할지 미리 못 정할 때. 요청을 처리자들의 사슬에 흘려보내 처리 가능한 자가 맡게 하는 행위 패턴.
GoF · Behavioral
State
상태에 따라 행동이 갈릴 때. 상태를 객체로 만들어 거대한 조건문을 다형성으로 바꾸는 행위 패턴.
GoF · Behavioral
Template Method
여러 흐름이 큰 틀은 같은데 한 단계만 다를 때. 뼈대를 상위가 고정하고 달라지는 구멍만 하위가 채우는 행위 패턴.
GoF · Behavioral
Command
요청을 나중에 실행하거나 되돌리거나 기록하고 싶을 때. 요청 자체를 객체로 캡슐화하는 행위 패턴.
GoF · Behavioral
Observer
상태가 바뀔 때마다 관심 있는 것들을 일일이 불러야 할 때. 구독한 여럿에게 자동으로 통지하는 행위 패턴.
GoF · Behavioral
Strategy
기준이 늘 때마다 if/switch를 고쳐야 할 때. 알고리즘을 인터페이스로 빼 런타임에 갈아끼우는 행위 패턴.
GoF · Structural
Flyweight
같은 객체를 수백만 개 만들어 메모리가 터질 때. 공유 가능한 부분을 뽑아 하나만 만들어 나눠 쓰는 구조 패턴.
GoF · Structural
Bridge
두 축이 곱해지며 클래스가 폭발할 때. '무엇'과 '어떻게'를 두 계층으로 갈라 다리로 잇고, 각자 독립해 자라게 하는 구조 패턴.
GoF · Structural
Composite
파일과 폴더처럼 개별과 묶음이 섞인 트리를, 클라이언트가 구분 없이 똑같이 다루게 하는 구조 패턴.
GoF · Structural
Facade
하나 하려고 여러 개를 순서대로 불러야 할 때. 복잡한 하위 시스템 앞에 단순한 창구 하나를 세우는 구조 패턴.
GoF · Structural
Proxy
진짜 객체 앞에 대리인을 세워 접근을 제어한다. 지연 로딩·권한 검사·캐싱을 진짜를 건드리지 않고 끼우는 구조 패턴.
GoF · Structural
Decorator
상속으로 조합을 만들면 클래스가 폭발한다. 같은 인터페이스로 감싸 기능을 덧입히고, 여러 겹으로 쌓는 구조 패턴.
GoF · Structural
Adapter
내 코드가 기대하는 인터페이스와 남의 코드가 주는 인터페이스가 안 맞을 때. 사이에 번역기를 끼워 붙게 만드는 구조 패턴.
GoF · Creational
Prototype
처음부터 만들지 않고, 이미 있는 객체를 복제해 새로 만든다. 얕은 복사의 함정과 clone()의 문제까지 다루는 마지막 생성 패턴.
GoF · Creational
Builder
인자가 많고 대부분 선택인 객체를, 생성자 폭발 없이 단계별로 조립한다. 읽히고, 필수를 강제하고, 끝에서 불변으로 굳히는 생성 패턴.
GoF · Creational
Abstract Factory
따로 만들면 짝이 어긋난다. 서로 맞아야 하는 관련 객체들을 한 세트로 만들어, 섞이지 않게 보장하는 생성 패턴.
GoF · Creational
Factory Method
객체를 만드는 코드가 구체 타입에 박히지 않게. 무엇을 만들지 하위 클래스가 결정하도록 생성을 메서드로 빼는 생성 패턴.
GoF · Creational
Singleton
앱 전체에 인스턴스가 딱 하나만 있어야 할 때. 만드는 법은 간단하지만, 멀티스레드와 전역 상태라는 두 함정이 따라오는 생성 패턴.
SOLID · DIP
Dependency Inversion Principle
고수준 정책이 저수준 구현에 매달리지 않게. 둘 사이에 추상화를 두어 의존의 방향을 뒤집는 마지막 SOLID 원칙.
SOLID · ISP
Interface Segregation Principle
쓰지도 않는 메서드에 의존하게 만들지 마라. 뚱뚱한 인터페이스를 역할별로 쪼개는 네 번째 SOLID 원칙.
SOLID · LSP
Liskov Substitution Principle
자식은 부모 자리에 넣어도 약속을 깨면 안 된다. 상속은 구조가 아니라 계약을 물려받는 것 - 세 번째 SOLID 원칙.
SOLID · OCP
Open/Closed Principle
새 동작은 추가하되 기존 코드는 고치지 않는다. 타입이 늘 때마다 if-else를 여는 대신, 다형성으로 확장점을 두는 두 번째 SOLID 원칙.
SOLID · SRP
Single Responsibility Principle
클래스가 바뀌는 이유는 하나여야 한다. '일'이 아니라 '이유'로 나누는 첫 번째 SOLID 원칙.
Database · Storage
B-tree와 LSM-tree - 읽기와 쓰기 중 무엇을 팔 것인가
8바이트를 바꾸려고 8KB를 다시 쓴다. 그게 싫으면 뒤집으면 되는데, 그러면 읽기가 대가를 낸다.
Database · Scaling
복제 - 방금 쓴 걸 왜 못 읽는가
읽기를 늘리려고 복제했더니 시간이 뒤엉킨다. 그리고 격리 수준은 이걸 막아주지 않는다.
Database · Scaling
파티셔닝과 샤딩 - 나누면 무엇을 잃는가
둘의 경계는 '누가 조각을 아는가'다. 그리고 샤딩하는 순간 지금까지의 트랜잭션 이야기가 대부분 무효가 된다.
Database · Design
키 설계 - 무엇으로 행을 가리킬 것인가
자연키가 배신하는 순간, 대리키를 쓰면서 흔히 빠뜨리는 것, 그리고 UUID가 인덱스를 망가뜨리는 이유.
Database · Design
정규화 - 한 테이블은 한 가지 사실만 말한다
정규형을 외우지 않고도 판단하는 법, 그리고 반정규화가 실제로 무엇을 사고파는 거래인지.
Database · Performance
실행 계획 - 세 가지만 보면 된다
EXPLAIN 출력이 외계어처럼 보일 때. 트리를 어느 방향으로 읽고, 어디서 범인을 찾는가.
Database · Join
조인 알고리즘 - DB는 세 가지 방법 중 하나를 고른다
중첩 반복·해시·정렬 병합이 각각 언제 이기는지, 그리고 조인이 갑자기 느려지는 진짜 이유.
Database · Index
인덱스 - 왜 빠르고, 왜 만들어도 안 타는가
B-tree가 이진트리가 아닌 이유부터, 인덱스를 만들어놓고도 풀스캔이 도는 다섯 가지 경우까지.
Database · Storage
블록 - DB는 바이트를 모른다
4바이트를 읽으려고 8KB를 읽는다. 낭비처럼 보이는 이 단위가 뒤에 나올 거의 모든 것의 바닥이다.
Database · Transaction
MVCC - 잠그는 대신 버전을 쌓는다
읽기가 쓰기를 막지 않게 만든 대가로 무엇을 떠안게 되는가. 버전 체인, 스냅샷, 그리고 청소 문제.
Database · Transaction
락과 데드락 - 잠가서 지키는 방식의 대가
공유락·배타락에서 2단계 잠금까지, 그리고 두 트랜잭션이 서로를 영원히 기다리게 되는 과정.
Database · Transaction
격리 수준 - 같이 읽고 쓰면 무엇이 깨지는가
더티 리드·반복 불가능 읽기·팬텀 리드가 언제 생기는지, 그리고 격리 수준이라는 이름이 왜 믿을 게 못 되는지.
Database · Transaction
트랜잭션과 ACID - 네 글자의 무게가 다르다
원자성·일관성·격리성·지속성이 각각 무엇을 보장하는지, 그리고 왜 넷 중 하나만 협상 대상인지.