gmail 설정하기
서버 관련(Springboot)
클라이언트 관련(Next)
gmail 설정하기
gmail 가입 -> google 계정 -> 보안 -> 2단계 인증 설정 후 상단 계정 검색에서 '앱 비밀번호' 검색
앱 비밀번호를 복사해 둔다.
Server
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-mail'
}
# application.properties
# mail
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username= 메일 주소 (메일@gmail.com)
spring.mail.password= 앱 비밀번호 (띄어쓰기 지우기)
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.starttls.enable=true
emailService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import java.security.SecureRandom;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class EmailService {
private final JavaMailSender mailSender;
private ConcurrentHashMap<String, String> codeStorage = new ConcurrentHashMap<>();
@Autowired
public EmailService(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
// 메일 보내기
public void sendSimpleMessage(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("상대에게 표시할 메일 주소");
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}
// 무작위 번호 생성
public static String generateRandomNumber(int length) {
SecureRandom random = new SecureRandom();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(random.nextInt(10)); // 0~9 사이 숫자
}
return builder.toString();
}
// 코드 저장 (email, code)
public void saveCode(String identifier, String code) {
codeStorage.put(identifier, code);
}
// 코드 검증 client(email, code)
public boolean verifyCode(String identifier, String code) {
String storedCode = codeStorage.get(identifier);
return storedCode != null && storedCode.equals(code);
}
// 코드 삭제 (검증 후 email로 map 삭제)
public void deleteCode(String identifier) {
codeStorage.remove(identifier);
}
}
Restcontroller.java
// 비밀번호 재설정
// 메일로 -> 임시 코드 보내서 -> 수정
@GetMapping("/sendEmail")
public ResponseEntity<Void> sendEmail(@RequestParam String email) {
String cord = emailService.generateRandomNumber(4);
emailService.saveCode(email, cord);
String to = email;
String subject = "비밀번호 재설정 안내";
String text = "비밀번호 재설정을 위한 인증번호 입니다. \n 인증번호: " + cord + "\n 인증 완료 후 인증번호는 임시 비밀번호로 설정됩니다.";
emailService.sendSimpleMessage(to, subject, text);
return null;
}
// 메일 전송 번호 확인
@GetMapping("/checkPassCord")
public ResponseEntity<Boolean> checkPassCord(@RequestParam String email,@RequestParam String passCord) {
boolean check = emailService.verifyCode(email, passCord);
if(check) {
userService.changePassword(email, passCord);
}
return ResponseEntity.ok(check);
}
client
// page.tsx
'use client';
import Link from 'next/link';
import React, { useState, useRef, useEffect } from 'react';
import { findEmail, checkPassCord, sendEmail } from '@/app/api/apiService';
export default function Page() {
// email 값 설정
const [sendedEmail, setSendedEmail] = useState('');
// 인증번호 input 나타내기
const [passCordOn, setPassCordOn] = useState(false);
// 비밀번호 찾기
const handleFindPassword = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const email = (form.elements.namedItem('email') as HTMLInputElement).value;
if (email === '') {
alert('이메일을 입력해주세요.');
return;
}
if (email.includes('@') === false) {
alert('이메일 형식이 올바르지 않습니다.');
return;
}
setSendedEmail(email);
sendEmail(email);
setPassCordOn(true);
};
// 인증번호 확인
const handlePassCordSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const email = (form.elements.namedItem('email') as HTMLInputElement).value;
const passCord = (form.elements.namedItem('passCord') as HTMLInputElement)
.value;
checkPassCord(passCord, email)
.then((response) => {
if (response.data === true) {
alert(
'인증번호가 일치합니다. \n 인증번호는 임시 비밀번호로 설정됩니다.',
);
setPassCordOn(false);
} else {
alert('인증번호가 일치하지 않습니다.');
}
})
.catch(() => {
alert('인증번호가 일치하지 않습니다.');
});
};
return (
<div>
<h2>
비밀번호 찾기
</h2>
<form onSubmit={handleFindPassword}>
<label >이메일</label>
<input
type="email"
placeholder="이메일"
name="email"
/>
<button
type="submit">
비밀번호 찾기
</button>
</form>
{passCordOn && (
<div>
<form
onSubmit={handlePassCordSubmit}>
<input type="hidden" name="email" value={sendedEmail} />
<label className="text-black">
인증번호 (인증완료 후 인증번호는 임시 비밀번호로 설정됩니다.)
</label>
<input
type="text"
placeholder="메일(아이디)로 전송된 인증번호를 입력해주세요."
name="passCord"
/>
<button
type="submit">
인증번호 확인
</button>
</form>
</div>
)}
</div>
);
}
axios를 이용해 서버 통신
// aixosApi.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000, // 요청 제한 시간
headers: { 'Content-Type': 'application/json' },
});
// API 요청 함수
export const sendEmail = async (email: string) => {
return apiClient.get(`/user/sendEmail?email=${email}`);
};
export const checkPassCord = async (passCord: string, email: string) => {
return apiClient.get(
`/user/checkPassCord?passCord=${passCord}&email=${email}`,
);
};'Java > Spring Boot' 카테고리의 다른 글
| JWT & CRSF (0) | 2025.03.25 |
|---|---|
| [restartedMain] i.n.r.d.DnsServerAddressStreamProviders : Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider, fallback to system defaults. This may result in incorrect DNS resolutions on MacOS. (3) | 2025.01.30 |
| [SpringBoot] Thymeleaf (1) | 2024.12.25 |
| [SpringBoot] Spring Test (1) | 2024.12.25 |
| [SpringBoot] H2database, JPA 설정과 사용법 (4) | 2024.12.22 |