사전 만료
개념

만료

gabury1고친 사람 github-actions[bot]

만료는 정해 둔 시각이 지난 값을 더 이상 쓰지 못하게 막는 일입니다. 값을 만들 때 언제까지 유효한지를 같이 적어 둡니다. 쓸 때마다 적어 둔 시각과 지금 시각을 견줍니다. 수명이 다한 값을 어떻게 할지는 분야마다 다릅니다.

쉽고 빠른 이해

만료는 「이 값은 언제까지만 쓴다」를 미리 정해 두는 일입니다. 로그인하고 받은 토큰에 30분을 붙여 두면 30분 뒤부터 그 토큰으로는 아무것도 못 합니다.

수명이 없으면 한 번 만든 값이 언제까지고 통합니다. 낡은 값이 계속 읽히고, 새어 나간 토큰이 영영 열리는 열쇠가 됩니다. 걷어내려면 사람이 일일이 찾아 지워야 합니다.

어떻게 도는가:

  1. 값을 만들 때 언제까지 쓸 수 있는지를 같이 적어 둡니다
  2. 그 값을 쓸 때마다 적어 둔 시각과 지금 시각을 견줍니다
  3. 지난 값은 거절하거나, 원본에 다시 묻거나, 지웁니다

대가는 시계와 몰림입니다. 두 기계의 시계가 어긋나면 한쪽은 유효하다고 보고 다른 쪽은 아니라고 봅니다. 많은 값에 같은 수명을 붙이면 그것들이 한꺼번에 만료되어 그 순간 원본으로 요청이 몰립니다.

원본이 바뀔 때 알려 줄 수 있는 사이면 무효화가 더 정확합니다. 만료는 그 신호를 주고받을 수 없을 때 마지막으로 값을 끊는 장치입니다.

상세

도서관에서 빌린 책은 반납일이 지나도 책상에 그대로 있습니다. 카운터에 들고 가 날짜를 보여야 그다음이 정해집니다. 기한을 늘려 받으면 도로 빌린 책이 됩니다.

만료는 값에 수명을 붙여 두고 그 수명이 다한 값을 쓰지 못하게 하는 일입니다. 저장해 둔 것이 언제까지 믿을 만한지를 사람의 판단이 아니라 시계 하나로 정합니다. 그래서 만료는 사건이 아니라 시각입니다. 아무 일도 일어나지 않아도 시각이 지나면 그 값은 만료된 것입니다.

수명을 안 붙이면 값은 언제까지고 유효합니다. 캐시에 담은 사본은 원본이 바뀐 줄 모르고 계속 읽힙니다. 한 번 새어 나간 토큰은 누가 눈치챌 때까지 열리는 열쇠로 남습니다. 지워야 하는 기록도 사람이 찾아 지우기 전에는 계속 쌓입니다.

만료는 그 「누군가 찾아서 치우는 일」을 시각 하나로 미리 정해 두는 것입니다.

이 절은 먼저 수명을 적는 두 표기를 봅니다. 이어서 수명이 다한 값에 벌어지는 세 갈래와 그 값이 저장소에서 없어지는 때를 짚습니다. 끝으로 축출·무효화와 갈리는 대목, 만료가 부르는 문제를 봅니다.

수명을 적는 두 표기

수명을 적는 방법은 둘입니다. 언제까지인지를 시각으로 못 박거나, 지금부터 얼마 동안인지를 길이로 적습니다.

무엇을 적나 예
만료 시각 유효가 끝나는 시점 3월 1일 09시까지
수명 길이 이 값을 받은 때부터의 기간 받은 때부터 600초

수명 길이 쪽을 TTL(Time To Live, 살아 있는 시간)이라고 부릅니다. 저장소에 값을 넣을 때 수명을 함께 주면, 저장소가 그 값이 들어온 시각에 수명을 더해 만료 시각을 만들어 둡니다. 겉으로는 길이를 줬지만 안에서는 시각으로 바뀝니다.

둘은 시계를 대하는 태도가 다릅니다. 만료 시각은 누가 읽어도 판정이 같습니다. 대신 읽는 쪽 시계가 틀리면 판정도 같이 틀립니다.

수명 길이는 받은 쪽이 자기 시계로 셉니다. 그래서 남의 시계와 안 맞아도 됩니다. 대신 값이 오는 동안 걸린 시간만큼 실제 수명이 길어집니다. 600초짜리 값이 오는 데 2초가 걸렸다면, 준 쪽 기준으로 598초만 남은 값을 받은 쪽은 600초로 셉니다.

