들어가기
일반 웹 통신(HTTP)와 다르게 메일 전송을 위해서는 SMTP를 사용한다.
SMTP(Simple Mail Transfer Protocol)는 이메일을 전송하기 위해 사용되는 인터넷 표준 프로토콜이며, SMTP는 이메일 메시지를 작성한 후, 해당 메시지를 수신자에게 전달하는 과정에서 주로 사용된다. 이 프로토콜은 발신자의 이메일 서버에서 수신자의 이메일 서버로 메시지를 전송하는 역할을 한다.
지난번 Spring boot를 상요하여 프로젝트를 진행할 때, JavaMailSender, Gmail을 통한 메일 전송 기능을 구현하였다.
구현하면서 드는 생각은, SMTP를 사용하였지만, SMTP 통신 과정이 어떻게 이루어지는지 정확히 파악하지 못하였다는 것이다.
학습을 하는 과정은 단순히 사용하는 것만이 아니라 이해하는 것이 중요하다고 생각한다.
그래서 SMTP 통신 과정과 Spring boot에서 어떻게 사용되었던 것인지 학습한 내용을 이 글에서 설명하고자 한다.
Spring boot와 Gmail을 이용하여 메일 전송 기능에 대해 궁금하다면 아래의 내용을 참고하길 바란다.
https://khdscor.tistory.com/131
목차는 아래와 같다.
2. Spring boot에서 JavaMailSender을 사용한 방식과 어떻게 다를까?
본론
1. 메일 시스템의 구조
메일 시스템은 크게 사용자 환경과 네트워크 환경으로 구분된다.
사용자 환경에서는 UA(User Agent)를 통해 메일 시스템에 접근할 수 있으며, 우리가 흔히 볼 수 있는 메일 인터페이스 즉, 메일 프로그램이나 구글, 네이버 같은 사이트에서 볼 수 있는 메일 기능을 말한다.
네트워크 환경에서는 MTA(Mail Transfer Agent)가 통해서 메일을 송수신하고 중개하는 기능을 수행한다.
이때, 송신과 수신 사이에는 SMTP(Simple Mail Transfer Protcol), POP3(Post Office Protocol) 혹은 IMAP(Internet Messaging Access Protocol)을 통해서 전송이 되는데, 아래의 그림과 같다.
UA에서 MTA로 STMP를 사용한 전송을 하면, MTA에서 MTA로 SMTP를 사용한 통신 과정이 일어난다. 예를 들면 네이버에서 구글 메일로 이메일을 보낼 때, 네이버 메일 서버 MTA에서 구글 메일 서버 MTA로 통신이 일어난다.
그리고 수신자 측 MTA에서는 수신한 메일을 보관하고 있는데, POP3, IMAP를 사용하여 메일 프로그램으로 메일을 전송(다운로드)하게 된다.
POP3는 메일을 로컬로 다운로드하고 서버에서 삭제하는 방식으로 서버에 메일이 남지 않는다. 하지만 요즘 시대에는 메일을 PC 뿐만 아니라 스마트폰, 태블릿 등 여러 클라이언트에서 동시에 접근이 필요할 때가 있다.
IMAP는 서버에서 메일을 관리하며, 여러 클라이언트에서 동시 접근이 가능하다. 즉, 메일은 서버에 계속 유지되는 것이다. 즉, POP3는 오프라인 메일 관리를 위한 것이고, IMAP는 실시간 메일 동기화와 관리에 적합하다고 할 수 있다.
그렇다면 MTA 서버는 어떻게 생성할 수 있을까?
이론적으로는 어떤 서버(ex.AWS EC2)든 MTA로 활용할 수 있다. 하지만 MTA로 사용하려면 추가적인 작업이 필요하다. SMTP 프로토콜을 구현하고, 메일 송수신 기능을 제공하는 소프트웨어를 설치하거나, 직접 구현해야 한다. 널리 사용되는 오픈소스 MTA로는 Postfix, Exim, Sendmail 등이 있다.
그리고 MTA 서버로 통신을 할때, 해당 서버의 도메인 이름과 호스트 이름을 통해 진행하는데, Google 메일은 'khds@gmail.com'와 같이 'khds'가 호스트고 @뒤에 'gmail.com'이라는 도메인 이름을 가진 서버로 SMTP 통신을 진행하는 것과 같다.
예를 들어 192.67.132.41, 192.67.132.42라는 IP로 MTA를 운영하고 있고, 첫 번째 서버에서 두 번째 IP 서버의 test라는 호스트에게 메일 전송을 한다고 하면, 수신자는 'test@192.67.132.42'로 설정하여 SMTP 통신을 하는 것이다.
물론 단순히 수신자 이메일 주소만 가지고 SMTP 통신을 진행하는 것은 아니며, 아래의 '3. SMTP 통신 과정'에서 자세히 살펴보겠다.
그렇다면 192.67.132.41 IP로 MTA를 구축하고 구글이나 네이버 메일 주소로 메일을 전송하는 것이 가능할까?
아쉽게도 단순히 메일 기능을 구축했다고 하더라도 바로 이용할 수는 없다. 대부분의 메일 서비스(MTA)나 클라이언트는 IP 주소 기반의 메일 송수신을 허용하지 않는 경우가 많다. 일반적으로 메일 주소는 도메인 기반으로 설정해야 한다.
예를 들어, test@192.67.132.42 형태의 이메일 주소는 메일 시스템에서 신뢰하지 않거나 필터링될 수 있다. 이를 방지하려면 IP가 아닌 도메인 이름을 설정하고, 해당 도메인을 인증하고 관리하는 것이 중요하다. 또한, 메일 송수신을 서버를 구축하기 위해서는 도메인 설정뿐만 아니라 DNS 레코드 설정(SPF, DKIM, DMARC 등), IP 주소의 신뢰성 관리(블랙리스트 등록 방지) 등도 중요하다고 한다.
마지막으로 POP3/IMAP 서버에 대해서 간단하게 알아보자. SMTP로 메일 전송 기능을 확인하였는데, 메일 수신은 POP3/IMAP를 통해서 진행된다. POP3/IMAP 서버는 Dovecot(POP3/IMAP)를 통해 구축할 수 있다.
두 역할을 담당하는 서버는 같은 서버에서 실행될 수도 있고 분리될 수도 있다.
이는 시스템의 규모나 설계에 따라 다른데, 만약 작은 시스템이나 개인용 서버에서 메일시스템을 구축할 경우 SMTP와 POP3/IMAP를 같은 서버에서 실행할 것이다. 이 경우 하나의 서버에서 메일 송신, 수신을 처리하는 점에서 관리가 쉽다는 장점이 있다.
하지만 리소스가 많이 필요할 경우, 한 서버에서 모든 작업을 처리하는 것은 힘들고, 장애가 발생할 경우 모든 메일 서비스가 중단될 수 있다.
보통 대규모 시스템이나 기업용 메일 시스템에서는 SMTP 서버와 POP3/IMAP 서버를 별도의 서버에서 실행하는 경우가 많다. SMTP 서버는 메일 전송만 담당하고, POP3/IMAP 서버는 메일을 수신 및 보관하는 역할만 담당하도록 말이다. 역할을 분리하였기에 더 높은 성능과 안정성, 확장성을 가진다는 장점이 있지만, 시스템 관리와 서버 간 통신 및 네트워크 설정이 더 복잡해질 수 있다는 단점이 있다.
2. Spring boot에서 JavaMailSender을 사용한 방식과 어떻게 다를까?
서론에서 언급했다시피 필자는 Spring boot에서 JavaMailSender을 통해 미리 설정한 Gmail 계정으로 메일 전송 기능을 구현하였다.
그렇다면 JavaMailSender을 사용하는 것은 MTA로서 동작하는 것인가? 하는 의문이 있었다. 하지만 MTA는 구글에서 따로 서버를 운영 중이고 JavaMailSender을 사용하는 것 UA 즉, 사용자가 메일 프로그램으로 메일을 작성하고 보내기 버튼을 클릭한 것과 같다.( JavaMailSender을 사용한 메일 전송 기능에 대한 내용은 https://khdscor.tistory.com/131를 참고하길 바란다.)
기본적으로 동일한 SMTP 프로토콜을 사용하므로, 송신 흐름 자체는 유사하며, 일반 사용자가 구글이나 다른 메일 서비스의 웹 인터페이스에서 메일을 작성하고 보내는 것은 UI 레벨에서의 차이일 뿐, 뒤에서 수행되는 동작은 이메일 클라이언트가 SMTP를 통해 MTA로 이메일을 전송하는 것이다.
즉, 아래와 같이 송신자, 수신자, 메일 제목, 메일 내용을 작성하는 과정이 메일 프로그램에서 작성하는 과정과 같다고 할 수 있다.
@Service
public class MailService {
@Autowired
private JavaMailSender mailSender;
public void sendMail() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("test@yourdomain.com"); // 보내는 사람 주소
message.setTo("recipient@example.com"); // 받는 사람 주소
message.setSubject("Test Mail");
message.setText("This is a test mail sent from Spring Boot using Sendmail.");
mailSender.send(message);
}
}
그리고 아래와 같이 properties, yml 파일에 작성한 정보에 해당하는 MTA로 작성한 메일 정보를 보내는데, 아래는 구글에서 운영하는 서버로 보내는 내용이다.
spring:
mail:
host: smtp.gmail.com
port: 587
username: test@gmail.com
password: testtestesttest
default-encoding: UTF-8
properties:
mail:
mime:
charset: UTF-8
smtp:
auth: true
timeout: 5000
starttls:
enable: true
물론 자체 구축한 MTA 서버를 활용할 수 있다.
spring:
mail:
host: 192.67.132.41 # 자체 MTA의 IP 주소 또는 도메인
port: 25 # 기본 SMTP 포트 (25(비보안), 587(STARTTLS 보안), 465(SSL 보안))
username: test@mydomain.com # 해당 서버에서 사용할 이메일 계정, auth를 false로 한다면 빈 값 설정
password: password # 해당 계정의 비밀번호, auth를 false로 한다면 빈 값 설정
default-encoding: UTF-8
properties:
mail:
mime:
charset: UTF-8
smtp:
auth: true # 자체 MTA에서 TLS와 SMTP 인증을 사용않는다면 false
timeout: 5000 # 타임아웃 설정 (밀리초)
starttls:
enable: false # 자체 MTA에서 TLS를 사용하지 않으면 false로 설정
ssl:
enable: false # SSL을 사용하지 않는 경우 false
자체 MTA에 따라 인증을 하지 않을 수 있으며, sendmail을 사용하여 MTA를 구축한 경우 기본 값으로 인증을 사용하지 않기에 auth는 false로 넣는다.
참고로 POP3 또는 IMAP 같은 프로토콜을 사용하여 메일을 처리할 수 있는 UA 또한 Spring boot에서 구현 가능하다. SMTP, POP3 서버 구축 과정은 내용이 길어질 것이라 생각하여, 다음에 이어서 작성하겠다.
3. SMTP 통신 과정
그렇다면 MTA는 SMTP를 통해 어떻게 통신을 진행할까?
MTA는 단순히 SMTP 명령과 그에 대한 응답 과정을 반복해 메일을 전송한다.
SMTP 주요 명령어는 아래와 같다.(참고: 쉽게 배우는 데이터 통신과 컴퓨터 네트워크 - 박기현)
명령 | 의미 |
HELO + (송신자의 호스트 이름) | SMTP 세션을 시작하며, 송신자의 호스트 이름을 전송하여 서버에 자신의 신분을 알려준다. |
MAIL + (송신자의 메일 주소) | 송신자의 메일 주소를 통지한다. |
RCPT + (수신자의 메일 주소) | 수신자의 메일 주소를 통지한다. |
DATA + (메시지의 매용) | 메일 메시지를 송신한다. |
QUIT | 더 이상의 전송 메시지가 없음을 통지하여 SMTP 세션을 종료한다. |
RSET | 현재의 연결 상태가 종료되었음을 통지하고, 연결 재설정이 이루어진다. |
VRFY + (수신자의 메일 주소) | 수신자의 주소를 조회하려고 사용한다. |
NOOP | 수신자의 상태를 검사하려고 사용한다. |
그렇다면 이에 대한 응답은 어떻게 나타날까? HTTP를 사용하는 웹서버를 개발하다보면 Status Code에 대해 알 것이다. 200, 201, 400 404, 500 등 성공, 오류 코드들이 있는데, SMTP에서도 동일하게 코드를 통해 응답 상태를 확인할 수 있다.
응답 코드는 2yz, 3yz, 4yz, 5yz로 나뉘며(yz는 범주. ex. 2yz는 2xx, 3yz는 3xx), 각각의 의미는 아래와 같다.
2yz: 요청 성공(ex. 220 (서비스 준비 완료), 250 (요청 명령 성공))
3yz: 중간 상태(추가 작업 필요), 클라이언트가 요청을 완료하기 위해 추가적인 작업이나 입력이 필요함.(ex. 354 (메일 입력 시작. "."으로 끝내십시오))
4yz: 일시적 요류, 나중에 다시 시도할 수 있다. 서버에서 문제가 해결되면 클라이언트는 동일한 요청을 재시도할 수 있다.(ex. 421 (서비스 사용 불가. 나중에 다시 시도하십시오))
5yz: 영구적 오류, 클라이언트의 요청이 영구적으로 실패.(ex. 550 (요청한 명령을 처리할 수 없음. 주소가 존재하지 않음))
메일을 보내는 과정을 절차상 3단계로 구분된다.
1. 송신측 MTA(SMTP 클라이너트)가 TCP를 이용해 수신 측 MTA(SMTP 서버)와 연결(세션)을 설정한다.
2. 수신측 MTA에 메일을 보내는 데이터 전송을 진행한다.
3. TCP 연결을 종료하여 SMTP 세션이 종료된다.
1. 연결 설정
우선 수신측 MTA에 대한 접근 가능성을 확인하여 연결 설정을 해야 한다. 만약 이 단계에서 서버가 다운되는 등의 상황이 생긴다면 이후 단계는 진행될 수 없다. 하지만 메일 시스템은 기본적으로 메시지를 실시간으로 처리하지 않고, 메시지 저장과 중개라는 개념에 따라 동작하기에 이런 문제를 극복할 수 있다.
연결 흐름은 아래와 같다. 25, 587, 465 중 수신측 MTA에 맞게 TCP 연결 요청을 보내면 220, 421 코드 중 하나를 보내온다. 421 코드는 연결 준비가 아직이라는 코드고, 220 코드는 준비가 완료되었다는 긍정적 코드다. 220 코드를 받은 후 HELO 명령과 함께 송신자의 호스트 이름을 전송하고, 250 응답 코드를 받으면서 연결 설정을 완료된다.
2. 데이터 전송
연결이 완료된 후 본격적으로 메일 내용을 전송하는 단계이다. 먼저 MAIL, RCPT 명령으로 송신자와 수신자의 메일 주소를 전달하고, 해당 MTA 호스트 목록에 수신자가 존재하는지 확인 후 응답 코드를 반환한다(ex. khds@gmail.com에서 gmail.com 서버에 등록된 호스트는 khds).
이 후 DATA 명령을 시작으로 헤더 정보를 순차적으로 전송한다. 모든 헤더가 들어갈 필요는 없다(To, CC, BCC, From, Sender, Date, Subject 등). 내용을 작성한 후 '.' 을 입력하여 데이터 전송을 완료한 후, 250 응답 코드를 받으며 데이터 전송이 완료된다.
만약 MTA 서버를 자체적으로 구축한다면 아래의 흐름은 참고일 뿐, 다른 로직으로 구성하여도 무방하다.
3. 연결 종료
QUIT 명령어를 통해 연결을 종료한다.
4. POP3, IMAP 통신 과정
SMTP 통신을 알아보았으니 POP3와 IMAP 통신 과정을 알아보자.
POP3, IMAP도 SMTP와 유사하게 연결, 데이터 처리, 연결 종료 흐름을 명령과 응답을 통해 진행된다. 그런데 연결을 요청하는 주체는 MTA가 아니다. UA이다. 이는 UA가 사용자의 명령을 받아 이메일을 서버에서 가져오거나 관리하는 역할을 하기 때문이다.
아래는 POP3, IMAP에 해당하는 일부 명령어이다.
POP3
명령 | 의미 |
USER + ( POP3 서버의 로그인 암호 ) | 사용자 인증 절차에 필요한 정보를 전송(사용자 이름) |
PASS + (POP3 서버의 로그인 암호) | 사용자 인증 절차에 필요한 정보를 전송(사용자 비밀번호) |
STAT | 메일 상태 정보(개수, 크기) 조회 |
RETR | 메일 가져오기 |
DELE | 해당 매일 삭제 |
QUIT | 연결 종료 |
IMAP
명령 | 의미 |
LOGIN | 사용자 인증 절차에 필요한 정보를 전송 |
SELECT | 메일 박스 선택 |
SEARCH | 메일 검색 |
FETCH | 메일 가져오기(POP3의 RETR과 유사하지만 훨씬 더 세부적인 작업이 가능) |
STORE | 메일 플래그 설정 |
COPY | 서버 내 메일을 다른 폴더로 복사 |
DELETE | 해당 메일 '삭제된' 표시로 변경(soft delete) |
LOGOUT | 연결 종료 |
IMAP는 POP와 달리 상태 기반의 명령어를 사용하며 더 복잡한 작업을 처리할 수 있도록 설계되었다.
응답 코드도 SMTP보다 더 단순하다.
POP3는 +OK(성공), -ERR(실패) 두 가지고 IMAP는 OK(성공), NO(실패, 논리적 이유가 있음), BAD(실패, 명령이 잘못되었음) 세 가지이다.
위에서 살펴본 POP3, IMAP 명령어와 응답은 연결 설정, 데이터 처리, 연결 종료 흐름으로 진행된다.
1. 연결 설정
POP3는 UA에서 MTA로 110, 995(SSL을 사용하는 경우) 포트로 TCP 연결 요청을 보낸다. 서버는 응답을 +OK로 보내면서 연결이 완료된다.
IMAP는 143, 993(SSL을 사용하는 경우) 포트로 TCP 연결 요청을 보낸다. 서버는 응답을 OK로 보내면서 연결이 완료된다.
2. 데이터 처리
연결이 완료된 후 POP3는 사용자 인증, 메일 상태 조회, 메일 수신, 메일 삭제 등의 흐름을 가지고, IMAP는 사용자 인증, 메일박스 선택, 메일 목록 조회, 메일 처리 등의 흐름을 가진다. IMAP는 서버 간의 실시간 동기화를 지원하여, 다른 장치에서 한 작업이 즉시 서버에 반영되어, 이를 통해 모든 장치에서 동일한 메일 상태를 유지할 수 있다.
3. 연결 종료
POP3는 QUIT, IMAP는 LOGOUT를 명령어를 입력하여 연결을 종료한다.
결론
이렇게 간단하게 SMTP, POP3와 IMAP까지 알아보고 어떻게 통신 흐름이 진행되는지 이해할 수 있었다.
더욱이 Spring boot JavaMailSender을 사용한 메일 전송 기능이 어떻게 동작하는지도 확실히 파악할 수 있었다.
일반적으로 규모가 있는 회사에서는 자체적인 이메일 주소를 가지고 있는 경우가 많다. 이런 회사들은 자체적인 MTA 서버를 구축하고 있는 경우일 것이고, 그룹웨어 관련 업체에 의뢰하여 기능을 사용할 수도 있을 것이다.
단순히 웹서버를 개발하는 것에만 관점을 가지고 있었지만, 이 외에도 많은 부분에서 통신 프로토콜을 활용한 다양한 기능을 구현할 수 있겠다는 생각이 들었다.
추후에 자체적으로 MTA 서버를 구축하여 메일 송신, 수신 기능을 Spring boot와 연계하여 개발을 진행해보고 싶다는 생각도 가지게 되었다.
잘못된 내용이 있거나 궁금한 내용은 댓글 달아주시면 감사합니다!
참고
쉽게 배우는 데이터 통신과 컴퓨터 네트워크 - 박기현
https://foxydog.tistory.com/56
https://goldsony.tistory.com/185
https://kkamagistory.tistory.com/229
https://blog.naver.com/neptune4781/90002644797
'통신 및 네트워크' 카테고리의 다른 글
HTTP 버전의 변화(HTTP/1.0, HTTP/1.1, HTTP/2.0, HTTPS, HTTP3.0) (0) | 2023.06.15 |
---|---|
TCP/IP 계층 간단 정리 및 TCP 연결/해제 과정(3-way handshake, 4-way handshake) (0) | 2023.06.10 |
네트워크 성능 분석 명령어 (0) | 2023.06.04 |
프로토콜 - HTTP 에 대한 간단 정리 (0) | 2022.05.07 |
네트워크 관련 기초 용어 (0) | 2022.05.07 |