본문 바로가기

분류 전체보기53

폴링 주기를 아무리 조절해도 안 되던 실시간 알림 폴링 주기를 아무리 조절해도 안 되던 실시간 알림을, 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.
오버페칭이 싫어 GraphQL로 갔다가 만난 것들 오버페칭이 싫어 GraphQL로 갔다가 만난 것들을, 실제 전환을 겪으며 알게 된 장단점 위주로 정리한 글입니다. REST에서 반복되던 오버페칭과 언더페칭지금도 기억나는 화면이 하나 있습니다. 2년 전 사내 커머스 앱의 상품 상세 페이지였는데요, 이 화면 하나를 그리려고 클라이언트가 REST 엔드포인트를 세 번이나 호출하고 있었습니다. 상품 기본 정보를 받으려 /products/{id} 를 부르고, 리뷰 요약을 받으려 /products/{id}/reviews 를 부르고, 판매자 배지를 받으려 다시 /sellers/{id} 를 부르는 식이었죠. 화면 하나에 요청 세 번, 그것도 앞 응답이 와야 뒤 요청 주소를 만들 수 있어서 순차로 이어지곤 했습니다.이렇게 필요한 데이터를 한 번에 못 받아 여러 엔드포인트.. 2026. 7. 5.
인덱스를 걸었는데 더 느려진 이유 인덱스를 걸었는데 왜 더 느려진 이유—읽기를 빠르게 하려다 쓰기를 느리게 만든 경험에서 인덱스의 양면을 정리한 글입니다.인덱스는 어떻게 조회를 빠르게 하는가인덱스를 처음 배울 때 가장 와닿았던 비유는 두꺼운 전공 책 맨 뒤의 색인이었습니다. 500쪽짜리 책에서 "트랜잭션"이라는 단어를 찾는다고 해 보죠. 1쪽부터 한 장씩 넘기며 눈으로 훑으면 언젠가는 찾겠지만, 그 사이 시간은 하염없이 흘러갑니다. 반면 책 뒤 색인에는 단어가 가나다순으로 정렬돼 있고, "트랜잭션 … 210, 233쪽"처럼 위치가 적혀 있습니다. 우리는 색인에서 단어를 빠르게 짚은 뒤 곧장 해당 쪽으로 넘어가죠. 데이터베이스 인덱스가 하는 일이 정확히 이것입니다.인덱스 없이 조회하면 데이터베이스는 테이블의 모든 행을 처음부터 끝까지 읽습.. 2026. 7. 5.