sequenceDiagram
    participant 주는 쪽
    participant 받는 쪽
    주는 쪽->>받는 쪽: 수명 600초를 실어 보낸다
    Note over 주는 쪽,받는 쪽: 오는 데 2초가 걸린다
    받는 쪽->>받는 쪽: 받은 시각부터 600초를 센다
    Note over 주는 쪽,받는 쪽: 시각으로 줬다면 두 시계가 어긋난 만큼 판정이 갈린다

수명이 다한 값에 벌어지는 일

수명이 다했다고 값이 저절로 사라지지는 않습니다. 만료는 「이제 이 값을 어떻게 대할 것인가」를 바꿀 뿐입니다. 무엇을 할지는 그 값이 무엇이냐에 따라 셋으로 갈립니다.

flowchart TD
    A["이 값을 쓰려 한다"] --> B{"만료 시각이 지났나"}
    B -->|아니오| C["그대로 쓴다"]
    B -->|예| D["만료된 값으로 다룬다"]

만료된 값으로 다룬다는 것이 무엇을 하는 것인지는 시각이 정하지 않습니다. 그 값의 성격이 정합니다.

값의 성격 만료되면 왜
자격 증명 거절한다 수명 자체가 안전장치다
캐시 사본 원본에 다시 묻는다 수명이 다해도 내용까지 틀린 것은 아니다
보존 기간이 찬 기록 지운다 안 쓰는 것으로 끝나지 않고 실제로 없애야 한다

거절은 자격을 증명하는 값에 걸립니다. 만료된 토큰이나 인증서를 들고 오면 받는 쪽이 거절합니다. 들고 온 쪽은 새 것을 받아 다시 옵니다. 이 값들은 수명이 곧 안전장치라 한 번 지나면 봐주는 것이 없습니다.

원본에 다시 묻는 것은 캐시에 걸립니다. 캐시가 들고 있는 사본은 수명이 다해도 내용까지 틀린 것은 아닙니다. 그래서 바로 버리지 않고 원본에 「그 뒤로 바뀐 것이 있나」를 묻습니다. 안 바뀌었으면 그 사본을 계속 씁니다. 수명만 새로 받습니다. 이렇게 물어 확인하는 것을 검증이라고 합니다.

삭제는 오래 쌓이는 기록에 걸립니다. 정해 둔 보존 기간이 찬 로그나 개인 정보는 더 쓰지 않는 것으로 끝나지 않고 실제로 지워야 합니다. 여기서는 만료가 「쓰지 마라」가 아니라 「없애라」는 뜻입니다.

만료된 때와 없어지는 때는 다르다

값은 만료되는 순간에 사라지지 않습니다. 만료된 값이 저장소에 그대로 남아 공간을 차지하는 구간이 있습니다. 저장소를 훑는 쪽이 나중에 그것을 치웁니다.

만료된 값이 곧장 없어지기만 하는 것도 아닙니다. 물어보니 안 바뀌었으면 도로 유효해지고, 원본이 응답하지 않으면 잠깐 더 봐주기도 합니다. 값의 일생은 한 방향으로만 흐르지 않습니다.

stateDiagram-v2
    state "봐주는 동안" as 유예
    [*] --> 유효: 수명을 붙여 만든다
    유효 --> 만료됨: 시각이 지난다
    만료됨 --> 유효: 물어보니 안 바뀌었다 · 수명을 새로 받는다
    만료됨 --> 없어짐: 읽으러 왔다가 걸러진다
    만료됨 --> 없어짐: 저장소를 훑는 쪽이 지운다
    만료됨 --> 유예: 원본이 응답하지 않는다
    유예 --> 없어짐: 봐주기로 한 기간도 지난다
    없어짐 --> [*]

치우는 방법은 둘입니다.

언제 치우나 대가
읽을 때 거른다 그 값을 읽으러 온 요청이 만료를 확인하고 지운다 아무도 안 읽는 값은 영영 안 지워진다
훑어서 지운다 훑는 쪽이 저장소를 돌며 지난 것을 골라 지운다 훑는 비용이 데이터 양을 따라 늘고 그동안 다른 요청이 늦어진다

읽을 때 거르는 방법만 쓰면 공간이 안 줄어듭니다. 아무도 찾지 않는 값이 만료된 채로 계속 쌓이기 때문입니다. 그래서 대개 둘을 같이 씁니다. 읽을 때 걸러 판정을 맞춥니다. 훑는 쪽은 남은 것을 뒤늦게 거둡니다.

축출·무효화와 갈리는 대목

값이 없어지는 방법은 만료 말고도 있습니다. 결과가 같아 보여서 이름이 자주 섞이는데, 가르는 것은 결과가 아니라 무엇이 방아쇠를 당겼느냐입니다.

