// 사용하여 이메일을 전송하는 Go 언어 예제를 제공합니다.//// 이 코드는 SMTP 프로토콜(포트 465, SSL/TLS 암호화)을 통해 Tencent Cloud SES// 에 이메일 전송 요청을 보내는 방법을 보여줍니다. 전체 흐름은 다음 단계로 구성됩니다:// 1. 전송 파라미터(발신자, 수신자, Cc, Bcc, 제목, 본문 등) 준비;// 2. RFC 822 형식으로 이메일 Header와 Body 구성 (Body는 Base64로 인코딩);// 3. tls.Dial을 사용하여 SMTP 서버에 암호화된 연결 설정;// 4. PlainAuth를 통해 인증 완료;// 5. MAIL FROM, RCPT TO, DATA 등의 SMTP 명령을 순차적으로 실행하여 메시지 전송.//// 이 코드를 사용하기 전에 Tencent Cloud Email Push 콘솔에서 다음 준비 작업을 완료하세요:// - 발신 도메인(Sender Domain) 생성 및 검증;// - 발신 주소(Sender Address) 생성 및 SMTP 전송 활성화;// - 콘솔의 "SMTP 비밀번호" 메뉴에서 전용 비밀번호 생성// (참고: Tencent Cloud 계정 비밀번호가 아닙니다);// - 발신 주소에 사용 가능한 전송 할당량이 있는지 확인.//// 의존성: 표준 라이브러리 net/smtp 및 crypto/tls만 사용하며, 외부 패키지가 필요하지 않습니다.//// 이 파일은 //go:build ignore 빌드 태그를 사용하여 동일한 디렉터리에 다른 데모 파일이// 있을 때 기본 빌드에서 제외되도록 합니다.// 이 단일 예제를 실행하려면: go run smtp-go-demo.ko.gopackage mainimport ("crypto/tls""encoding/base64""fmt""log""net""net/smtp")// Test465 는 포트 465 (SSL/TLS)를 통해 HTML 형식의 이메일을 전송하는 예제입니다.//// 작업 흐름:// 1. SMTP 서버 주소, 포트, 발신 계정 및 SMTP 비밀번호 설정;// 2. 이메일 헤더 (From / To / Cc / Bcc / Subject / Content-Type 등) 구성;// 3. 본문을 Base64로 인코딩하여 헤더 뒤에 추가// (헤더와 본문 사이에 빈 줄이 필요);// 4. smtp.PlainAuth를 사용하여 PLAIN 인증 객체 생성;// 5. SendMailWithTLS를 호출하여 TLS 연결을 통해 메시지 전송.//// 반환값: 성공 시 nil 반환, 실패 시 구체적인 오류 반환.func Test465() error {// SMTP 서버 주소. Tencent Cloud Email Push의 경우 smtp.qcloudmail.com 으로 고정.host := "smtp.qcloudmail.com"// SMTP 포트: 465는 SSL/TLS 암호화 연결을 의미합니다;// STARTTLS를 사용하려면 포트 25로 변경하고, 코드의 tls.Dial도// 그에 맞게 net.Dial + StartTLS로 변경해야 합니다.port := 465// email: 콘솔에서 생성 및 검증된 발신 주소(Sender Address);// SMTP 비밀번호를 소유한 계정과 일치해야 합니다.email := "abc@cd.com"// password: 콘솔의 "SMTP 설정" 페이지에서 이 발신 주소를 위해// 별도로 생성된 SMTP 비밀번호.// Tencent Cloud 로그인 비밀번호도 API 키도 아닙니다.password := "****"// 수신자 주소 (필수, 최소 한 개)toEmail := "test@test123.com"// 참조(Cc) 주소 (선택)ccEmail := "cc@test123.com"// 숨은참조(Bcc) 주소 (선택)bccEmail := "bcc@test123.com"// 이메일 헤더를 구성합니다. SMTP 프로토콜은 헤더 필드를 "Key: Value\\r\\n"// 형식으로 작성하도록 요구하며, 모든 헤더 뒤에는 빈 줄("\\r\\n")이 필요하고,// 그 뒤에 본문이 따라옵니다.header := make(map[string]string)// "From"은 "표시 이름 <발신 주소>" 형식으로 작성하는 것이 권장됩니다.// 표시 이름은 받은 편지함에서 발신자 별칭으로 표시됩니다.header["From"] = "test " + "<" + email + ">"header["To"] = toEmailheader["Cc"] = ccEmailheader["Bcc"] = bccEmail// 이메일 제목. 비 ASCII 문자가 포함된 경우 RFC 2047 인코딩// (=?UTF-8?B?...?=)이 권장됩니다. 이 예제는 순수 ASCII이므로 인코딩이 적용되지 않았습니다.header["Subject"] = "test subject"// Content-Type은 본문이 HTML임을 지정하고 문자셋을 UTF-8로 선언합니다.header["Content-Type"] = "text/html; charset=UTF-8"// Content-Transfer-Encoding은 본문의 전송 인코딩을 지정합니다.// base64를 사용하면 비 ASCII 또는 특수 문자가 SMTP 전송 중에// 잘리거나 손상되는 것을 방지할 수 있습니다.header["Content-Transfer-Encoding"] = "base64"// HTML 본문 샘플body := "<!DOCTYPE html>\\n<html>\\n<head>\\n<meta charset=\\"utf-8\\">\\n<title>hello world</title>\\n</head>\\n<body>\\n " +"<h1>나의 첫 번째 제목</h1>\\n <p>나의 첫 번째 단락입니다.</p>\\n</body>\\n</html>"// 일반 텍스트 이메일을 보내려면 위의 HTML 구성을 주석 처리하고// 다음 두 줄을 활성화하세요://header["Content-Type"] = "text/plain; charset=UTF-8"//body := "test body"// 최종 SMTP 메시지를 구성합니다: 먼저 모든 헤더를 "Key: Value\\r\\n"으로// 연결하고, 구분자로 빈 줄을 추가한 다음, 마지막으로 Base64로 인코딩된// 본문을 덧붙입니다.message := ""for k, v := range header {message += fmt.Sprintf("%s: %s\\r\\n", k, v)}message += "\\r\\n" + base64.StdEncoding.EncodeToString([]byte(body))// PLAIN 인증 객체를 생성합니다.// 1번째 매개변수 "identity"는 보통 비어 있고, 2번째는 사용자 이름// (발신 주소), 3번째는 SMTP 비밀번호, 4번째는 host (서버 측 검증에 사용)입니다.auth := smtp.PlainAuth("",email,password,host,)// TLS 버전의 전송 함수를 호출하여 서버 주소, 인증 객체, 발신자,// 수신자 목록 및 원시 메시지를 전달합니다.// 참고: "to" 목록은 SMTP의 RCPT TO 명령에 사용되며 실제로 어떤 메일함이// 이메일을 받을지 결정합니다. Cc/Bcc 수신자도 실제로 메시지를 받게 하려면// ccEmail / bccEmail도 이 목록에 추가해야 합니다.err := SendMailWithTLS(fmt.Sprintf("%s:%d", host, port),auth,email,[]string{toEmail},[]byte(message),)if err != nil {fmt.Println("Send email error:", err)} else {fmt.Println("Send mail success!")}return err}// Dial 은 SMTP 서버에 TLS 암호화 연결을 설정하고 사용 가능한// *smtp.Client 를 반환합니다.//// 매개변수:// - addr: "host:port" 형식의 서버 주소, 예: "smtp.qcloudmail.com:465".//// 참고:// - 표준 라이브러리의 smtp.Dial은 기본적으로 일반(plain) 연결을 사용합니다.// 포트 465의 경우, 먼저 TLS 채널을 설정한 다음 SMTP 클라이언트로 래핑해야 하므로,// 이 함수는 net.Dial 대신 tls.Dial을 사용합니다.// - 두 번째 인자는 *tls.Config이며, nil을 전달하면 기본 구성// (서버 인증서를 검증함)을 사용한다는 의미입니다.// 인증서 검증을 건너뛰려면(프로덕션 사용은 권장하지 않음),// &tls.Config{InsecureSkipVerify: true}를 전달하세요.func Dial(addr string) (*smtp.Client, error) {conn, err := tls.Dial("tcp", addr, nil)if err != nil {log.Println("tls.Dial Error:", err)return nil, err}// smtp.NewClient는 EHLO/HELO 핸드셰이크를 위해 host (포트 제외)가 필요합니다.host, _, _ := net.SplitHostPort(addr)return smtp.NewClient(conn, host)}// SendMailWithTLS 는 TLS 암호화된 SMTP 연결을 통해 완전한 이메일 전송을 수행합니다.//// 매개변수:// - addr: "host:port" 형식의 SMTP 서버 주소.// - auth: SMTP 인증 객체. smtp.PlainAuth로 생성 가능;// 인증이 필요하지 않으면 nil을 전달합니다.// - from: MAIL FROM 명령에서 사용되는 발신자 주소.// 인증된 계정과 일치해야 합니다.// - to: RCPT TO 명령에서 사용되는 수신자 목록.// To, Cc, Bcc의 모든 실제 수신자를 포함합니다.// - msg: 완전한 이메일 메시지 (Header + 빈 줄 + Body), RFC 822 형식이어야 합니다.//// 단계:// 1. Dial을 통해 TLS + SMTP 연결 설정;// 2. 서버가 AUTH 확장을 지원하는 경우 인증 수행;// 3. MAIL FROM, RCPT TO, DATA 명령을 순차적으로 실행;// 4. 이메일 본문을 작성하고 데이터 스트림을 닫음;// 5. QUIT 명령을 호출하여 세션을 정상적으로 종료.//// 어떤 단계라도 실패하면 즉시 해당 오류를 반환합니다.func SendMailWithTLS(addr string, auth smtp.Auth, from string,to []string, msg []byte) (err error) {// 1. TLS 연결을 설정하고 SMTP 클라이언트를 얻습니다.c, err := Dial(addr)if err != nil {log.Println("Create smtp client error:", err)return err}// 연결 누수를 피하기 위해 연결이 최종적으로 닫히도록 보장합니다.defer c.Close()// 2. auth가 제공되고 서버가 EHLO를 통해 AUTH 확장을 알리는 경우,// 인증을 수행합니다.if auth != nil {if ok, _ := c.Extension("AUTH"); ok {if err = c.Auth(auth); err != nil {log.Println("Error during AUTH", err)return err}}}// 3. MAIL FROM 명령을 보내 서버에게 발신자가 누구인지 알립니다.if err = c.Mail(from); err != nil {return err}// 4. 각 수신자에 대해 RCPT TO 명령을 보냅니다.for _, addr := range to {if err = c.Rcpt(addr); err != nil {return err}}// 5. DATA 명령을 보내고 이메일 콘텐츠 (Header + Body)를 작성할 writer를 얻습니다.w, err := c.Data()if err != nil {return err}_, err = w.Write(msg)if err != nil {return err}// writer를 닫아 본문 입력의 끝을 표시합니다// (SMTP 프로토콜에서 끝은 "\\r\\n.\\r\\n"으로 표시됩니다).err = w.Close()if err != nil {return err}// 6. QUIT 명령을 보내 서버와의 세션을 정상적으로 종료합니다.return c.Quit()}// main 은 진입점으로, Test465를 직접 호출하여 전송을 트리거합니다.// 실제 비즈니스 통합 시에는 전송 매개변수(계정, 비밀번호, 수신자 등)를// 구성 또는 함수 인자로 분리하고, Test465의 오류에 대해 더 세밀한// 처리 및 재시도를 수행하는 것이 좋습니다.func main() {Test465()}
피드백