Skip to content
isdnetworks
Go back

프록시 주소는 레코드가 있어야 산다

이미지 재생성 작업으로 새 미디어를 만들고 옛 미디어를 삭제한 뒤, 며칠 지나 두 상품의 이미지가 마켓 사이트에서 깨져 보인다는 신고를 받았다.

Table of contents

Open Table of contents

증상과 첫 확인

먼저 저장소를 확인했는데 새 이미지 파일은 정상적으로 존재하고 응답도 200이었다.

curl -sI 'https://<저장소>/<새 ID>/conversions/resize.jpg'
# 200

파일이 있는데 외부에서 안 보인다는 것은 외부가 보는 주소가 우리가 확인한 주소와 다르다는 뜻이다. 브로커가 마켓으로 무엇을 보내는지 확인하니 저장소 주소가 아니라 별도 프록시 도메인에 미디어 ID를 붙인 형태였다.

private function getProductImageUrl($media)
{
    return "https://img.<도메인>/{$media->id}?name=resize.jpg";
}

프록시 주소의 의존 구조

이 프록시는 요청받은 미디어 ID로 DB에서 해당 행을 찾고, 그 행에 적힌 정보로 저장소 경로를 조립해 응답한다. 미디어 행이 없으면 경로를 만들 수 없으므로 파일이 저장소에 그대로 있어도 404를 반환한다.

옛 ID와 새 ID로 각각 요청해 보니 예상대로 갈렸다.

curl -sI 'https://img.<도메인>/<옛 ID>?name=resize.jpg'   # 404
curl -sI 'https://img.<도메인>/<새 ID>?name=resize.jpg'   # 200

마켓에는 옛 주소가 등록돼 있었고, 옛 미디어 행을 삭제한 시점부터 그 주소가 죽은 것이다. 정적 파일 주소는 파일만 있으면 살지만 프록시 주소는 행과 파일이 둘 다 있어야 산다. 의존이 하나 더 있는데 그 의존은 주소 문자열만 봐서는 드러나지 않는다.

참조 방식이 갈리는 지점

같은 상품의 본문 이미지는 깨지지 않았다는 점이 확인에 도움이 됐다. 본문 HTML은 저장소를 직접 참조하고 있어서 프록시를 거치지 않으므로, 미디어 행이 사라져도 파일만 남아 있으면 그대로 표시된다.

한 상품 안에서도 대표 이미지와 본문 이미지의 참조 방식이 다르다는 것이 이번 건에서 드러났다. 같은 파일을 가리키는 주소가 두 종류이고 각각의 생존 조건이 다르므로, 한쪽이 정상이라는 사실은 다른 쪽에 대해 아무것도 보증하지 않는다.

마켓 페이지의 실제 이미지 주소를 확인하니 마켓 자체 CDN이었다. 마켓이 우리 주소에서 이미지를 가져가 자기 쪽에 복사해 두는 구조인데, 그렇다면 우리 쪽 주소가 죽어도 이미 복사된 것은 남아야 한다. 깨진 이유는 재전송 시점에 있었다. 상품 정보를 다시 보낼 때 마켓이 이미지를 새로 가져가는데 그때 우리 주소가 404면 가져가기가 실패하고 그 결과가 깨진 이미지로 남는다.

삭제 작업의 전제 조건

이 건에서 나온 규칙은 옛 미디어를 삭제하는 작업이 아직 그 프록시 주소를 참조하고 있는 외부 등록을 깨뜨릴 수 있다는 것이다. 삭제 자체가 위험한 것이 아니라 그 주소를 참조하는 곳이 남아 있는 상태에서의 삭제가 위험하다.

삭제 전에 확인할 것이 세 단계다. 이 미디어의 프록시 주소가 어디에 등록돼 있는지 찾고, 그 등록을 새 주소로 갱신했는지 보고, 갱신 작업이 실제로 성공했는지까지 확인한다. 세 번째가 핵심이다. 갱신 배치를 돌렸다는 사실은 갱신이 반영됐다는 증거가 아니고, 이번 건에서 깨진 두 상품이 정확히 그 갱신이 안 된 것들이었다.

디버깅 순서

같은 유형의 문제를 볼 때 확인 순서를 정해 뒀다.

1. 저장소에 파일이 있나          →  있어도 안심할 수 없다
2. 프록시가 그 주소를 해석하나    ←  여기가 핵심
3. 외부에 등록된 주소가 무엇인가
4. 재전송 작업이 새 주소로 갱신됐나

1번만 확인하고 파일이 있는데 왜 안 나오느냐에서 막히기 쉽다. 프록시가 한 겹 끼어 있으면 파일의 존재와 그 파일에 도달 가능한지가 별개의 문제가 되고, 우리가 습관적으로 확인하는 것은 대개 앞쪽이다.

정리


Share this post on:

Previous Post
고치다 발견한 다른 결함
Next Post
AWS RDS MySQL 8.0 표준 지원 종료 — 마이그레이션 가이드