✔️ 주요 코드 중심 분석 ✔️
아이디 찾기
Front
// 아이디 찾기 정보 입력 페이지
axios.post(`${url}/searchId`, { name: data.name, phone: data.phone }) // 1
.then((res) => {
Toast('success', '결과 페이지로 이동합니다')
setTimeout(() => {
navigate('/searchIdResult', { state: { result: res.data.id } }); // 2
}, 1500);
})
.catch((err) => {
Toast('error', err.response.data)
})
// 아이디 찾기 결과 페이지
const location = useLocation();
const searchId = location.state.result; // 3
- searchId 엔드포인트로 이름과 전화번호를 담아 POST 요청 전달
- 응답으로 받은 데이터를 useNavigate()로 페이지를 이동시키며 파라미터를 전달
- 결과 페이지에서 useLocation()으로 파라미터를 취득하여 결과 노출
Back
@PostMapping("/searchId")
public ResponseEntity<Object> idSearch(@RequestBody SearchIdDto searchIdDto) {
try {
String name = searchIdDto.getName();
String phone = searchIdDto.getPhone();
Member member = memberService.searchId(name, phone); // 1
SearchIdDto id = SearchIdDto.searchIdToDto(member); // 2
return new ResponseEntity<>(id, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
public Member searchId(String name, String phone) throws Exception {
Member member = memberRepository.findByNameAndPhone(name, phone); // 3
if(member == null) throw new Exception("회원정보가 일치하지 않습니다.");
return member;
}
- 클라이언트에서 전달 받은 데이터를 통해 서비스 레이어로 해당 회원 정보 요청
- Member 객체를 DTO로 변환하여 클라이언트에 반환
- 데이터베이스에서 이름과 전화번호를 이용하여 회원 정보를 검색
비밀번호 찾기/재설정
Front
axios.post(`${url}/searchPw`, { id: data.id, phone: data.phone }) // 1
.then((res) => {
if (res.data === true){ // 2
const random = Math.floor(Math.random() * 9000) + 1000;
setRandomCode(random);
console.log(random);
axios.get(`${url}/check/sendSMS?phone=${data.phone}&code=${random}`)
.then((res) => {
Toast('success', '인증번호가 발송되었습니다')
setPhoneAuth(true);
})
} else {
Toast('error', '일치하는 회원 정보가 없습니다')
}
})
.catch((error) => {
Toast('error', error.response.data)
});
const handleSubmit = (e) => {
e.preventDefault(); // 3
// 유효성 검사 생략
axios.put(`${url}/resetPw/${id}`, { password: data.password }) // 4
.then((res) => {
Toast('error', '완료되었습니다\n로그인 페이지로 이동합니다')
setTimeout(() => {
navigate('/login');
}, 1500);
})
.catch((error) => {
Toast('error', '실패했습니다\n다시 시도해주세요')
})
}
- searchPw 엔드포인트로 아이디와 전화번호를 담아 POST 요청 전달
- 응답 데이터가 true인 경우(회원정보가 일치하는 경우) 랜덤 코드를 생성하여 휴대폰 인증번호 발송을 위한 요청 전달
- 유효성 검사를 위해 e.preventDetault()를 이용하여 submit을 방지
- resetPw/{id} 엔드포인트로 아이디와 변경할 비밀번호를 담아 PUT 요청 전달하여 비밀번호 재설정 완료
Back
@PostMapping("/searchPw")
public ResponseEntity<Object> pwSearch(@RequestBody SearchPwDto searchPwDto) {
try {
String id = searchPwDto.getId();
String phone = searchPwDto.getPhone();
boolean result = memberService.searchPw(id, phone); // 1
return new ResponseEntity<>(result, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
@PutMapping("/resetPw/{id}")
public ResponseEntity<Object> changePassword(@PathVariable String id, @RequestBody SearchPwDto searchPwDto) {
try {
memberService.changePw(id, searchPwDto.getPassword()); // 1
return new ResponseEntity<>(true, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
public boolean searchPw(String id, String phone) throws Exception {
Member member = memberRepository.findById(id); // 2
if (member != null && member.getPhone().equals(phone)) {
return true;
} else {
return false;
}
}
public void changePw(String id, String newPassword) throws Exception {
Member member = memberRepository.findById(id); // 2
if(member != null) {
member.changePassword(bCryptPasswordEncoder.encode(newPassword)); // 3
memberRepository.save(member);
} else {
throw new Exception("회원정보가 일치하지 않습니다.");
}
}
- 클라이언트에서 전달 받은 데이터를 통해 서비스 레이어로 해당 회원 정보 요청
- id를 통해 회원 정보를 담은 객체 저장
- 회원정보가 있는 경우 BCryptPasswordEncoder를 사용하여 새로운 비밀번호를 해싱하고 데이터베이스에 저장
휴대폰 인증번호 발송
CoolSMS를 활용하여 휴대폰 인증번호를 발송
public class PhoneCodeServiceImpl implements PhoneCodeService {
@Value("${send-phone-key}")
private String apiKey;
@Value("${send-phone-secret-key}")
private String secretKey;
@Value("${soobin-phone-number}")
private String fromNumber;
private DefaultMessageService messageService;
public PhoneCodeServiceImpl() {}
@PostConstruct
public void init() {
this.messageService = NurigoApp.INSTANCE.initialize(apiKey, secretKey, "https://api.coolsms.co.kr");
}
@Override
public SingleMessageSentResponse sendPhoneCode(String phone, String code) throws Exception {
Message message = new Message();
message.setFrom(fromNumber); // 발신번호
message.setText("[CafeHub] 인증번호 : " + code + "\n타인 유출로 인한 피해 주의");
message.setTo(phone);
return this.messageService.sendOne(new SingleMessageSendingRequest(message));
}
}
- @Value 어노테이션을 통해 외부 프로퍼티 파일에서 값을 주입
- @PostConstruct 어노테이션을 통해 클래스가 초기화될 때 실행하도록 설정
발생 에러
org.springframework.beans.factory.
UnsatisfiedDependencyException
: Error creating bean with name 'phoneCodeController': Unsatisfied dependency expressed through field 'phoneCodeService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'phoneCodeServiceImpl' defined in file
발생 원인
PhoneCodeServiceImpl 객체를 초기화하는 중 @Value로 주입되는 값들이 아직 초기화되지 않은 상태에서 NurigoApp.INSTANCE.initialize() 메서드가 호출되어 발생한 문제
Spring은 @Value로 주입되는 값을 객체 생성 과정에서 초기화하는데, 생성자 초기화를 시도할 때 해당 값들이 아직 주입되지 않아 값을 참조하지 못하는 경우
해결 방법
@PostConstruct 어노테이션을 사용하여 객체가 생성된 이후에 초기화 되도록 설정하여 객체 생성 과정에서 @Value로 주입된 프로퍼티 값을 사용할 수 있도록 함
따라서, 해당 값들이 모두 주입된 이후 초기화 메소드가 실행되어 문제 해결
휴대폰 인증번호 발송 결과

'[Project] CafeHub' 카테고리의 다른 글
| [CafeHub] 코드 분석 및 이슈 기록 - 카페 정보 (with. 카카오 지도) (0) | 2024.01.08 |
|---|---|
| [CafeHub] 코드 분석 및 이슈 기록 - 리뷰 상세 / 리뷰 찜 / 리뷰 추천 (0) | 2024.01.05 |
| [CafeHub] 기획 - 담당 역할에 따른 화면 정의서 (0) | 2023.12.23 |
| [CafeHub] 기획 - 담당 역할에 따른 사용자 요구 사항 정의서 (0) | 2023.12.23 |
| [CafeHub] 기획 - 주제 선정 및 기능 회의 (0) | 2023.12.22 |