방아쇠 언제 일어나나
만료 미리 정해 둔 시각 시계가 그 시각을 지날 때
축출 저장 공간이 모자람 새 값을 넣을 공간이 없을 때
무효화 원본이 바뀐 사건 바꾼 쪽이 알릴 때

만료는 처음부터 예고된 끝입니다. 값을 만들 때 이미 언제 끝날지를 적어 두었고, 그 시각이 되면 아무 일이 없어도 끝납니다. 축출은 예고가 없습니다. 수명이 한참 남은 값도 공간이 모자라면 밀려납니다.

무효화는 밖에서 온 사건이 방아쇠입니다. 원본이 바뀌었으니 들고 있는 사본을 버리라는 신호가 와야 일어납니다. 그 신호가 안 오면 사본은 수명이 다할 때까지 낡은 채로 살아 있습니다.

그래서 원본이 바뀔 때 신호를 보낼 수 있는 사이라면 무효화 쪽이 더 정확합니다. 만료는 그 신호를 주고받을 수 없거나 신호가 늦게 닿을 때 마지막으로 값을 끊어 주는 장치입니다. 둘을 같이 걸어 두는 곳이 많은 까닭입니다.

만료가 부르는 문제

수명을 붙이는 순간 새 문제가 셋 생깁니다. 시계, 몰림, 그리고 수명 길이를 얼마로 잡을지입니다.

첫째는 시계입니다. 만료 판정은 벽시계(사람이 보는 그 시각을 그대로 읽는 시계)를 읽어서 합니다. 그런데 기계마다 시계가 조금씩 어긋납니다. 발급한 쪽 시계로는 아직 남은 값이 받는 쪽 시계로는 이미 지난 값이 됩니다.

그래서 판정에 몇 초쯤 여유를 두거나, 시각 대신 수명 길이로 주고받습니다.

둘째는 몰림입니다. 같은 때에 만든 값에 같은 수명을 붙이면 그것들이 같은 순간에 한꺼번에 만료됩니다. 그 직후 들어오는 요청이 전부 원본으로 밀려가는데, 이것이 캐시 스탬피드입니다. 수명에 임의의 흔들림을 조금 섞어 만료 시점을 흩어 놓는 것이 흔한 대응입니다.

flowchart TD
    subgraph S1["같은 수명을 붙였을 때"]
        A1["값 1"] --> T1["같은 만료 시각"]
        A2["값 2"] --> T1
        A3["값 3"] --> T1
        T1 --> O1["원본에 요청 셋이 한꺼번에"]
    end
    subgraph S2["흔들림을 조금 섞었을 때"]
        B1["값 1"] --> T2["만료 시각 · 조금 이르게"]
        B2["값 2"] --> T3["만료 시각 · 그대로"]
        B3["값 3"] --> T4["만료 시각 · 조금 늦게"]
        T2 --> O2["원본에 요청이 하나씩"]
        T3 --> O2
        T4 --> O2
    end

셋째는 수명 길이를 정하는 일입니다. 짧게 잡으면 원본에 다시 묻는 일이 잦아집니다. 길게 잡으면 낡은 데이터가 오래 읽힙니다. 새어 나간 토큰도 그만큼 오래 살아 있습니다. 두 대가를 견줘서 값마다 따로 정할 수밖에 없습니다.

만료된 값을 잠깐 더 쓰게 하는 선택도 있습니다. 원본이 응답하지 않을 때 만료된 사본이라도 내주면 화면이 비지는 않습니다. 대신 읽는 쪽은 언제 적 값을 보는지 모릅니다. 그래서 얼마나 오래 봐줄지를 함께 정해 둡니다.

관련 항목

수명을 적어 두는 표기와 헤더

TTL · 만료 시각 · Cache-Control · max-age · Expires · s-maxage · 나이

수명이 다한 값을 대하는 처리

검증 · 무효화 · 축출 · 낡은 데이터 · 유예 기간 · 소프트 삭제

만료가 걸리는 캐시 계층

캐싱 · HTTP 캐시 · 신선도 · 휴리스틱 신선도 · 캐시 스탬피드 · 썬더링 허드

수명이 붙는 자격 증명

토큰 · 액세스 토큰 · 리프레시 토큰 · JWT · 세션 · 쿠키 · 인증서 · X.509 · 폐기 목록

기간이 차면 데이터를 지우는 운영 절차

보존 기간 · 로그 로테이션 · 아카이빙 · 가비지 컬렉션 · 데이터 파기

만료 판정이 딛는 시계와 시간 장치

벽시계 · NTP · 단조 시계 · 타임아웃 · 하트비트 · 리스

다른 이름: expiration · expiry