정적 배포 편에서 배포했는데 안 바뀐다는 문제를 슬쩍 짚었잖아요. 이번 편은 그 이야기를 제대로 파고드는 자리예요. 배포를 좀 해 본 분이라면 누구나 한 번쯤 분명히 올렸는데 왜 옛날 화면이지 하며 진땀 뺀 경험이 있을 거예요. 저도 신입 때 이걸로 다 고쳤다고 보고했다가 정작 사용자 화면은 그대로라 크게 무안했던 기억이 있어요. 그 범인이 바로 캐시인데, 이 편에서 캐시랑 잘 지내는 법을 확실히 익혀 볼게요.


이번 편에서는 캐시가 왜 필요한지, 배포했는데 왜 안 바뀌는지, 그걸 푸는 캐시 버스터가 뭔지, 파일마다 캐시를 어떻게 다르게 둬야 하는지, 그리고 흔히 저지르는 실수까지 짚어 볼게요. 캐시는 알면 고마운 친구, 모르면 골탕 먹이는 훼방꾼이라, 원리를 잡아 두면 배포가 한결 편해져요.


캐시가 대체 왜 필요해요?


캐시는 한 번 받은 걸 가까이 아껴 뒀다가 다시 쓰는 장치예요. 사용자가 우리 사이트에 처음 오면 화면 문서랑 여러 파일을 내려받는데, 이걸 매번 새로 받으면 느리고 데이터도 낭비거든요. 그래서 브라우저는 받은 파일을 자기 안에 저장해 두고, 다음에 같은 걸 요청하면 내려받지 않고 저장해 둔 걸 꺼내 써요. 덕분에 두 번째 방문부턴 화면이 훨씬 빨리 떠요.


캐시는 브라우저에만 있는 게 아니에요. 앞서 말한 배포망도 파일을 세계 곳곳에 사본으로 아껴 둬 사용자랑 가까운 데서 건네주죠. 이것도 일종의 캐시예요. 이렇게 여러 층에서 파일을 아껴 두니, 우리 서버까지 매번 갈 필요가 없어져 서비스 전체가 빨라지고 서버 부담도 줄어요. 캐시는 웹을 빠르게 굴리는 숨은 공신인 셈이에요.


그런데 이 아껴 둔다는 성질이 배포 땐 문제를 일으켜요. 우리가 파일을 새로 올려도, 브라우저나 배포망이 아직 옛날 걸 아껴 두고 있으면 그걸 계속 꺼내 쓰거든요. 그럼 사용자는 새 파일이 있는 줄도 모르고 옛 화면을 계속 봐요. 빠르자고 만든 캐시가, 배포 땐 변화를 가로막는 걸림돌이 되는 거죠.


그러니까 캐시를 대할 땐 두 마음이 필요해요. 안 바뀌는 건 오래오래 아껴 두게 해서 속도를 누리고, 바뀌는 건 제때 새로 받게 해서 옛것에 붙들리지 않게요. 이 둘을 어떻게 가르느냐가 캐시를 잘 다루는 핵심이에요. 저는 이걸 아낄 건 아끼고, 갱신할 건 갱신하는 균형이라고 불러요.


이 균형을 못 맞추면 둘 중 하나로 고생해요. 다 오래 아껴 두게 하면 배포해도 안 바뀌는 문제가, 다 안 아끼게 하면 매번 새로 받아 느려지는 문제가 생기죠. 그래서 파일 성격에 따라 캐시를 다르게 두는 요령이 필요한데, 그 답이 바로 이 편의 주제인 캐시 버스터예요.


배포했는데 왜 안 바뀌어요?


안 바뀌는 문제의 뿌리는 이름은 같은데 내용은 달라진 데 있어요. 우리는 app.js라는 파일의 내용만 바꿔 다시 올리는데, 브라우저 입장에선 이름이 똑같으니 예전 거랑 같은 파일이겠거니 여기거든요. 그래서 굳이 새로 안 받고 아껴 둔 옛 내용을 꺼내 써요. 브라우저는 이름으로 같고 다름을 판단하는데, 우리는 이름을 안 바꿨으니 착각이 생기는 거죠.


