앱이 '잊어버려'라고 말할 때, 그것을 믿기 위해 무엇이 필요할까요?
요약
본 글은 애플리케이션의 여러 부분이 사용자 데이터를 공유 저장소에 보관할 때 발생하는 데이터 동기화 및 삭제 처리의 복잡성을 다룹니다. 특히, 한 창에서 메시지를 '삭제'해도 다른 창이나 백그라운드 프로세스가 이전 버전을 가지고 있어 의도치 않게 정보가 복원되는 현상을 지적합니다.
핵심 포인트
- 애플리케이션 간 데이터 동기화 시 삭제 처리의 모호성이 발생할 수 있습니다.
- 한 곳에서 삭제되어도 다른 창이나 백그라운드 프로세스가 이전 데이터를 보유할 위험이 있습니다.
- 데이터를 '삭제'한다는 것은 단순히 로컬에서 제거하는 것을 넘어선 복잡한 개념입니다.
저는 채팅 기록을 더 편리하게 만들려고 노력했습니다. 그러다가 스크린샷을 남기는 사람의 소프트웨어적인 동등물을 만났습니다.
“제가 방금 말한 것을 잊어버려.”
합리적인 요청입니다. 다섯 단어. 다이어그램이 필요할 거라고 아무도 예상하지 못합니다.
비서에게 예전 주소를 삭제하고, 노트북을 닫았다가 나중에 돌아왔는데 그 주소가 다시 기억되어 있는 것을 상상해 보세요.
당신은 이사를 갔지만 소프트웨어가 임대 기간을 갱신한 것입니다.
이것 뒤에 지능적이거나 악의적인 무언가가 있을 필요는 없습니다. 애플리케이션의 일부가 단순히 사용자의 정보 이전 버전을 가지고 작동하고 있을 수 있습니다.
불행하게도, 그것은 저장할 권한도 가지고 있습니다.
저는 VS Code 채팅 기록 변경 제안 작업을 하는 동안 이러한 종류의 문제에 부딪혔습니다. 저는 대화 내용이 작업 공간 전반에서 사용 가능하기를 원했습니다. 즉, 편집기에서 열려 있는 서로 다른 프로젝트 폴더들 말입니다.
편리한 기능이라고 생각했습니다.
검토 코멘트들이 프레젠테이션을 준비했습니다.
저는 채팅 내용이 저를 따라다니기를 바랐습니다
저의 VS Code 풀 리퀘스트는 로컬 채팅 기록을 사용자 프로필 전반에 공유되는 저장소로 이동시키려고 시도했습니다.
아이디어는 간단했습니다. 다른 프로젝트를 열어도 여전히 대화 내용을 찾을 수 있게 하는 것입니다.
하지만 여러 창이 동일한 기록에 접근할 수 있게 되면, 그것에 대해 무엇이 되어야 할지에 대한 경쟁적인 아이디어를 가질 수도 있습니다.
Copilot의 검토 코멘트 중 하나는 가능한 시퀀스를 식별했습니다. 대화가 두 개의 창에서 열려 있고, 한 창이 이를 삭제합니다. 다른 창은 여전히 이전 버전을 가지고 있습니다. 그 두 번째 창이 저장하거나 종료될 때, 삭제된 항목을 다시 생성할 수 있습니다.
따라서 대화는 아무도 요청하지 않은 창의 영웅적인 노력 덕분에 삭제를 생존할 수 있었습니다.
나중에 올라온 코멘트에서는 또 다른 버전을 제기했습니다. 이미 저장되기를 기다리고 있는 업데이트가 대화를 삭제된 것으로 표시하는 기록을 덮어쓸 수 있다는 것입니다.
이것들은 제가 제안한 코드에 대한 발견이었으며, 배포된 VS Code 결함에 대한 주장은 아니었습니다. 저는 결국 리뷰 과정의 어려움 속에서 PR을 닫았습니다.
저는 모든 리뷰가 이 솔루션을 명확하게 만들었다고 말하고 싶습니다. 제가 어려움을 겪고 있다고 인정하는 PR 설명은 좀 더 영화적이지 않은 설명을 제공합니다.
하지만 문제는 흥미로웠습니다:
애플리케이션의 다른 부분이 여전히 저장할 가치가 있는 무언가를 가지고 있다고 생각할 때, “삭제됨(deleted)”이란 무엇을 의미할까요?
두 번째 창에는 스크린샷이 있습니다
자신의 채팅 기록에서 메시지를 삭제하는 것을 생각해 보세요. 친구는 여전히 사본을 가지고 있을 수 있습니다.
당신은 휴대폰에서 그것을 제거했습니다. 하지만 당신의 친구는 그것을 ‘결혼식 때 꺼낼 것들(Things To Bring Up At Your Wedding)’ 폴더에 보관해 두었습니다.
두 애플리케이션 창이 비슷하게 작동할 수 있지만, 두 번째 창은 공유 저장소에 사본을 다시 넣도록 허용될 수도 있습니다.
여기에 의도적으로 작은 JavaScript 예제가 있습니다:
let stored = { address: "14 Example Road" };
// 다른 창이 자체 사본을 로드합니다.
...
AI가 아닙니다. 정교한 인프라가 아닙니다. 단지 삭제 후에 도착하는 오래된 사본일 뿐입니다.
삭제 작업은 성공적으로 실행되었습니다. 그러다가 나중에 저장하는 과정에서 제거했던 것을 다시 생성했습니다.
만약 삭제 직후에만 확인했다면, 모든 것이 괜찮아 보였을 것입니다. 문제는 두 번째 창이 차례를 받을 때 발생합니다.
오프라인 장치가 나중에 재연결되는 것도 비슷한 설계 문제를 만듭니다. 서버가 더 이상 가지고 있지 않은 정보가 있습니다. 이 정보를 복원해야 할까요?
어쩌면 서버는 그것을 받은 적이 없을 수도 있습니다. 어쩌면 사용자가 의도적으로 삭제했을 수도 있습니다.
무슨 일이 일어났는지에 대한 기록이 없으면, 장치는 두 가지를 안정적으로 구별할 수 없습니다. 마치 당신의 책상을 정리하는 사람의 확신으로 그저 그 사람이 이해하는 시스템으로 만들려고 노력하고 있는 것과 같습니다.
무언가를 잊기 위해서는 그것의 삭제를 기억해야 할 수도 있습니다
확립된 접근 방식 중 하나는 **무덤석(tombstone)**입니다. 이는 항목이 삭제되었다고 말하는 작은 기록입니다.
네, 데이터베이스에는 무덤석이 있습니다. 우리는 그 데이터가 계속 돌아오는 것을 막기 위해 그것에게 장례식을 치러주었습니다.
주소를 유지하는 대신, 단순화된 시스템은 다음을 유지할 수 있습니다:
{
id: "memory-42",
revision: 2,
...}
id는 항목을 식별합니다. revision은 그 버전을 식별합니다. 삭제 플래그(deletion flag)는 이 마커에 주소(address)를 유지하지 않으면서 무엇이 일어났는지 기록합니다.
버전 1로 수정 사항을 저장하려는 오래된 창은 이제 거부될 수 있습니다. 그 정보는 삭제보다 앞선 시점의 것입니다.
이것은 기존 데이터베이스 기술입니다. 예를 들어, CouchDB는 삭제 마커를 문서화하여 삭제가 여러 데이터베이스 간에 전파될 수 있도록 합니다.
하지만 마커가 유효하려면 관련 쓰기 경로(write paths)들이 이를 존중해야 합니다.
제 PR에 대한 한 코멘트에서는 보류 중인 업데이트가 삭제 마커를 덮어쓰는 경우를 설명했습니다. 즉, 시스템이 “이것은 삭제되었다”고 기록했더라도 오래된 작업이 그 결정을 대체하도록 허용할 수 있었다는 의미입니다.
그 무덤돌(tombstone)은 거기에 있었습니다. 누군가 그것 위에 주차한 것입니다.
견고한 구현을 위해서는 쓰기 작업을 커밋할 때 여전히 허용되는지 확인해야 합니다. 이 확인과 쓰기는 하나의 보호된 작업으로 이루어져야 합니다. 그렇지 않으면 확인하는 시점과 저장하는 시점 사이에 무언가가 변경될 수 있습니다.
또한 재창조(recreation)를 위한 의도적인 정책이 필요합니다. 주소를 다시 입력하기로 선택한 사용자와 아무런 요청 없이 이를 복원하는 오래된 창은 다릅니다.
“다시 기억해 줘”와 “소파 뒤에서 이걸 찾았어”는 동일한 권한을 받아서는 안 됩니다.
비서가 문장을 삭제한 후에도 사실을 유지할 수 있도록 하기
이제 메모리나 요약을 생성하는 비서를 추가해 보세요.
만약 다음과 같이 말한다고 가정해 봅시다:
“저는 예시로 거리 14번지에 살아요. 배달을 위해 기억해 주세요.”
애플리케이션 설계에 따라, 이는 여러 기록을 생성할 수 있습니다:
| 기록 | 포함될 수 있는 내용 |
|---|---|
| 대화(Conversation) | 사용자의 원래 문장 |
| ... | |
| 이것은 특정 비서에 대한 주장이 아니라 가상의 아키텍처입니다. |
중요한 세부 사항은 그 주소가 이제 여러 곳에 살 수 있다는 것입니다. 그것은 우리 대부분보다 더 다각화된 재산 포트폴리오를 달성했습니다.
저장된 메모리 기록을 삭제한다고 해서, 다른 장소의 주소가 자동으로 제거되지는 않습니다.
만약 어시스턴트가 나중에 대화를 읽고 주소를 다시 추출한다면, 당신이 제거한 기억을 재구성할 수 있습니다. 유용한 정보를 기억하도록 설계된 기능이 구식 지침에 극도로 집착하게 된 것입니다.
사용자가 “잊어버려”라고 말합니다. 메모리 시스템은 “내가 뭘 찾았는지 봐”라고 합니다.
여기서 **출처(provenance)**가 도움이 됩니다: 기억이나 요약이 어디에서 왔는지를 기록하는 것입니다.
저장된 사실이 특정 메시지에서 파생되었다면, 그 관계는 애플리케이션에게 어떤 다른 기록을 제거하거나 재구축해야 할지 결정할 시작점을 제공합니다.
이는 완전한 해결책은 아닙니다. 요약은 여러 출처를 결합할 수 있습니다. 동일한 사실이 다른 대화에서 독립적으로 나타날 수도 있습니다.
사용자의 의도 또한 중요합니다. 저장된 주소를 삭제하는 것은 “더 이상 배송에 사용하지 마세요”라는 의미일 수 있습니다. 또는 “저장된 모든 발생 기록을 제거하세요”라는 의미일 수도 있습니다. 이러한 요청들은 서로 다른 범위를 가지며, 인터페이스는 어떤 것을 이행할 수 있는지 설명해야 합니다.
작은 휴지통 아이콘 하나가 여기서 많은 계약 협상을 수행하고 있는 셈입니다.
당신의 어시스턴트는 이미 절반쯤 말했을 수도 있습니다
또 다른 어색한 순서가 있습니다:
- 어시스턴트가 당신의 주소를 검색합니다.
- 당신이 그 주소를 삭제합니다.
- 어시스턴트가 이미 시작했던 답변을 마무리합니다.
저장소는 3단계에서 정확할 수 있습니다. 하지만 답변에는 여전히 주소가 포함되어 있을 수 있습니다.
이는 누군가 마이크를 켜고 난 후에 “그거 말하지 마”라고 하는 소프트웨어 버전과 같습니다.
미래의 저장 것을 제어하는 것이 이 문제를 해결하지 못합니다. 애플리케이션은 또한 영향을 받는 작업을 취소하거나, 정보가 여전히 허용되는지 확인한 후에 결과를 표시해야 할 수도 있습니다.
우리가 가정한 어시스턴트라면, 저는 ‘잊는 것’이 다음 상황에서도 유지되는지 테스트해 볼 것입니다:
- 상태를 저장하는 이전 창(window).
- 재연결하는 오프라인 클라이언트(client).
- 완료되는 대기 중인 메모리 업데이트(memory update).
- 다시 생성되는 요약(summary).
- 진행 중인 답변(answer).
각 테스트에서 유용한 질문은 구체적입니다. 이 작업이 삭제 후에도 정보를 복원하거나 노출할 수 있는지를 존중해야 합니다.
이러한 검사를 통과하는 것은 특정한 동작을 확립합니다. 하지만 모든 백업, 로그 또는 외부 서비스가 모든 사본을 지웠다고 증명하지는 못합니다. 그러한 것들은 별도의 처리가 필요하며 무엇이 남아 있는지에 대한 정확한 설명이 요구됩니다.
녹색 테스트 스위트(green test suite)는 안심이 됩니다. 하지만 여전히 당신이 테스트하지 않은 시스템에 대해서는 보증할 수 없습니다.
“잊어버림”은 “이 목록에서 제거됨”보다 더 큰 약속입니다
이 시점에서는, ‘삭제’라는 라벨이 이렇게 작은 버튼에 붙기에는 놀라울 정도로 야심차게 느껴집니다.
사용자로서 저는 데이터베이스의 안내 투어를 원하지 않습니다. 제가 휴지통 아이콘을 클릭했을 뿐입니다. 오후 일과를 계속하고 싶었을 뿐이죠.
하지만 다음과 같은 메시지를 보는 편이 더 좋습니다:
“저장된 메모에서 제거되었습니다. 이 대화에는 여전히 표시됩니다.”
다음 주 화요일에 제 예전 주소가 게스트처럼 등장하는 상황과 함께 나오는 자신감 넘치는 “잊어버림!”이라는 문구보다는요.
그 메시지는 저에게 유용한 무언가를 제공합니다: 무엇이 바뀌었고 무엇이 남아 있는지에 대한 명확한 설명입니다. 저는 대화를 제거할지 여부를 결정할 수 있습니다.
그 밑단의 엔지니어링 역시 그 선택을 존중해야 합니다. 이전 창, 예약된 업데이트 및 재연결되는 장치들은 원래의 공지 사항을 놓쳤더라도 삭제를 존중해야 합니다.
제가 계속해서 돌아오는 부분은 바로 이것입니다: 잊어버림에는 미래 시제(future tense)가 있습니다. 지금 정보를 제거하는 것은 작업의 일부일 뿐입니다. 시스템은 또한 스스로의 미완성된 작업이 나중에 그것을 복원하는 것을 막아야 합니다.
그렇지 않다면, “제가 그걸 잊었어요”라는 말은 단지 앱이 다른 창에서 점심 식사 후 돌아오기 전에 한 말에 불과할 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기