![[실전 서버리스 14] 삭제 고아 잡기, retry와 큐로 스토리지 청소](https://img.thenullpage.com/posts/5616/5616_1_86fe8c.webp)
글에 이미지를 올렸다가 글을 지우면, 그 이미지는 어떻게 될까. 글은 데이터베이스에서 지워졌는데 이미지 파일은 스토리지에 그대로 남을 수 있다. 이렇게 주인을 잃고 떠도는 파일을 고아 파일이라고 한다. 방치하면 스토리지에 쓰레기가 쌓인다. 이번 편은 이 고아 파일이 왜 생기고 어떻게 정리하는지를 다룬다.
이건 앞서 SEO 시리즈의 죽은 주소와 비슷한, 스토리지 판의 청소 문제다. 데이터와 파일이 분리돼 있으니 둘의 정합성을 맞추는 게 숙제가 된다.
1. 고아 파일이 생기는 이유
앞서 말했듯 데이터는 데이터베이스에, 파일은 스토리지에 나뉘어 있다. 글을 지우면 데이터베이스의 글 데이터는 지워지는데, 그 글에 딸린 파일까지 자동으로 지워지지는 않는다. 둘은 별개의 저장소라, 한쪽을 지워도 다른 쪽은 별도로 지워줘야 한다.
그런데 이 파일 삭제가 실패하거나 누락되면 고아가 생긴다. 글은 지웠는데 파일 삭제 코드가 실행되지 않았거나, 실행됐지만 스토리지 삭제가 실패했거나 하는 경우다. 그러면 데이터베이스에는 없는 글의 파일이 스토리지에만 덩그러니 남는다. 주인 없는 파일이 되는 것이다.
이게 쌓이면 문제가 된다. 아무도 참조하지 않는 파일들이 스토리지를 차지하며 저장 비용을 낸다. 사용되지도 않는 데 돈이 나가는 것이다. 그리고 앞서 SEO 시리즈에서 다룬 것처럼, 이런 유령 데이터가 검색엔진에 잡히거나 사이트맵에 남아 혼란을 줄 수도 있다. 그래서 고아 파일은 정리해야 한다.
고아 파일이 앞 편들의 죽은 주소와 닮았다는 게 흥미롭다. 데이터베이스의 글과 스토리지의 파일이 별개라, 한쪽만 지워지면 다른 쪽이 유령으로 남는다. SEO에서 죽은 URL을 청소했듯, 스토리지에서도 이 유령을 청소해야 한다. 분리된 저장소를 쓰는 대가로 정합성 관리라는 숙제가 따라오는 것이다.
한 가지 더, 고아 파일은 조용히 쌓인다는 게 위험하다. 당장은 사이트 동작에 영향이 없어서 눈에 안 띄지만, 시간이 지나며 스토리지에 쓰레기가 누적되고 저장 비용이 새어나간다. 그래서 지금 문제가 없어 보여도 정합성을 지키는 장치를 미리 갖춰두는 게, 나중의 큰 청소를 막는 길이다.
2. 삭제를 확실하게, retry
첫 번째 대책은 삭제를 확실하게 하는 것이다. 파일 삭제가 실패할 수 있으니, 한 번 시도해서 실패하면 다시 시도한다. 이게 retry다. 스토리지 삭제 같은 외부 작업은 일시적인 이유로 실패할 수 있는데, 재시도로 대부분 성공시킬 수 있다.
다만 재시도에도 요령이 있다. 무작정 즉시 다시 시도하면 같은 이유로 또 실패할 수 있다. 그래서 잠깐 기다렸다 재시도하고, 그래도 실패하면 좀 더 기다렸다 재시도하는 식으로 간격을 둔다. 일시적 문제라면 시간이 지나며 해결되니, 간격을 두고 몇 번 재시도하면 대체로 성공한다.
그래도 계속 실패하면 어떻게 할까. 무한히 재시도할 수는 없다. 몇 번 시도해도 안 되면, 이 삭제 작업을 나중에 다시 처리할 목록에 넣어둔다. 지금 당장 안 되니 뒤로 미뤄두고, 나중에 다시 시도하는 것이다. 여기서 다음 대책인 큐가 등장한다.
재시도에 간격을 두는 게 중요한 실무 포인트다. 실패하자마자 즉시 다시 하면 같은 이유로 또 실패하기 쉽다. 잠깐 기다렸다, 그래도 안 되면 좀 더 기다렸다 시도하면 일시적 문제가 그사이 풀린다. 간격을 점점 늘리며 몇 번 시도하는 이 방식이 일시적 실패를 대부분 성공으로 돌린다.
3. 실패한 작업을 큐로
재시도해도 안 되는 삭제 작업은 큐에 넣는다. 큐는 나중에 처리할 작업을 담아두는 대기열이다. 삭제에 실패한 파일을 이 큐에 넣어두면, 나중에 큐를 처리하는 작업이 그걸 꺼내 다시 삭제를 시도한다. 지금 못 한 걸 뒤로 미뤄 확실히 처리하는 것이다.
큐를 쓰는 이유는 실패한 작업을 잃어버리지 않기 위해서다. 재시도하다 포기하고 그냥 넘어가면 그 파일은 영영 고아로 남는다. 하지만 큐에 넣어두면 반드시 나중에 다시 처리된다. 지금의 일시적 실패가 영구적 누락으로 이어지지 않게, 큐가 안전망 역할을 한다.
이 큐는 뒤에서 다룰 비동기 처리의 한 예다. 당장 처리하기 어렵거나 무거운 작업을 큐에 담아 뒤에서 처리하는 방식이다. 삭제 실패 정리 외에도 여러 무거운 작업에 이 패턴을 쓴다. 큐 자체는 별도 편에서 더 다루겠지만, 여기서는 고아 파일 정리의 안전망으로 쓰인다.
큐를 안전망으로 쓰는 발상이 고아 파일 정리의 핵심이다. 재시도해도 안 되는 걸 그냥 포기하면 영구 고아가 되지만, 큐에 넣어두면 반드시 나중에 다시 처리된다. 지금의 일시적 실패가 영구적 누락으로 이어지지 않게 큐가 받쳐주는 것이다. 잃어버리면 안 되는 작업을 큐에 맡기는 이 패턴이 두루 쓰인다.
또 하나, 실패한 작업을 큐로 넘길 때는 그 작업 정보를 충분히 담아야 한다. 어떤 파일을 지워야 하는지, 몇 번 시도했는지 같은 정보가 있어야 나중에 제대로 재처리된다. 정보가 부족하면 큐에서 꺼내도 뭘 해야 할지 모른다. 큐에 넣는 작업은 그 자체로 완결된 정보를 갖도록 설계하는 게 좋다.
4. 주기적인 전수 점검
재시도와 큐로도 놓치는 고아가 있을 수 있다. 그래서 주기적으로 전체를 점검하는 게 필요하다. 스토리지의 파일 목록과 데이터베이스가 참조하는 파일 목록을 대조해서, 스토리지엔 있는데 아무 데서도 참조하지 않는 파일을 찾아낸다. 이게 고아 파일이다.
이 전수 점검은 자주 할 필요는 없다. 재시도와 큐가 대부분을 잡아주니, 전수 점검은 그물을 빠져나간 소수를 잡는 최후의 그물이다. 나는 이걸 한 달에 한 번 정도 수동으로 한다. 자동으로 돌릴 수도 있지만, 파일을 삭제하는 민감한 작업이라 사람이 확인하며 하는 게 안전하다고 봤다.
점검할 때는 신중해야 한다. 참조되지 않는다고 무작정 지우면 안 된다. 최근에 올라와 아직 글에 연결되기 전인 파일일 수도 있기 때문이다. 그래서 일정 시간이 지났는데도 참조되지 않는 파일만 고아로 판단해 지운다. 성급한 삭제가 살아 있는 파일을 없애는 사고를 막기 위해서다.
주기적 전수 점검이 최후의 그물이라는 게 맞는 표현이다. 재시도와 큐가 대부분을 잡아도 그물을 빠져나간 소수가 있을 수 있으니, 가끔 전체를 대조해 남은 고아를 잡는다. 다만 파일 삭제는 민감하니 신중하게, 일정 시간 지나 확실한 것만 지운다. 성급한 삭제가 살아 있는 파일을 없애는 사고를 막아야 한다.
5. 정합성을 지키는 설계
고아 파일 문제의 근본은 데이터와 파일이 두 저장소에 나뉘어 있어 정합성이 깨질 수 있다는 것이다. 그래서 이상적인 건 애초에 정합성이 잘 유지되게 설계하는 것이다. 글을 지우는 흐름 안에 파일 삭제를 확실히 묶어두고, 실패에 대비한 재시도와 큐를 갖추는 것이다.
이건 앞서 SEO 시리즈에서 글 삭제와 사이트맵 제거를 한 흐름으로 묶으라던 것과 통한다. 관련된 정리 작업을 흩어두지 말고 한 흐름으로 엮어, 하나가 되면 다른 것도 함께 처리되게 하는 것이다. 그러면 사람이 매번 기억하지 않아도 정합성이 유지된다. 자동화가 정합성을 지킨다.
정리하면 고아 파일은 데이터와 파일이 나뉘어 정합성이 깨질 때 생기고, 재시도로 삭제를 확실히 하고, 실패한 건 큐로 안전망을 두고, 주기적 전수 점검으로 놓친 걸 잡는다. 여기까지가 R2 파트다. 다음 편부터는 그 외의 저장소와 비동기 도구들, 즉 KV, 두러블 오브젝트, 큐, 워크플로를 언제 무엇에 쓰는지 정리한다.
정합성을 지키는 설계가 근본 해법이라는 게 이 편의 결론이다. 청소도 필요하지만, 애초에 글 삭제 흐름에 파일 삭제를 확실히 묶어두면 고아가 덜 생긴다. SEO에서 글 삭제와 사이트맵 제거를 한 흐름으로 묶으라던 것과 같다. 관련 정리를 흩어두지 말고 엮어서, 하나가 되면 다른 것도 함께 처리되게 하는 것이다.
덧붙이면, 고아 파일 관리는 결국 데이터와 파일이라는 두 세계를 일치시키는 일이다. 이 둘이 어긋나지 않게 삭제를 확실히 하고, 실패에 대비하고, 가끔 대조해 바로잡는다. 이 관리가 몸에 배면 스토리지가 깨끗하게 유지되고, 쓰지도 않는 파일에 비용을 내는 낭비가 사라진다. 보이지 않는 곳을 챙기는 게 좋은 운영이다.
다음 편부터는 데이터베이스와 스토리지 외의 도구들, 즉 KV와 두러블 오브젝트, 큐, 워크플로를 언제 무엇에 쓰는지 정리한다. 서버리스 생태계의 여러 도구를 상황에 맞게 고르는 기준을 잡는다.