이 착각은 겉으로 잘 안 드러나 더 골치예요. 개발자 본인은 강제로 새로 받아 보니 새 화면이 뜨는데, 정작 일반 사용자는 옛 화면을 보거든요. 그래서 나는 되는데 사용자는 안 된다는 답답한 상황이 벌어져요. 저는 이 함정을 여러 번 겪고 나서, 배포 확인을 내 브라우저의 새 창이 아니라 캐시가 살아 있는 평범한 상태에서 해 보는 습관을 들였어요.


더 고약한 경우는 일부만 바뀌는 거예요. 화면 문서는 새로 받았는데 그게 부르는 코드 파일은 옛 걸 쓰거나, 그 반대일 때요. 그럼 새 화면 문서랑 옛 코드가 서로 안 맞아 엉뚱하게 깨져요. 저는 이런 반쪽만 갱신된 상태가 아예 안 바뀐 것보다 더 위험하다고 봐요. 완전히 옛것이면 그나마 옛 모습이라도 멀쩡한데, 섞이면 이도 저도 아니게 무너지거든요.


배포망 층에서 아껴 둔 것도 같은 문제를 일으켜요. 브라우저는 새로 받으려 하는데 배포망이 옛 사본을 건네면, 역시 옛 내용이 나가죠. 그래서 배포 뒤 안 바뀌면, 저는 어느 층의 캐시가 옛것을 붙들고 있는지부터 갈라요. 브라우저인지, 배포망인지에 따라 손쓸 데가 다르거든요.


이 모든 문제의 뿌리가 이름은 그대로인데 내용만 바뀐 한 가지라는 걸 알면, 해법도 자연스럽게 떠올라요. 내용이 바뀌면 이름도 바뀌게 만들면 되잖아요. 이름이 달라지면 브라우저는 처음 보는 파일로 여겨 새로 받을 테니까요. 이 단순한 아이디어가 바로 캐시 버스터예요.


캐시 버스터가 뭐예요?


캐시 버스터내용이 바뀌면 파일 이름도 바뀌게 해서 캐시의 착각을 깨는 방법이에요. 파일 내용에서 고유한 표식을 뽑아 이름에 붙이는데, 내용이 조금이라도 바뀌면 이 표식이 달라지니 이름이 통째로 달라져요. 예를 들어 app.jsapp.9f3a1c.js가 되고, 내용이 바뀌면 app.7b2e5d.js가 되는 식이죠.


이렇게 하면 배포할 때마다 바뀐 파일은 새 이름으로 올라가요. 브라우저는 화면 문서에서 새 이름의 파일을 부르니, 아껴 둔 옛것 대신 새것을 받아 와요. 반대로 안 바뀐 파일은 이름도 그대로라, 아껴 둔 걸 그냥 써 속도를 누리죠. 바뀐 것만 새로 받고 안 바뀐 건 아끼는, 딱 우리가 바라던 균형이 저절로 맞춰져요.


요즘 빌드 도구는 이 표식 붙이기를 알아서 해 줘요. 우리가 설정에서 켜 두기만 하면, 빌드할 때마다 파일마다 내용에 맞는 표식을 계산해 이름에 새기고, 화면 문서 안의 부르는 곳도 새 이름으로 고쳐 줘요. 그래서 우리는 원리만 알면 되고, 손으로 이름을 바꿀 일은 거의 없어요. 저는 이게 요즘 빌드 도구의 참 고마운 점이라고 느껴요.


여기서 표식은 내용에서 뽑아낸다는 게 중요해요. 그냥 배포할 때마다 아무 숫자나 붙이면, 안 바뀐 파일까지 이름이 바뀌어 다 새로 받게 되거든요. 그럼 캐시의 이점을 잃죠. 내용에서 뽑으면 바뀐 것만 이름이 바뀌니, 안 바뀐 건 계속 아껴 둘 수 있어요. 저는 이 내용 기반 표식인지를 꼭 확인해요.


