메타코딩 SNS프로젝트

20. 회원정보 수정 - Optional사용하기

정현3 2022. 6. 19. 15:40

< 20. 회원정보 수정 - Optinal 사용하기 >

서버단에서의 '유효성 검사'는 끝이났다

이제 남은것은 DB단에서의 문제를 해결하는 것이다.

앞 시간에 설명했던 Optional<T>의 3가지 옵션중에 orElseThrow()를 사용해서 Exception을 강제로 발동시킬것이다.

UserService로 가서 get()을 지우고 orElseThrow()를 넣어주도록 하자

package com.cos.photogramstart.handler;

import java.util.Map;

public class CustomValidationApiException extends RuntimeException{


    //객체를 구분하기 위해 '시리얼넘버'를 넣어주는것
    //JVM을 위해 걸어주는것이다
    private static final long serialVersionUID = 1L;

    private Map<String, String> errorMap;

    public CustomValidationApiException(String message) { //String message만 입력해도 되는 메서드 새로 만들어줌
        super(message);

    }

    public CustomValidationApiException(String message, Map<String, String> errorMap) {
        super(message);
        this.errorMap = errorMap;
    }

    public Map<String, String> getErrorMap() {
        return errorMap;
    }
}

-> 따라서 fail에 if문을 사용하여 message만 뿌릴 경우와, errorMap까지 뿌릴 경우를 분리해주어야 한다.


@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;


    @Transactional
    public User 회원수정(int id, User user) {

        //1.영속화
        //User userEntity = userRepository.findById(id).get(); //1.무조건 찾았다 걱정마 get() 2. 못찾았어 Excetption 발동시킬게
                                                                                       // orElseThrow()
        User userEntity = userRepository.findById(id).orElseThrow(() ->  {
            return new CustomValidationApiException("찾을 수 없는 아이디 입니다."); //생성자 새로 추가
        });

        //2.영속화된 오브젝트를 수정 - DirtyChecking (업데이트 완료)

        String rawPassword = user.getPassword();
        String encPassword = bCryptPasswordEncoder.encode(rawPassword); //password 암호화를 해주어야 한다

        userEntity.setName(user.getName());
        userEntity.setPassword(user.getPassword());
        userEntity.setBio(user.getBio());
        userEntity.setWebsite(user.getWebsite());
        userEntity.setPhone(user.getPhone());
        userEntity.setGender(user.getGender());

        return userEntity; //DirtyChecking이 일어나서 업데이트가 완료경
    }
}
//영속화된 Object를 수정하면 자동으로 DB에 반영이 된다

-> orElseThrow()의 안에 Supplier 타입을 담고 <>의 자리에 IllgalArgumentException을 담아주고 GET을 걸어주면 된다.

IllegalArgumentException은 findById()의 내부에 id값이 잘못되었을때 발동되는 Exception이다.

그리고 이 코드를 '람다식'으로 바꾸어서 조금 더 간결하게 수정해주자.

 

DB에 존재하지 않는 userId에 대한 '수정요청'이 들어왔을때, 서버가 터지면서 

"java.lang.IllegalArgumentException : 찾을 수 없는 ID입니다" 의 오류가 출력된다.

이런 오류들도 '글로벌 예외처리'를 하는것이 좋다.

'글로벌 예외 처리'를 위해 내가 Custom한 Exception을 사용할 것인데, IllegalExeption대신 CustomValidationApiException을 걸어주면 에러가 뜬다.

그 이유는 바로 CustomValidationApiException에서 만든 '생성자'가 받는 매개변수가

String message, Map<String,Stirng> errorMap 두개 이기 때문이다.

우리가 응답할 에러는 "찾을 수 없는 ID입니다" 라는 String 하나이고, errorMap은 필요없다.

따라서 CustomValidationException에 StringMessage만 받는 '생성자'자를 하나 더 만들어준다.

package com.cos.photogramstart.handler;

import java.util.Map;

public class CustomValidationApiException extends RuntimeException{


    //객체를 구분하기 위해 '시리얼넘버'를 넣어주는것
    //JVM을 위해 걸어주는것이다
    private static final long serialVersionUID = 1L;

    private Map<String, String> errorMap;

    public CustomValidationApiException(String message) { //String message만 입력해도 되는 메서드 새로 만들어줌
        super(message);

    }

    public CustomValidationApiException(String message, Map<String, String> errorMap) {
        super(message);
        this.errorMap = errorMap;
    }

    public Map<String, String> getErrorMap() {
        return errorMap;
    }
}

-> UserService에 있던 에러가 사라진다.

 

이제 이 에러가 발생하여 CustomValidationApiException이 발생하게 되면, ControllerExceptionHandler가 에러를 낚아채서 ResponseEntity를 응답하게 되어있다.

당연하게도 HttpStatus 상태코드가 있기 때문에 AJAX는 fail코드가 실행되면서, StringMessage인 "찾을 수 없는 ID입니다"가 응답될 것이다.

// (1) 회원정보 수정
function update(userId, event) {

    event.preventDefault(); //폼태그 액션을 더이상 진행되지 않게 막기
    let data = $("#profileUpdate").serialize();

    console.log(data);

    $.ajax({
        type:"put",
        url :`/api/user/${userId}`,
        data : data,
        contentType:"application/x-www-form-urlencoded; charset=utf-8",
        dataType:"json"
    }).done(res => { //HttpStatus 상태코드 200번대 일때
        console.log("update 성공", res);
        location.href = `/user/${userId}`;
    }).fail(error => { //HttpStatus 상태코드 200번대가 아닐때, fail이 실행된다
        if (error.data == null) {
            alert(error.responseJSON.message); //message만 응답
        } else {
            alert("회원정보 수정에 실패하였습니다. 원인 : " + JSON.stringify(error.responseJSON.data)); //errorMap이 포함된 응답
        }
    });
}

-> 다시 테스트해보면 정상적으로 '에러메세지'가 응답된다. 

이로써 DB단에서의 문제점도 해결됨으로써 '회원가입, 로그인, 회원정보수정'의 기능이 모두 끝이났다.