diff --git a/src/main/java/com/project/syncly/domain/auth/email/EmailSenderImpl.java b/src/main/java/com/project/syncly/domain/auth/email/EmailSenderImpl.java index 55eb49a..c501609 100644 --- a/src/main/java/com/project/syncly/domain/auth/email/EmailSenderImpl.java +++ b/src/main/java/com/project/syncly/domain/auth/email/EmailSenderImpl.java @@ -1,8 +1,11 @@ package com.project.syncly.domain.auth.email; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; -import org.springframework.mail.SimpleMailMessage; +import org.springframework.core.io.ClassPathResource; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; @Service @@ -13,10 +16,98 @@ public class EmailSenderImpl implements EmailSender { @Override public void send(String to, String subject, String content) { - SimpleMailMessage message = new SimpleMailMessage(); - message.setTo(to); - message.setSubject(subject); - message.setText(content); - javaMailSender.send(message); + try { + MimeMessage message = javaMailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setTo(to); + helper.setSubject(subject); + + String body = createEmailTemplate(content); + helper.setText(body, true); + + // 돌고래 이미지 첨부 + ClassPathResource dolphinImage = new ClassPathResource("static/syncly_dolphin.png"); + helper.addInline("dolphinLogo", dolphinImage); + + javaMailSender.send(message); + } catch (MessagingException e) { + throw new RuntimeException("이메일 전송에 실패했습니다.", e); + } + } + + private String createEmailTemplate(String verificationCode) { + StringBuilder body = new StringBuilder(); + body.append(""); + body.append(""); + body.append(""); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(""); + body.append(""); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append("
"); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append(" "); + body.append("
"); + body.append("
"); + body.append(" \"Syncly"); + body.append("
"); + body.append("

이메일 인증

"); + body.append("

회원가입을 위한 인증코드입니다

"); + body.append("
"); + body.append("

안녕하세요,

"); + body.append("

"); + body.append(" Syncly 회원가입을 환영합니다.
"); + body.append(" 아래 인증코드를 입력하여 이메일 인증을 완료해 주세요."); + body.append("

"); + body.append(" "); + body.append("
"); + body.append("
"); + body.append("

").append(verificationCode).append("

"); + body.append("
"); + body.append("
"); + body.append(" "); + body.append("
"); + body.append("

보안 안내

"); + body.append("

"); + body.append(" • 이 인증코드는 10분간 유효합니다
"); + body.append(" • 타인에게 코드를 공유하지 마세요
"); + body.append(" • 본인이 요청하지 않은 경우 이메일을 무시하세요"); + body.append("

"); + body.append("
"); + body.append("

"); + body.append(" 만약 회원가입을 요청하지 않으셨다면, 이 이메일을 무시하셔도 됩니다."); + body.append("

"); + body.append("
"); + body.append("

"); + body.append(" © 2025 Syncly. All rights reserved.
"); + body.append(" 함께 만들어가는 협업 워크스페이스"); + body.append("

"); + body.append("
"); + body.append("
"); + body.append(""); + body.append(""); + + return body.toString(); } } diff --git a/src/main/java/com/project/syncly/domain/workspace/service/InvitationMailServiceImpl.java b/src/main/java/com/project/syncly/domain/workspace/service/InvitationMailServiceImpl.java index 86f2680..7f4ffa3 100644 --- a/src/main/java/com/project/syncly/domain/workspace/service/InvitationMailServiceImpl.java +++ b/src/main/java/com/project/syncly/domain/workspace/service/InvitationMailServiceImpl.java @@ -7,8 +7,10 @@ import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import java.util.Random; @@ -63,17 +65,87 @@ private String generateRandomToken() { public MimeMessage createMail(String mail, String number) { try { MimeMessage message = javaMailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); - message.setFrom(senderEmail); - message.setRecipients(MimeMessage.RecipientType.TO, mail); - message.setSubject("이메일 인증"); + helper.setFrom(senderEmail); + helper.setTo(mail); + helper.setSubject("이메일 인증"); String body = ""; - body += "

워크스페이스 초대 수락 링크입니다.

"; - body += "

" + number + "

"; - body += "

감사합니다.

"; - - message.setText(body, "UTF-8", "html"); + body += ""; + body += ""; + body += ""; + body += " "; + body += " "; + body += " "; + body += ""; + body += ""; + body += " "; + body += " "; + body += " "; + body += " "; + body += "
"; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += " "; + body += "
"; + body += "
"; + body += " \"Syncly"; + body += "
"; + body += "

워크스페이스 초대

"; + body += "

Syncly 워크스페이스에 초대되었습니다

"; + body += "
"; + body += "

안녕하세요,

"; + body += "

"; + body += " 새로운 워크스페이스에서 함께 협업하실 수 있도록 초대장을 보내드립니다.
"; + body += " 아래 버튼을 클릭하여 초대를 수락해 주세요."; + body += "

"; + body += " "; + body += " "; + body += " "; + body += "
"; + body += "

버튼이 작동하지 않나요?

"; + body += "

"; + body += " 아래 링크를 복사하여 브라우저에 붙여넣으세요:
"; + body += " " + number + ""; + body += "

"; + body += "
"; + body += "

"; + body += " 이 초대장은 7일간 유효합니다. 만약 이 이메일을 예상하지 못했다면 무시하셔도 됩니다."; + body += "

"; + body += "
"; + body += "

"; + body += " © 2025 Syncly. All rights reserved.
"; + body += " 함께 만들어가는 협업 워크스페이스"; + body += "

"; + body += "
"; + body += "
"; + body += ""; + body += ""; + + helper.setText(body, true); + + // 이미지 첨부 (CID 방식) + ClassPathResource dolphinImage = new ClassPathResource("static/syncly_dolphin.png"); + helper.addInline("dolphinLogo", dolphinImage); return message; diff --git a/src/main/resources/static/syncly_dolphin.png b/src/main/resources/static/syncly_dolphin.png new file mode 100644 index 0000000..9e3614b Binary files /dev/null and b/src/main/resources/static/syncly_dolphin.png differ