본문 바로가기

전체 글52

복합 인덱스는 순서가 전부입니다 복합 인덱스는 순서가 전부입니다—leftmost prefix 규칙과 함께 컬럼 순서 설계를 정리한 글입니다. 데이터베이스를 15년 가까이 다루면서 가장 자주 마주친 성능 사고는 화려한 튜닝 실패가 아니라 복합 인덱스의 컬럼 순서 하나를 잘못 잡아 벌어진 일이었습니다. 인덱스를 분명히 만들었는데도 쿼리가 느리다는 제보를 받고 EXPLAIN을 열어보면, 인덱스가 아예 안 쓰이거나 딱 절반만 쓰이고 있던 경우가 부지기수였죠. 오늘은 그 순서라는 주제 하나만 붙잡고 이야기해 보려 합니다.leftmost prefix, 인덱스가 왼쪽부터 읽히는 규칙복합 인덱스를 이해하는 핵심은 딱 하나입니다. B-Tree 인덱스는 정의된 컬럼 순서대로 왼쪽부터 정렬되어 저장된다는 사실입니다. (a, b, c)로 인덱스를 만들면,.. 2026. 7. 7.
SELECT에 컬럼 하나 더했다가 느려진 이유, 커버링 인덱스 SELECT에 컬럼 하나를 더했더니 느려졌던 경험과 커버링 인덱스를 index-only scan 관점에서 정리한 글입니다. 잘 태운 쿼리가 밀리초 단위로 끝나다가, 어느 날 배포 이후 몇 초씩 걸리기 시작했습니다. diff를 보니 바뀐 건 딱 한 줄, SELECT 목록에 컬럼 하나가 추가된 것뿐이었죠. WHERE도 ORDER BY도 그대로였는데 왜 느려졌을까요. 범인은 조용히 깨진 index-only scan이었습니다. 오늘은 커버링 인덱스라는 최적화가 왜 그렇게 빠른지, 또 왜 그렇게 쉽게 무너지는지를 실제 사고 기록과 함께 정리해 보겠습니다.index-only scan은 왜 그렇게 빠른가일반적인 index scan은 두 단계로 움직입니다. 먼저 B-tree 인덱스를 타고 내려가 조건에 맞는 엔트리를.. 2026. 7. 7.
폴링 주기를 아무리 조절해도 안 되던 실시간 알림 폴링 주기를 아무리 조절해도 안 되던 실시간 알림을, WebSocket으로 옮기며 배운 것들로 정리해봤습니다. 폴링으로 실시간을 흉내 내던 시절몇 해 전 사내 협업 툴에 알림 기능을 붙일 때 이야기입니다. 누군가 나를 멘션하거나 문서에 댓글을 달면 종 모양 아이콘에 빨간 점이 떠야 했죠. 저는 가장 익숙한 방식으로 시작했습니다. setInterval로 일정 주기마다 서버에 "나한테 새 알림 있나요?"라고 물어보는 polling이었습니다.// 5초마다 새 알림 개수를 물어보는 주기적 요청setInterval(async () => { const res = await fetch("/api/notifications/unread"); const { count } = await res.json(); updat.. 2026. 7. 6.
롤백을 빼먹고 낙관적 업데이트를 켰다가 UI가 꼬였습니다 롤백을 빼먹고 낙관적 업데이트를 키면 안된다는 걸 깨닫고 좋아요 버튼 하나에서 시작된 문제로 낙관적 업데이트의 핵심을 정리한 글입니다.응답을 기다리지 않고 먼저 그리는 이유좋아요 버튼을 누르면 하트가 채워지고 숫자가 하나 올라갑니다. 이 반응이 즉각적일수록 앱은 빠르고 매끄럽게 느껴지죠. 그런데 실제로 그 숫자를 바꾸려면 서버에 요청을 보내고, DB에 기록되고, 응답이 돌아와야 합니다. 네트워크가 조금만 느려도 여기에 수백 밀리초가 걸립니다. 서버가 "네, 반영했습니다"라고 확정해 줄 때까지 하트가 멍하니 굳어 있으면, 사용자는 버튼이 고장 났나 하고 한 번 더 누르게 되죠.이 대기 시간을 감추는 기법이 낙관적 업데이트(optimistic update)입니다. 핵심은 이름 그대로입니다. "요청은 어차피 .. 2026. 7. 6.
대용량 업로드로 서버 메모리가 터진 뒤 바꾼 방식 대용량 업로드로 서버 메모리가 터진 뒤 바꾼 방식을, 프리사인드 URL 직접 업로드로 옮긴 경험으로 정리한 글입니다.파일이 전부 서버를 거쳐 가던 구조처음 만든 첨부 기능은 아주 교과서적이었습니다. 클라이언트가 파일을 multipart/form-data로 API 서버에 보내면, 서버가 그 본문을 받아서 임시로 메모리나 디스크에 쌓아 두고, 다시 스토리지(S3)로 올려 주는 방식이었죠. 그림으로 그리면 파일이 클라이언트 → 서버 → 스토리지 순서로 서버를 한 번 반드시 통과하는 모양입니다. 코드도 단순해서, 프레임워크가 주는 업로드 미들웨어를 붙이고 핸들러에서 버퍼를 스토리지 SDK에 넘기면 끝이었습니다. 문서나 이미지처럼 몇 MB짜리를 다룰 때는 이 구조가 아무 문제가 없었습니다. 그런데 사용자가 첨부.. 2026. 7. 6.
scroll 이벤트로 짠 무한 스크롤을 갈아엎은 이유 scroll 이벤트로 짠 무한 스크롤을 갈아엎은 이유를, Intersection Observer로 옮기며 겪은 과정으로 정리한 글입니다.scroll 이벤트로 만든 무한 스크롤의 함정무한 스크롤을 처음 만들었을 때 저는 가장 익숙한 방법을 택했습니다. window에 scroll 이벤트를 붙이고, 스크롤이 바닥 근처에 닿으면 다음 페이지를 불러오는 방식이었죠. 상품 목록을 20개씩 끊어 보여 주는 화면이었는데, 로컬에서는 정말 잘 굴러갔습니다. 아래로 내리면 다음 상품이 착착 붙었으니까요.문제를 눈치챈 건 성능 탭을 열어 본 순간이었습니다. scroll 이벤트는 스크롤하는 내내 초당 수십 번씩 콜백을 호출합니다. 손가락으로 한 번 쭉 내리는 그 짧은 동작에도 콜백이 40번, 50번씩 실행되고 있었죠. 게다가.. 2026. 7. 5.