본문 바로가기
AWS

SES 프로덕션 전환 신청부터 하세요 — 코드로 안 되는 것들

by 꼼냥냥 2026. 9. 3.
728x90

Terraform을 쓰면 전부 코드로 될 것 같지만, 승인과 대기가 필요한 것들이 있다. 그리고 이것들이 일정을 잡아먹는다. 인프라를 다 만들어 놓고 서비스를 못 여는 상황이 여기서 나온다. 문자 발신번호와 SES 프로덕션 전환이 리드타임이 가장 길다. 인프라 코드를 쓰기 전에 신청부터 넣는 게 맞다.

먼저 신청해야 하는 것들

항목 왜 걸리나
문자 발신번호·Sender ID 계정에 종속. 새 계정이면 재취득·재등록이고 리드타임이 가장 길다
메일(SES) 프로덕션 액세스 기본이 샌드박스. 도메인·DKIM 인증 + 별도 신청 + 승인 대기
버킷 이름 전역 유일. 다른 계정에서 쓰던 이름을 그대로 못 쓴다
DNS 네임서버 zone을 만들면 NS가 바뀐다. 등록기관에서 바꿔야 전환된다
CDN용 인증서 반드시 us-east-1. DNS 검증 레코드 반영 대기
DB 코드로는 빈 인스턴스만 생긴다. 데이터 이관은 별도

인프라 코드를 쓰기 전에 신청부터 넣는 게 맞다. 우리는 이걸 저장소 README 앞쪽에 표로 박아 뒀다. 다음 사람이 같은 데서 막히지 않도록.

DNS — 같은 이름+타입은 레코드셋 "하나"다

이게 조용히 서비스를 깬다.

도메인 소유권 확인용 TXT를 apex에 넣어 두고, 나중에 SPF나 DMARC를 넣으려고 새 리소스를 만들면 어떻게 되냐면 — 기존 레코드셋을 덮어쓴다. 그리고 소유권 검증이 풀린다.

DNS에서 (이름, 타입)은 레코드셋 하나이고, 그 안에 값이 여러 개 들어간다. Terraform 리소스를 두 개 만든다고 레코드셋이 두 개가 되지 않는다. 1편의 create 함정과 같은 구조다 — 코드에 새 문장만 적었는데 실물에는 이미 값이 들어 있는 경우다.

기존 records 배열에 덧붙여야 한다.

resource "aws_route53_record" "apex_txt" {
  name = "example.com"
  type = "TXT"
  records = [
    "google-site-verification=...",   # 기존
    "v=spf1 include:... ~all",        # 추가
  ]
}

TXT 한 문자열은 255바이트까지다

DKIM 값은 400자가 넘는데, provider가 자동으로 안 쪼갠다. 내부 큰따옴표로 나눠 두 문자열로 담아야 한다.

records = ["\"앞부분…\" \"뒷부분…\""]

조회했을 때 "앞…" "뒤…"로 보이는 게 정상이다. 처음 보면 잘못 들어간 줄 알고 고치려 든다.

인증서 — 리전이 다른데 검증 레코드는 같다

CDN용 인증서는 반드시 us-east-1에 있어야 한다. 리전용 인증서를 이미 만들어 뒀다면, 검증 레코드를 또 만들어야 하나 싶은데 — 안 만들어도 된다.

(계정, 도메인)이 같으면 리전이 달라도 검증 레코드의 이름과 값이 같다.

생각해 보면 당연하다. CNAME은 값이 하나뿐이라, 값이 달랐다면 두 인증서를 동시에 검증할 수 없다. 몰라서 레코드를 덮어쓰면 먼저 있던 인증서 검증이 풀린다.

인증서 태그에는 괄호를 못 쓴다

허용 문자가 [\p{L}\p{Z}\p{N}_.:/=+\-@]라서 이게 거부된다.

Name = "example.com (CloudFront)"
→ ValidationException

서비스마다 태그 규칙이 다르다. 다른 데서 되던 태그가 여기선 안 된다.

문자 — 티어가 아니라 한도를 먼저 본다

문자 발송이 갑자기 안 될 때, 보통 "샌드박스인가" "티어 문제인가"부터 본다. 그런데 실제로는 지출 한도에 걸린 경우가 많다.

aws pinpoint-sms-voice-v2 describe-spend-limits --region <리전>

한도에 걸리면 조용히 발송만 실패한다. 에러가 눈에 띄게 나지 않는다.

그래서 월 지출 알람을 2단으로 걸었다. 경고 단계에서 먼저 알고, 한도에 닿기 전에 손을 쓸 수 있게.

알림 채널 자체가 안전한지도 봐야 한다

알람을 아무리 잘 만들어도 안 도착하면 없는 것이다. 여기서 두 가지를 겪었다.

확인 메일이 스팸함으로 간다

SNS 구독 확인 메일이 Gmail 스팸함에 들어갔다. 문제는 실제 알람도 같은 발신자라는 것이다. 스팸 해제만 하고 필터를 안 걸면, 정작 경고를 놓친다.

수신자를 추가할 때마다 발신자 허용을 같이 처리하는 것을 절차로 넣었다.

이메일은 전달 실패를 알 방법이 없다

SNS는 이메일 프로토콜에 대해 전달 상태 로깅을 지원하지 않는다. 보낸 쪽에서는 성공으로 보이고, 안 왔다는 걸 알 방법이 계정 쪽에 없다.

알림 경로는 주기적으로 실제로 울려 봐야 한다. 이게 유일한 검증이다.

정리

  • 리드타임 있는 항목(문자 발신번호·메일 프로덕션 전환)은 인프라 코드보다 먼저 신청한다.
  • DNS는 (이름, 타입)이 레코드셋 하나다. 새 리소스를 만들면 덮어쓴다.
  • TXT 문자열은 255바이트. DKIM은 나눠 담고, 나눠 보이는 게 정상이다.
  • 인증서 검증 레코드는 리전이 달라도 같다. 새로 안 만들어도 된다.
  • 문자가 안 나가면 티어가 아니라 한도를 본다.
  • 알림 경로는 만들어 놓고 끝이 아니다. 스팸함과 전달 실패를 감안한다.

다음 편은 관측이다. 야간 요청이 낮의 9.5배인데 이유를 몰랐다. 액세스 로그가 전부 꺼져 있었기 때문이다.

댓글