실행 방법입니다. 외부 jar 는 필요 없습니다.
cd java-src\extension\19_mail
javac -encoding UTF-8 *.java
java -Dstdout.encoding=UTF-8 MainSmtpClient.send — EHLO~QUIT 대화와 실패 분류String rcptResponse = command(out, in, "RCPT TO:<" + to + ">", -1);
int code = Integer.parseInt(rcptResponse.substring(0, 3));
if (code >= 500) {
out.print("QUIT\r\n"); out.flush();
throw new PermanentFailureException(to + " 영구 실패: " + rcptResponse);
}
if (code >= 400) {
out.print("QUIT\r\n"); out.flush();
throw new TemporaryFailureException(to + " 일시 실패: " + rcptResponse);
}응답 코드 첫 자리만 보고 즉시 갈립니다. 500 이상은 PermanentFailureException, 400 이상은 TemporaryFailureException 으로 구분해 호출부가 재시도 여부를 스스로 정하게 합니다.
// RFC 2047: 한글 제목을 base64 로 감싼다
private static String encodeSubject(String subject) {
byte[] bytes = subject.getBytes(StandardCharsets.UTF_8);
return "=?UTF-8?B?" + Base64.getEncoder().encodeToString(bytes) + "?=";
}
// 같은 오류 키는 창(WINDOW) 안이면 건너뛴다
private static void notifyAlert(Map<String, Long> lastAlertAt,
String errorKey, String message) {
long now = System.currentTimeMillis();
Long last = lastAlertAt.get(errorKey);
if (last != null && now - last < ALERT_WINDOW_MS) {
System.out.println("억제(스킵): " + message);
return;
}
lastAlertAt.put(errorKey, now);
System.out.println("알림 발송: " + message);
}가짜 SMTP 서버 시작: 포트 52366
=== 데모 1: 첨부 포함 정상 발송 ===
발송 성공
서버 수신 헤더: Subject: =?UTF-8?B?OeyblCDsoJXsgrAg66as7Y+s7Yq4?=
=== 데모 2: 일시 실패(451) 재시도 후 포기 ===
1번째 시도 일시 실패: [email protected] 일시 실패: 451 ...
2번째 시도 일시 실패: ...
3번째 시도 일시 실패: ...
4번째 시도 일시 실패: ...
최대 재시도 초과, 포기하고 실패 처리
=== 데모 3: 영구 실패(550) 즉시 중단 ===
영구 실패로 즉시 중단: [email protected] 영구 실패: 550 ...
=== 데모 4: 멱등키로 중복 발송 방지 ===
1차 발송: 발송 완료, 키 등록(batch-2026-09:[email protected])
2차 발송(중복): 이미 보낸 키(batch-2026-09:[email protected]), 건너뜀
=== 데모 5: 알림 폭주 억제(같은 오류 5회 호출) ===
알림 발송: 주문 배치 타임아웃 발생 (1번째 감지) (메일 + 사내 메신저 웹훅)
억제(스킵): 주문 배치 타임아웃 발생 (2번째 감지)
억제(스킵): 주문 배치 타임아웃 발생 (3번째 감지)
억제(스킵): 주문 배치 타임아웃 발생 (4번째 감지)
억제(스킵): 주문 배치 타임아웃 발생 (5번째 감지)
가짜 SMTP 서버 종료첫 시도부터 4번 연속 451 을 받고 포기하는 이유는 초기 시도 1회 + 재시도 3회(BACKOFF_MS.length 만큼)이기 때문입니다. 알림은 5번 호출 중 1번만 실제로 나가고 나머지 4번은 5분 창 안이라 억제됩니다.