직원들이 보내는 메일은 잘 도착하는데, 새로 도입한 뉴스레터만 인증에 실패한다고 가정해 보겠습니다. 뉴스레터 서비스에서는 “SPF 값을 추가하세요”라고 안내합니다. DNS에는 이미 SPF가 있습니다. 새 줄을 만들면 될까요, 기존 값을 바꾸면 될까요?
이때 필요한 것은 발신 서비스의 이름을 모두 적어 넣는 작업이 아닙니다. 어느 도메인의 발신 권한을, 어떤 서버에 줄 것인지 결정해야 합니다. 주소와 정책의 관계를 이해하면 긴 문자열도 읽을 수 있고, 설정 후 무엇을 확인해야 할지도 분명해집니다.
SPF(Sender Policy Framework)는 도메인 관리자가 DNS에 공개하는 발신 서버 허용 목록입니다. DNS는 도메인 정보를 찾아보는 인터넷 주소록이고, SPF는 그중 TXT라는 형식으로 저장하는 정보입니다. 이 글에서는 직원 메일에 뉴스레터와 청구서 서버를 추가하는 가상의 회사를 따라가 보겠습니다. example.com 등의 예시 도메인과 IP는 설명용이며, 실제 DNS에 그대로 등록하면 안 됩니다.
SPF를 어디에 넣을지, 두 주소부터 구분합니다
이메일에는 사람이 읽는 발신 주소와 서버끼리 전송할 때 쓰는 발신 주소가 있습니다. 메일 앱의 보낸 사람 칸은 From입니다. 배달 실패 안내를 돌려보낼 주소는 SMTP 전송의 MAIL FROM이며, 받은 메일에서는 보통 Return-Path 헤더로 확인합니다.
From: news@example.com
Return-Path: bounce@bounce.example.com
SPF TXT lookup: bounce.example.com이 메일을 받은 서버는 접속한 발신 IP를 bounce.example.com의 SPF와 비교합니다. 화면에 news@example.com이 보인다는 이유로 루트 도메인인 example.com의 SPF를 대신 읽지 않습니다. 부모 도메인의 SPF가 하위 도메인에 자동 상속되지도 않습니다.
- 01접속한 서버 확인
메일을 건넨 서버의 공인 IP와 MAIL FROM을 읽습니다.
- 02정확한 도메인 조회
MAIL FROM의 @ 뒤 도메인에서 SPF TXT를 찾습니다.
- 03허용 목록과 비교
그 IP가 정책에 해당하는지 검사해 결과를 남깁니다.
외부 서비스가 자신의 도메인을 MAIL FROM으로 쓰면, 검사 대상도 그 서비스의 도메인입니다. 이때 회사 루트 도메인에 그 서비스의 include를 추가해도 해당 메일의 SPF 검사에는 쓰이지 않을 수 있습니다. 서비스 관리자 화면의 “사용자 지정 MAIL FROM” 또는 “반송 도메인” 설정과 실제 수신 헤더를 먼저 대조하세요.
배달 실패 통지처럼 MAIL FROM이 비어 있는 메일에서는 서버가 인사할 때 밝히는 HELO 도메인이 사용됩니다. 일반 업무 메일 설정은 MAIL FROM부터 확인하면 됩니다. SPF가 검사하는 발신자 정보 ↗
긴 SPF 문자열은 왼쪽부터 읽으면 됩니다
아래는 직접 관리하는 서버 한 대와 외부 서비스 하나를 허용하는 가상 정책입니다. 첫 항목 v=spf1은 SPF 버전 표시입니다. 그다음 조건은 왼쪽부터 검사하고, 일치하는 조건을 만나면 그 결과로 평가를 끝냅니다. 일반적인 허용 항목은 앞에 기호를 생략하면 +, 즉 pass입니다.
v=spf1 ip4:192.0.2.40 include:_spf.news.example.net -all접속 IP가 192.0.2.40이면 첫 조건에서 pass입니다. 다른 IP라면 include로 넘어갑니다. 이는 외부 정책의 글자를 복사해 붙인다는 뜻이 아니라, 같은 발신 IP로 그 도메인의 SPF도 평가해 보라는 뜻입니다. 그 평가가 pass이면 이 include에 일치하고, 여기서도 pass가 됩니다.
참조한 정책이 fail·softfail·neutral이면 다음 조건으로 진행합니다. 따라서 외부 정책 안의 -all을 만났다고 회사 SPF도 곧바로 fail이 되지는 않습니다. 반면 참조 대상에 SPF가 없거나 정책 오류가 있으면 permerror가 될 수 있고, 일시적인 DNS 오류는 temperror로 이어질 수 있습니다. include의 평가 규칙 ↗
마지막 all은 앞에서 일치하지 않은 나머지 IP를 모두 받는 조건입니다. 이 예시에서는 -가 붙었으므로 fail입니다. all 뒤에 include를 써도 그 허용 조건까지 도달하지 않으므로, 추가 서비스는 마지막 all 앞에 넣어야 합니다.
~all과 -all은 통과의 강도가 아니라, 나머지 발신자에 대한 판단입니다
~all의 softfail은 “허용한 발신자로 보기 어렵다”는 약한 부정이고, -all의 fail은 “이 도메인으로 발송하도록 허용하지 않았다”는 명확한 부정입니다. 둘 다 SPF pass가 아닙니다. 어느 것도 메일을 반드시 스팸함에 넣거나 삭제하라는 강제 명령은 아닙니다. 최종 처리는 수신 서버의 정책과 다른 검사 결과에 달려 있습니다.
표를 좌우로 밀어 전체 내용을 확인하세요.
| 항목 | 역할 | 사용 기준 |
|---|---|---|
| v=spf1 | SPF 레코드임을 표시 | 맨 앞에 한 번 사용 |
| ip4: / ip6: | 지정 IP 또는 범위 허용 | 직접 관리하는 공인 발신 IP가 확실할 때 |
| include: | 다른 SPF의 pass 여부 평가 | 사용 중인 서비스가 공식 제공한 도메인 사용 |
| ~all | 나머지는 softfail | Google Workspace의 공식 권고. 누락 여부도 별도 확인 |
| -all | 나머지는 fail | Microsoft 365의 공식 권고. 모든 정상 발신처 확인 필요 |
| ?all / +all | 나머지는 neutral / pass | 일반적인 기업 발신 허용 목록의 기본값으로 쓰지 않음 |
Google Workspace와 Microsoft 365의 권고가 다르므로 둘 중 하나를 무조건 정답으로 볼 필요는 없습니다. 이용하는 서비스의 안내와 전체 발신 구성을 따르되, 강하게 설정했다는 이유만으로 점검을 생략하지 마세요. 사용하지 않는 도메인에 발신 서버를 하나도 허용하지 않으려면 v=spf1 -all로 표현할 수 있습니다. 이는 화면에 보이는 From 주소 도용까지 단독으로 막아 주지는 않습니다. Google의 항목 설명 ↗
a와 mx도 발신 IP를 허용하는 조건이지만, 웹 서버 주소나 수신 메일 서버가 발신 서버와 같다는 보장은 없습니다. 필요 없이 넣으면 허용 범위와 DNS 조회 부담이 커집니다. redirect=는 다른 정책으로 평가를 넘길 때 쓰며 서비스 추가용 include와 다릅니다. all이 있는 정책에서는 redirect가 적용되지 않습니다.
발신처를 모은 뒤, SPF 레코드 하나를 수정합니다
설정 전에 현재 SPF 값을 보관하고 발송 담당자에게 직원 메일 외의 경로를 확인합니다. 홈페이지 알림, 뉴스레터, 청구서처럼 평소 메일 앱에 보이지 않는 서비스도 포함하세요. 각 서비스의 실제 MAIL FROM을 적으면 같은 SPF에 합칠 것과 별도 도메인에 설정할 것이 구분됩니다.
표를 좌우로 밀어 전체 내용을 확인하세요.
| 발신 경로 | 허용 정보 | 확인할 근거 |
|---|---|---|
| 직원 메일 | Google Workspace include | 관리자가 사용하는 실제 메일 서비스 |
| 새 뉴스레터 | _spf.news.example.net | 가상 서비스가 제공했다고 가정한 SPF 도메인 |
| 자체 청구서 서버 | 192.0.2.40 | 가상 서버가 외부로 메일을 보낼 때의 공인 IP |
직원 메일만 보내던 회사라면 기존 값은 다음과 같을 수 있습니다. 이 값은 Google Workspace만 사용하는 경우의 공식 예시입니다.
Type: TXT
Name: @
Value: v=spf1 include:_spf.google.com ~all@는 많은 DNS 관리 화면에서 도메인 자체를 뜻합니다. 업체에 따라 빈칸이나 전체 도메인을 요구하므로 입력 도움말을 확인합니다. Google SPF 등록 안내 ↗
Type: TXT
Name: @
Value: v=spf1 include:spf.protection.outlook.com -allGoogle 예시와 Microsoft 예시는 운영 환경별 대안입니다. 둘을 모두 이용하지 않는다면 두 서비스를 함께 허용할 이유가 없습니다. Microsoft 정부 전용·중국 운영 환경이나 별도 게이트웨이는 값이 다를 수 있습니다. Microsoft의 환경별 SPF 값 ↗
뉴스레터를 추가할 때 SPF를 하나 더 만들면 안 됩니다
가상 회사의 세 발신처를 한 정책으로 합치면 다음 구조가 됩니다. 버전 표시는 앞에 한 번, 나머지 발신자에 대한 all은 끝에 한 번 둡니다. 기존 Google 허용 항목을 남겨 두었으므로 직원 메일의 발신 권한도 유지됩니다.
v=spf1 ip4:192.0.2.40 include:_spf.google.com include:_spf.news.example.net ~all실제 적용할 때는 뉴스레터 서비스가 제공한 현재 값과 서버의 공인 발신 IP로 구성해야 합니다. PC의 사설 IP나 홈페이지 IP를 대신 쓰면 안 됩니다. 서비스가 회사의 하위 도메인에 별도 MAIL FROM을 구성하도록 안내한다면, 그 이름에 필요한 정책을 설정합니다.
- 실제로 사용 중인 DNS 관리 화면을 엽니다
도메인을 구매한 업체와 DNS를 운영하는 업체가 다를 수 있습니다. 현재 네임서버를 확인하고 해당 DNS에서 수정합니다.
- 해당 이름의 기존 SPF를 찾습니다
TXT 중 v=spf1로 시작하는 레코드를 확인합니다. 소유권 확인용 TXT 같은 다른 레코드는 그대로 둡니다.
- 기존 SPF 한 개에 필요한 발신처를 합칩니다
변경 전 값을 보관하고 등록 이름·유형·전체 값을 확인합니다. TTL은 DNS 제공업체와 메일 서비스의 운영 안내를 따릅니다.
따옴표 두 쌍과 SPF 두 개는 다릅니다
TXT 한 레코드는 여러 문자열 조각으로 표현될 수 있습니다. 조각 하나의 길이 제한은 255바이트이며, 긴 값은 DNS 업체가 나눠 저장하기도 합니다. SPF를 따로 두 레코드로 게시하는 것과는 다릅니다.
One TXT record:
"v=spf1 ip4:192.0.2.40 " "include:_spf.news.example.net ~all"
Two TXT records — incorrect:
"v=spf1 ip4:192.0.2.40 ~all"
"v=spf1 include:_spf.news.example.net ~all"한 레코드 안의 문자열은 공백을 추가하지 않고 이어 붙입니다. 위 정상 예시는 첫 조각 끝에 공백이 있어 조건이 붙어 버리지 않습니다. 아래 중복 예시는 어느 SPF를 적용할지 고르는 구조가 아니며 permerror를 일으킵니다. 관리 화면의 줄바꿈만으로 판단하지 말고 실제 TXT 레코드 단위로 확인하세요. TXT 문자열과 레코드의 구분 ↗
include 세 개가 조회 한도 열 개를 채울 수 있습니다
SPF에는 DNS 조회를 유발하는 평가 항목의 합계를 최대 10개로 제한하는 규칙이 있습니다. include, a, mx, exists, ptr, redirect가 해당하며, 참조한 정책 안에서 평가하는 항목도 같은 한도를 씁니다. “눈에 보이는 include가 열 개 이하”만 확인해서는 부족합니다.
아래는 계산을 위한 별도 가상 구조입니다. 회사 정책에는 include가 세 개뿐이지만, 각 서비스가 다른 SPF를 다시 참조합니다. 모든 DNS 조회가 정상이고 어떤 허용 IP에도 일치하지 않아 끝까지 검사한다고 가정하겠습니다. 말단 정책은 모두 ip4와 -all만으로 구성되어 있습니다.
example.com
├─ include:a.example (1)
│ ├─ include:a1.example (2)
│ └─ include:a2.example (3)
├─ include:b.example (4)
│ ├─ include:b1.example (5)
│ └─ include:b2.example (6)
└─ include:c.example (7)
├─ include:c1.example (8)
├─ include:c2.example (9)
└─ include:c3.example (10)A에서 3개, B에서 3개, C에서 4개를 평가해 합계 10개입니다. 여기에 C 서비스가 include:c4.example을 추가하면 11번째 항목에 도달하는 메일은 permerror가 됩니다. 회사 DNS를 바꾸지 않았어도, 참조하는 외부 정책이 바뀌면 한도를 넘을 수 있는 이유입니다.
반대로 첫 번째 말단인 a1에서 IP가 일치하면 평가가 끝나므로 이 경로에서는 2개만 사용합니다. 따라서 한 발신 IP로 통과했다는 사실만으로 전체 구성이 안전하다고 판단하면 안 됩니다. 각 발신 경로와 끝까지 검사하는 경로를 함께 점검해야 합니다.
여기서 세는 것은 네트워크에 오간 DNS 요청 패킷 수가 아닙니다. 처음 회사 SPF를 읽는 TXT 조회를 위 합계에 하나 더하지 않으며, 캐시에 있다는 이유로 include의 비용을 빼지도 않습니다. ip4, ip6, all은 이 10개 한도를 소비하지 않습니다. MX의 주소 조회나 빈 DNS 응답에는 별도 제한도 있으며, ptr는 사용하지 않는 것이 권고됩니다. SPF 조회 제한 ↗
공개 DNS와 실제 수신 헤더를 차례로 확인합니다
DNS 관리 화면의 저장 완료와 수신 서버가 새 값을 읽는 시점은 다릅니다. TTL은 조회한 정보를 얼마 동안 캐시에 둘지 정하는 값입니다. 이전 값을 보관 중인 서버는 당장 바뀌지 않을 수 있습니다. 변경 시간을 기록하고 공개 DNS에서 정확한 이름과 값이 보이는지 확인합니다.
dig @8.8.8.8 TXT example.com +short
dig @1.1.1.1 TXT example.com +short출력에서 SPF 레코드가 하나인지, 유지해야 할 발신처가 남아 있는지, 새 include가 마지막 all 앞에 있는지 봅니다. 결과가 서로 다르면 캐시뿐 아니라 실제 관리 중인 DNS가 맞는지도 확인하세요. 공개 조회 두 곳이 같다고 모든 수신 서버가 갱신됐다는 뜻은 아닙니다.
다음으로 각 서비스에서 실제 외부 수신함으로 새 메일을 보냅니다. 직원 메일에서 한 통 보낸 것으로 뉴스레터 설정까지 검증되지는 않습니다. Gmail에서는 메일의 “원본 보기”로 전체 헤더를 열 수 있습니다. 수신 시스템이 기록한 Authentication-Results의 SPF 결과와 smtp.mailfrom을 함께 읽습니다. Google의 헤더 확인 방법 ↗
Before: newsletter sender missing from SPF
Authentication-Results: mx.receiver.example.org;
spf=softfail smtp.mailfrom=example.com
After: authorized newsletter sender added
Authentication-Results: mx.receiver.example.org;
spf=pass smtp.mailfrom=example.com이 예시는 뉴스레터가 example.com을 MAIL FROM으로 사용하지만 Google만 허용된 SPF에서는 일치하지 않아 ~all에 도달한 상황입니다. 가상의 뉴스레터 발신 IP 198.51.100.20을 허용하도록 서비스가 안내한 include를 추가한 뒤, 같은 발송 경로의 새 메일에서 pass를 확인합니다. 이전 메일의 결과는 DNS를 고친다고 바뀌지 않습니다.
다른 곳에 적힌 pass 한 줄을 골라 보면 안 됩니다. 여러 중계 서버의 헤더가 있다면, 최종 수신 서비스가 신뢰하는 인증 결과를 기준으로 봐야 합니다. 발신자가 임의로 넣은 헤더도 있을 수 있습니다. 발신 IP는 수신 서버의 Received-SPF나 관련 로그와 대조하고, 인증 결과가 보이지 않으면 수신 시스템 관리자에게 확인합니다. 인증 결과 헤더의 신뢰 범위 ↗
SPF pass 다음에는 어떤 도메인이 통과했는지 봅니다
DMARC는 인증한 도메인이 화면의 From과 맞는지 확인합니다. 이 관계가 도메인 정렬입니다. 기본 완화 모드에서는 bounce.example.com과 example.com처럼 조직 도메인이 같아도 되지만, 엄격 모드는 정확히 같은 도메인을 요구합니다.
외부 서비스가 mailer.example.net으로 SPF를 통과했다고 해도 화면의 From이 example.com이면 두 도메인은 정렬되지 않습니다. 회사 SPF를 아무리 수정해도 외부 서비스의 MAIL FROM이 그대로라면 이 관계는 바뀌지 않습니다.
서비스가 지원하는 회사 도메인의 MAIL FROM을 설정하거나, 회사 도메인으로 DKIM 서명을 구성해 DMARC를 통과할 수 있습니다. DMARC는 SPF 통과와 From 정렬, 또는 DKIM 통과와 From 정렬 중 한 경로가 필요합니다. softfail도 이때 SPF 통과로 인정되지 않습니다. DMARC의 SPF 도메인 정렬 ↗
실패 결과를 읽으면 고칠 위치가 좁혀집니다
SPF 실패가 곧 주소 도용이라는 뜻은 아닙니다. 허용할 서버를 빠뜨렸는지, 정책 자체를 읽을 수 없는지, 전달 경로가 바뀌었는지에 따라 해결할 곳이 다릅니다. 다음 표에서 자신의 결과를 찾고 수정 후에는 같은 발신 서비스로 다시 보내 확인하세요.
표를 좌우로 밀어 전체 내용을 확인하세요.
| 결과 | 무엇을 뜻하나 | 다음 확인 |
|---|---|---|
| pass | 접속 IP가 해당 도메인으로 발송할 권한을 얻음 | smtp.mailfrom과 From 정렬, DKIM·DMARC도 확인 |
| fail / softfail | 정책은 평가했지만 허용 조건에 일치하지 않음 | 실제 발신 IP, 누락된 서비스, 전달 여부 확인 |
| permerror | 정책을 정상적으로 평가할 수 없는 오류 | 중복 SPF, 잘못된 include 대상·문법, 조회 한도 확인 |
| temperror | DNS 조회 등에서 일시적 오류 발생 | DNS 서비스와 응답 상태를 확인한 뒤 재검사 |
| none | 검사할 SPF를 찾지 못한 경우 등 | 정확한 MAIL FROM 도메인에 TXT가 있는지 확인 |
| neutral | 정책이 발신 권한을 확정하지 않음 | ?all 또는 일치 조건·기본 결과를 점검 |
전달된 메일만 실패한다면 발신 IP가 바뀌었을 수 있습니다
뉴스레터를 바로 받으면 pass인데, 동료가 자동 전달한 메일은 실패할 수 있습니다. 최종 수신 서버는 원래 뉴스레터 서버가 아니라 메일을 다시 건넨 전달 서버의 IP를 보기 때문입니다.
- 01원래 발송 서버
198.51.100.20에서 보냅니다. 이 IP는 허용되어 있습니다.
- 02자동 전달 서버
203.0.113.70이 메일을 다른 수신함으로 다시 보냅니다.
- 03최종 수신 서버
203.0.113.70을 검사하므로 원래 SPF에 일치하지 않습니다.
이 문제를 없애려고 알 수 없는 전달 서버를 모두 회사 SPF에 넣지는 마세요. 전달 서비스가 MAIL FROM을 자신의 주소로 바꿔 SPF를 통과시키더라도 회사 From과의 정렬은 별개입니다. 원래 회사 도메인의 DKIM 서명이 유효하게 유지되면 SPF가 실패해도 DMARC를 통과할 수 있습니다. 전달 중 서명한 내용이 바뀌면 DKIM도 실패할 수 있으므로 결과를 함께 봐야 합니다.
설정 작업은 TXT를 저장했을 때 끝나지 않습니다. 직원 메일·뉴스레터·자동 알림 각각에서 의도한 도메인의 SPF pass를 확인하고, DKIM과 DMARC까지 점검했을 때 운영에 쓸 수 있는 상태가 됩니다. 발신 목록에 담당자와 마지막 확인 날짜를 남겨 두면 다음 서비스를 추가하거나 계약을 종료할 때 같은 문제를 반복하지 않을 수 있습니다.
Genharden DMARC · 2026.09.21 기준. SPF·DKIM·DMARC 표준과 본문에 연결한 공식 문서를 참고했습니다. 회사·도메인·IP·집계 수치는 별도 표시가 없는 한 설명을 위한 가상 예시입니다.