이 방식의 또 다른 장점은 여러 버전이 공존할 수 있다는 거예요. 새 파일은 새 이름이라, 옛 파일이랑 이름이 안 겹쳐요. 그래서 배포 중에 누군가 옛 화면 문서를 붙들고 있어도, 그게 부르는 옛 이름 파일이 아직 남아 있어 안 깨져요. 이름이 겹쳤다면 옛것을 덮어써 옛 화면 문서가 부를 파일을 잃었을 텐데, 그 사고를 막아 주죠.


파일마다 캐시를 어떻게 다르게 둬요?


캐시를 얼마나 오래 아껴 둘지는 파일한테 딸려 보내는 지시로 정해요. 서버가 파일을 건넬 때 이건 이만큼 아껴 둬도 돼 하는 표시를 함께 보내는 거죠. 이 표시를 길게 주면 오래 아껴 두고, 짧게 주거나 아끼지 말라고 하면 매번 새로 받아요. 그러니 파일 성격에 맞게 이 표시를 다르게 주는 게 요령이에요.


표식이 붙은 파일은 아주 오래 아껴 두게 해도 안전해요. 내용이 바뀌면 어차피 이름이 바뀌어 새로 받을 테니, 이 이름 이 내용은 영원히 안 바뀐다고 봐도 되거든요. 그래서 이런 파일엔 최대한 길게 아껴 두라는 표시를 줘요. 흔히 Cache-Control: max-age=31536000, immutable처럼 일 년쯤 아끼고 안 바뀐다고 알려 주죠. 사용자는 두 번째 방문부터 이 무거운 파일을 안 받고 곧장 화면을 봐요.


반면 화면 문서는 이야기가 달라요. 이건 어떤 표식 파일을 부를지를 담고 있어서, 오래 아껴 두면 새 표식 파일을 영영 못 부르거든요. 그래서 화면 문서엔 아끼지 말고 늘 확인하라는 표시를 줘요. 사용자가 올 때마다 최신 화면 문서를 받아, 그 안에 적힌 새 표식 파일들을 부르게요. 화면 문서는 가벼우니 매번 받아도 큰 부담이 없어요.


정리하면 지문 붙은 파일은 오래, 화면 문서는 매번이에요. 이 조합이 캐시 버스터의 핵심 전략이죠. 무거운 코드랑 자원은 오래 아껴 속도를 누리고, 그것들을 엮는 가벼운 화면 문서만 매번 새로 받아 항상 최신 조합을 가리키게 하는 거예요. 저는 이 전략을 변하지 않는 건 붙잡고, 변하는 이름표만 갱신한다고 외워 둬요.


그림이나 글꼴 같은 자원도 표식을 붙여 오래 아껴 두면 좋아요. 다만 자주 바뀌는 데이터 같은 건 짧게 아끼거나 아예 안 아끼게 둬야 하고요. 저는 파일을 올릴 때 이건 얼마나 자주 바뀌나를 기준으로 캐시 표시를 정해요. 거의 안 바뀌는 건 길게, 자주 바뀌는 건 짧게, 이 단순한 물음이 대부분의 판단을 이끌어 줘요.


인증된 응답도 캐시해도 돼요?


여기서 꼭 짚어야 할 위험이 하나 있어요. 사람마다 다른 내용은 함부로 아껴 두게 하면 안 돼요. 로그인한 사람 이름이 담긴 화면 같은 걸 누구나 볼 수 있게 아껴 두면, 다음 사람이 남의 정보가 담긴 화면을 받는 끔찍한 사고가 나거든요. 캐시는 같은 요청엔 같은 답이라고 믿는데, 사람마다 답이 달라야 하는 걸 아껴 두면 이 믿음이 깨지죠.


