개발일기 15편 [숨은 참조(BCC)를 활용한 벌크 이메일 전송]

문제점

초기 프로젝트 설계 단계에서 공지사항 이메일 발송 로직은 구독자 개개인에게 개별적으로 이메일을 보내는 방식이었습니다. 이는 To 필드에 각 사용자의 이메일을 설정하여 개인화된 메일을 보내는 구조였습니다.

 

그러나 이런 방식에는 큰 문제가 있었습니다

 

수백 명의 사용자를 대상으로 각각의 이메일을 보내는 작업은 SMTP 서버의 부하를 높이고, 네트워크 리소스를 낭비하는 결과를 초래했습니다.

 

이러한 문제를 인식하고 있었지만, 이메일을 "한꺼번에 보내면서도 각 사용자 정보가 노출되지 않는 방법"을 떠올리지 못해 개별 전송 방식을 유지하고 있었습니다.

 

전환점

 

이 문제를 해결하게 된 계기는 모빌리티42 면접에서의 대화였습니다. 프로젝트 관련 질문을 받던 중, 이메일 발송 로직에 대해 설명하며 위의 문제점들을 언급했는데, 당시 대표님께서 간단한 해결책으로 숨은 참조(BCC)를 추천해 주셨습니다.

숨은 참조(BCC)란?

  • BCC(Blind Carbon Copy)는 수신자 목록을 다른 사람에게 보이지 않게 숨길 수 있는 이메일 기능입니다.
  • 여러 사용자에게 이메일을 보낼 때 수신자들이 서로의 이메일 주소를 확인할 수 없도록 보호할 수 있습니다.
  • 동시에 하나의 SMTP 요청으로 여러 이메일을 전송할 수 있어 효율성도 크게 향상됩니다.

이 조언을 듣고, 이렇게 간단한 해결책이 있다는 사실에 다소 허탈함을 느꼈습니다. 이를 통해 다양한 도메인 지식의 중요성을 깨닫게 되었으며, 효율적인 문제 해결을 위해 폭넓은 이해가 필요하다는 점을 배웠습니다. 이후 곧바로 해당 해결책을 적용해 개선 작업을 시작했습니다.

 

개선된 로직

 

기존 로직에서는 각 사용자에게 개별적으로 메일을 발송하던 것을, 사용자를 100명씩 묶어 한 번에 발송하도록 변경했습니다. 변경된 로직은 다음과 같습니다.

 

fun notifyNoticeMembers(noticeInfo: String, members: List<Member>) {
    val chunkSize = 100 // 한 번에 발송 가능한 최대 수신자 수
    val memberChunks = members.chunked(chunkSize) // 100명씩 나누기

    memberChunks.map { chunk ->
        try {
            emailService.sendBulkEmail(chunk, noticeInfo)
        } catch (e: Exception) {
            log.error("[그룹 이메일 발송 실패] 그룹 크기: {} | 오류: {}", chunk.size, e.message)
        }
    }
}

 

@Throws(Exception::class)
fun sendBulkEmail(membersChunk: List<Member>, noticeInfo: String) {
    val message = noticeMailSender.createMimeMessage()
    val helper = MimeMessageHelper(message, "utf-8")

    helper.setFrom(noticeSenderEmail)
    helper.setSubject("[notice-crawler] 오늘의 공지사항 전송드립니다")
    helper.setText(noticeInfo, true)

    // BCC(숨은 참조)로 수신자 설정
    val recipientEmails = membersChunk.map { it.email.trim() }.toTypedArray()
    helper.setBcc(recipientEmails)

    // 이메일 발송
    noticeMailSender.send(message)
    log.info("[이메일 발송 성공] 그룹 수신자 수: {}", membersChunk.size)
}

 

변경 후 결과

변경 후, 이메일 발송 로직에서 몇 가지 중요한 개선 효과를 얻을 수 있었습니다

  1. 성능 개선
    • 사용자를 100명씩 묶어 한 번의 SMTP 요청으로 대량 이메일을 발송함으로써, 개별적으로 이메일을 보내던 기존 방식에 비해 훨씬 효율적으로 작업할 수 있었습니다. 이로 인해 발송 시간이 크게 단축되어, 이전 방식에 비해 약 5배 빠르게 이메일을 전송할 수 있었습니다.
  2. 개인정보 보호
    • 이메일 발송 시 모든 수신자를 숨은 참조(BCC)에 설정하여 각 사용자가 다른 사용자의 이메일 주소를 볼 수 없도록 했습니다. 이를 통해 개인정보 노출 위험을 완전히 제거했습니다.
  3. SMTP 제한 준수
    • Gmail SMTP 서버는 한 번에 보낼 수 있는 수신자의 최대 수를 100명으로 제한하고 있습니다. 이번 개선을 통해 수신자를 100명씩 묶어 전송하도록 로직을 변경하여, Gmail의 제한을 초과하지 않도록 설계했습니다. 이로써 이메일 발송 실패나 서버 거부와 같은 문제를 방지할 수 있었습니다.

결과적으로, 변경된 로직은 성능, 보안, 안정성을 모두 개선하는 데 성공했습니다.

 

배운 점

이번 개선 과정에서 배운 가장 중요한 점은 단순하면서도 강력한 솔루션을 찾는 것의 중요성이었습니다. 기존의 문제는 상당히 복잡하게 느껴졌지만, 대표님께서 조언해 주신 숨은 참조 방식으로 간단히 해결할 수 있었습니다.

또한, 이번 경험을 통해 프로젝트의 설계와 구현을 항상 최적화 가능성의 관점에서 다시 살펴보는 습관을 가지게 되었습니다. 이런 대화와 피드백을 통해 배우고 성장할 수 있었던 점에 매우 감사드립니다.