본문 바로가기
[Project] CafeHub

[CafeHub] 코드 분석 및 이슈 기록 - 회원 정보 수정 / 찜한 리뷰 리스트 / 찜한 카페 리스트

by heosj 2024. 1. 8.

✔️ 주요 코드 중심 분석 ✔️


회원 정보 수정 - 이름, 이메일, 전화번호

Front

const save = () => { // 저장버튼 클릭 시 input readOnly
    // 1
    const isUnchanged = Object.keys(updateUser).every((key) => 
    			updateUser[key] === member[key]);
    if (isUnchanged) {
      Toast('warning', '변경된 정보가 없습니다')
      setEditMode(false);
      return;
    }
    const allCheck = Object.values(saveCheck).every((value) => value === true);
    if (!allCheck) {
      Toast('error', '입력값을 확인해주세요')
      return;
    }

    axios.put(`${url}/member/modifyInfo`, updateUser, { // 2
        headers : {
            Authorization :accessToken,
            Refresh : getCookie("refreshToken")
        }
    })
    .then((res) => {
      dispatch({type:"member", payload: updateUser}); // 3
      setEditMode(false);
      Toast('success', '정상적으로 수정되었습니다')
      })
    .catch((error) => {
      console.log(error);
    })
};
  1.  변경된 정보가 없는 경우, 빈 값이 존재하는 경우 상황에 맞는 메시지를 보여주고  return 
  2. member/modifyInfo 엔드포인트로 수정할 회원 정보 데이터를 담아 PUT 요청 전달
  3. Redux에 회원 정보를 저장하고 있으므로 해당 정보도 함께 변경해줌
  4. 일반 로그인 회원과 달리 소셜 로그인 회원은 전화번호만 수정이 가능함

(좌) 일반 로그인 회원의 회원 정보 수정 페이지 (우) 소셜 로그인 회원의 회원 정보 수정 페이지

 

Back

@PutMapping("/member/modifyInfo")
public ResponseEntity<Object> userInfoModify(@RequestBody ModifyReqDto modifyReqDto) {
    Member member = mapper.modifyReqDtoToMember(modifyReqDto);
    try {
        member = memberService.modifyMember(member); // 1
        ModifyResDto modifyResDto = mapper.memberToModifyResDto(member);
        return new ResponseEntity<>(modifyResDto, HttpStatus.OK);
    } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}
public Member modifyMember(Member member) throws Exception {
    Member modifyMember = memberRepository.findById(member.getId()); // 2
    if(modifyMember == null) throw new Exception("존재하지 않는 회원입니다.");
    modifyMember.setUserInfo(member.getName(), member.getNickname(),
    			     member.getPhone(), member.getEmail()); // 3
    memberRepository.save(modifyMember);
    return modifyMember;
}
  1. 클라이언트에서 전달 받은 데이터를 통해 서비스 레이어로 해당 회원 정보 요청
  2. 데이터베이스에서 해당 회원의 아이디를 통해 Member 정보를 찾음
  3. Member 정보가 확인된 경우 변경할 회원 정보로 설정해준 후 저장

회원 정보 수정 - 비밀번호

Front

const originPw = (name, value) => {
    if (name === 'pw' && value.trim() !== '' && value.length > 7) {
      axios.post(`${url}/password`, {id: member.id, password: value}) // 1
      .then((res) => {
        if(res.data === true) { setPasswordMatch(true); } 
        else { setPasswordMatch(false); }
      })
      .catch((error) => { console.log(error); })
    }
}

const pwSubmit = () => {
    axios.put(`${url}/resetPw/${member.id}`, { password: password.newPw }) // 2
      .then((res) => {
        Toast('success', '비밀번호 재설정 완료되었습니다')
      })
      .catch((error) => {
        Toast('error', '비밀번호 재설정 실패했습니다')
      })
    setPassword({ pw: '', newPw: '', newPwCheck: '' }); // 3
    setPasswordMatch(false);
    }
}
  1. password 엔드포인트로 아이디와 비밀번호를 담아 POST 요청 전달, 응답 데이터가 true인 경우 비밀번호 재설정 가능
  2. resetPw/{id} 엔드포인트로 비밀번호를 담아 PUT 요청 전달
  3. 입력했던 필드를 공백으로 지정하여 초기화

Back

@PostMapping("/password") 
public ResponseEntity<Object> matchPassword(@RequestBody SearchPwDto searchPwDto) {
    String id = searchPwDto.getId();
    String phone = searchPwDto.getPassword();
    try {
        boolean result = memberService.matchPw(id, phone); // 1
        return new ResponseEntity<>(result, HttpStatus.OK);
    } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
    }
}
public boolean matchPw(String id, String password) throws Exception {
    Member member = memberRepository.findById(id);
    if(member == null) throw new Exception("존재하지 않는 회원입니다.");
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // 2
    return encoder.matches(password, member.getPassword());
}
  1. 클라이언트에서 전달 받은 데이터를 통해 서비스 레이어로 해당 회원의 비밀번호 일치 여부 확인
  2. 저장되어 있는 비밀번호와 입력 받은 비밀번호가 일치하는지 확인하기 위해 해싱하여 비교

찜한 리뷰 리스트 / 찜한 카페 리스트

두 기능의 로직은 거의 유사하므로 찜한 리뷰를 중심으로 분석

 

Front

useEffect(() => {
    axios.get(`${url}/member/wishReviewList/${memNo}?page=${currentPage-1}`, { // 1
        headers: {
          Authorization: accessToken,
          "Content-Type": "application/json",
        },
      })
      .then((res) => {
        setWishReviewList(res.data.data);
        setTotalPages(res.data.pageInfo.totalPages);
      })
      .catch((error) => {
        console.error(error);
      });
}, [memNo, currentPage]);
{showModal && ( // 2
<div className="modalBox">
    <ReviewDetail modalDetail wishReviewNo = {wishReviewNo}/> // 3
    <p className='closeBtn'><img src="/img/X.png" alt="x" onClick={closeModal}/></p>
</div>
)}
  1.  member/wishReviewList/{memNo} 엔드포인트로 GET 요청 전달
  2. 리스트 중 특정 리뷰/카페를 선택하면 modal로 해당 리뷰/카페에 대한 정보를 보여줌

✅ wishReviewNo 관련 이슈는 아래 링크의 리뷰 상세 부분에서 확인

 

[CafeHub] 코드 분석 및 이슈 기록 - 리뷰 상세 / 리뷰 찜 / 리뷰 추천

 

sjin-record.tistory.com

 

Back

@GetMapping("member/wishReviewList/{memNo}")
public ResponseEntity<Object> getWishReview(@PathVariable Integer memNo,
                                            @RequestParam(defaultValue = "0") int page,
                                            @RequestParam(defaultValue = "9") int size) {
    try {
        Pageable pageable = PageRequest.of(page, size);
        MultiResponseDto<WishReviewDto> wishReviewList = wishReviewService.getWishReviewList(memNo, pageable);
        return new ResponseEntity<>(wishReviewList, HttpStatus.OK);
    } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}
public MultiResponseDto<WishReviewDto> getWishReviewList(Integer memNo, Pageable pageable) throws Exception {
    Page<WishReviewDto> wishReviewPage = wishRepository.findWishReviewList(memNo, pageable);
    return new MultiResponseDto<>(wishReviewPage.getContent(), wishReviewPage);
}