업무/공부

[SpringBoot] 메일 전송 구현 - Contact 페이지 만들기 (CORS 에러, 구글 2단계 인증 앱 비밀번호 사용 시 주의사항)

2024. 1. 7. 00:34

1. 구글 계정 설정

1) 2단계 인증 설정

2) 앱 비밀번호 생성

 

3) 보안 수준이 낮은 앱의 액세스 허용

이제 이 설정 필요 없음.

 

 

 

2. 구글 메일 설정

설정 > 전달 및 POP/IMAP

1) 모든 메일에 대해 POP 사용

2) IMAP 사용

 

설정 > 계정 및 가져오기 > 비밀번호 변경 → 앱 비밀번호로 교체

비밀번호 변경하면 앱 비밀번호 없어진다!!!!!!!!!!!!

웹에서 로그인할 때는 그대로 기존의 비밀번호로 로그인 하면 되고,

서버에서 Gmail SMTP 이용해서 메일 전송할 때 비밀번호는 앱 비밀번호를 사용한다!!!!!!!!!!!!!!!!!!!!!!!!!!!

 

→ 즉, 단계는 간단하게

1) 앱 비밀번호 생성

2) application.yml spring.mail.password: 앱 비밀번호 입력

 

 

 

3. springboot starter mail 라이브러리 다운

메이븐을 사용하므로 pom.xml에 springboot starter mail dependency 추가, maven 새로고침

 

 

 

4. spring.mail 설정

1) application.yml

spring:
  mail:
    host: smtp.gmail.com
    port: 587 # Gmail SMTP 서버
    username: ${mail.username}
    password: ${mail.password} # 앱 비밀번호
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true
          connectiontimeout: 5000
          timeout: 5000
          writetimeout: 5000
      auth-code-expiration-millis: 1800000  # 30 * 60 * 1000 == 30분

username, password 값은 변수로 지정

 

2) intellij 변수 지정

edit configuration

 

 

 

5. 구현체 작성

1) 전체 코드

스프링의 JavaMailSender, SimpleMailMessage 사용하였다.

// EmailService.java

@Service
@AllArgsConstructor
public class EmailService {
    private JavaMailSender javaMailSender;

    public boolean sendSimpleMail(ContactDto contactDto) throws Exception {
        boolean result = false;
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();

        // 메일 내용
        String text = "E-mail: \n " + contactDto.getSenderMail()
            + "\n\nName: \n " + contactDto.getSenderName()
            + "\n\nMessage: \n " + contactDto.getMessage();

        simpleMailMessage.setTo(메일); // 받는 사람 메일 주소 세팅
        simpleMailMessage.setSubject(contactDto.getSubject()); // 메일 제목 세팅
        simpleMailMessage.setText(text); // 메일 내용 세팅

        try {
            javaMailSender.send(simpleMailMessage);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
// ContactController.java

@RestController
@RequestMapping("/contact")
@CrossOrigin(originPatterns = 프론트 도메인)
public class ContactController {
    private final EmailService emailService;

    public ContactController(EmailService emailService) {
        this.emailService = emailService;
    }

    @PostMapping("/send")
    public String sendMail(@Valid @RequestBody ContactDto contactDto) {
        try {
            if (emailService.sendSimpleMail(contactDto)) {
                return "전송 성공";
            } else {
                return "전송 실패";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "오류 발생";
        }
    }
}
// ContactDto 코드는 생략

 

 

 

2) CORS 문제

API에 @CrossOrigin 어노테이션으로 특정 도메인 허용하여 CORS 해결한다.

 

 

 

6. 완성!

 


에러 상황 정리

더보기

[에러1]

여전히 cors 에러 발생..

서버에서 다음 로그 남음.

o.s.web.cors.DefaultCorsProcessor : Reject: '클라이언트 도메인' origin is not allowed

 

[해결]

버전 문제인가?

SpringBoot 2.7.16 사용중

@CrossOrigin(origin = " 클라이언트 도메인 ") → @CrossOrigin(originPatterns = " 클라이언트 도메인 ")

 


[에러2]

cors 해결했더니 이제는 로그인이 안 되는 듯 함..

authentication failed; nested exception is javax.mail.authenticationfailedexception: failed to connect, no password specified

 

[해결]

application.yml 파일에 변수명에 달러 기호가 빠져 있었다....

${mail.username} 이라고 해야 하는데 {mail.username} 이라고 썼던 것....

 


[에러3]

이번엔 보안 수준이 낮은 앱에서 접근했다고 에러가 났음..

org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 534-5.7.9 Application-specific password required

 

[해결]

앱 비밀번호 확인하러 가보니까 안 보임...

알고보니까 비밀번호를 변경하면 앱 비밀번호가 사라지는 거였다..

즉, 앱 비밀번호 생성한 후에 비밀번호를 앱 비밀번호로 바꿔줄 필요가 없었다.

단계는 간단하게

1) 앱 비밀번호 생성

2) application.yml spring.mail.password: 앱 비밀번호

끝 !!!!!!!

 

앱 비밀번호 생성 후

비밀번호를 앱 비밀번호로 바꿀 필요 없다!!!!!!!!

 

웹에서 로그인할 때는 그대로 원래 비밀번호 입력하면 된다!!!!!!!!!