그래서 사람마다 다르거나 로그인이 필요한 응답공용으로 아끼지 말라고 분명히 표시해야 해요. 이런 응답엔 개인용으로만 잠깐 아끼거나 아예 안 아끼게 두는 거죠. 저는 사용자별 내용을 다루는 곳엔 공용 캐시 금지를 습관처럼 붙여요. 속도 조금 얻자고 남의 정보가 새는 위험을 무릅쓸 순 없으니까요.


이걸 놓치면 중간의 배포망이나 대리 서버가 개인 응답을 아껴 두고 다른 사람한테 건네는 일이 벌어져요. 개인정보 유출 중에 이렇게 캐시 설정 실수로 터지는 경우가 실제로 있어요. 그래서 저는 정적 자산이랑 개인 응답을 확실히 갈라, 정적 자산은 마음껏 아끼되 개인 응답은 절대 공용으로 안 아끼게 둬요.


반대로, 사람이 달라도 답이 똑같은 공개 정보는 아껴 두면 좋아요. 누구한테나 같은 공지나 목록 같은 건 잠깐 아껴 서버 부담을 덜 수 있죠. 핵심은 이 응답이 사람마다 다른가를 갈라, 같으면 아끼고 다르면 안 아끼는 거예요. 이 구분만 또렷하면 캐시는 위험하지 않고 든든한 도우미가 돼요.


저는 그래서 캐시 설정을 정할 때 늘 이건 공개인가 개인인가부터 물어요. 정적 자산은 대개 공개라 오래 아끼고, 사용자별 응답은 개인이라 안 아끼죠. 이 한 가지 물음이 속도랑 안전을 가르는 갈림길이라, 무심코 넘기지 않으려 애써요.


캐시 다룰 때 흔한 실수는요?


첫째 실수는 화면 문서까지 오래 아껴 두는 거예요. 모든 파일에 똑같이 길게 아끼라고 줘 버리면, 화면 문서가 옛 표식 파일을 붙들어 배포해도 안 바뀌는 문제가 생겨요. 저는 캐시 설정을 할 때 화면 문서만은 예외인지를 가장 먼저 확인해요. 여기 하나 어긋나면 배포가 통째로 안 먹히거든요.


둘째는 표식을 안 붙이고 오래 아끼게 하는 거예요. 이름이 안 바뀌는데 오래 아끼게 하면, 파일을 바꿔도 영영 옛것이 남아 손쓸 방법이 없어요. 그럼 급하게 고칠 게 있어도 사용자한테 안 닿죠. 오래 아끼기랑 표식은 반드시 짝이라는 걸 기억하세요. 표식 없이 오래 아끼기만 켜면 스스로 발등을 찍는 셈이에요.


셋째는 배포망 캐시를 안 비우는 거예요. 브라우저 쪽은 표식으로 풀어도, 배포망이 옛 화면 문서를 붙들고 있으면 여전히 옛것이 나가요. 그래서 배포 뒤 배포망에 새 걸로 갈라고 알려 주는 절차가 필요할 때가 있어요. 저는 자동 배포에 배포망 캐시 비우기를 한 단계로 넣어, 배포하면 옛 사본이 자동으로 물러나게 해 둬요.


넷째는 확인을 강제 새로고침으로만 하는 거예요. 강제 새로고침은 캐시를 무시하니 늘 새것이 떠서, 정작 일반 사용자가 겪는 캐시 문제를 못 봐요. 저는 배포 확인을 평범한 방문 상태로도 해서, 사용자 눈높이에서 제대로 갱신되는지를 봐요. 개발자만 되는 배포는 안 된 배포나 마찬가지니까요.


정리하면, 캐시는 아껴 둬 빠르게 하는 고마운 장치지만, 이름이 같으면 안 바뀐 줄 아는 성질 때문에 배포 땐 훼방꾼이 돼요. 이걸 내용이 바뀌면 이름도 바뀌게 하는 캐시 버스터로 풀고, 표식 파일은 오래, 화면 문서는 매번 아끼며, 개인 응답은 절대 공용으로 안 아끼게 두면 돼요. 다음 편에서는 서버를 사용자 몰래 갈아 끼우는 무중단 배포 이야기로 넘어가 볼게요.