Optional, ifPresentOrElse 로 간단한 리팩토링

2023. 3. 9. 19:10☕️ Java

ifPresentOrElse 를 이용한 (초)간단 리팩토링

 

프로세스

1. userEmail 로 회원이 존재하는지 확인

2. Optional 타입의 findUser 반환

3. Optional 값이 비어있으면 userEmail 을 가지는 회원이 없다는 뜻이므로, newUser 엔티티를 저장 (repository.save()) . 

4. Optional 값이 존재 시, findUser 의 토큰값 갱신

5. @Transactional 어노테이션으로 인해, 더티체킹 후 영속화 진행

 

기존.java

@Transactional
private void createUser(String userEmail, Token token) {

        Optional<User> findUser = userRepository.findUserByEmail(userEmail);

        if (findUser.isEmpty()) {
            User newUser = new User(userEmail);
            userRepository.save(newUser);
        } else {
            findUser.ifPresent(user -> {
                user.setAccessToken(token.getAccessToken());
                user.setRefreshToken(token.getRefreshToken());
            });
        }
    }

 

로직은 맞을지라도 if~else 구문이 마음에 들지 않고, 무엇보다 코드가 한눈에 들어오지 않는다.

Optional 의 ifPresentOrElse() 메서드를 이용해 개선해보자. 

else 구문에서도 별도의 로직이 존재하기 때문에 ifPresentOrElse() 메서드를 사용한다.

 

개선.java

@Transactional
private void createUser(String userEmail, Token token) {

    userRepository.findUserByEmailAndDelFalse(userEmail)
            .ifPresentOrElse(
                existUser -> updateTokens(token, existUser),
                () -> {
                    User newUser = new User(userEmail);
                    userRepository.save(newUser);
                });
}

private void updateTokens(Token token, User user) {
    user.setAccessToken(token.getAccessToken());
    user.setRefreshToken(token.getRefreshToken());
}

 

 

개선_진짜_최종.java

orElse 에 해당되는 인자도 메서드로 추출해주자. 위의 코드에 비해 명시적으로 else 구문에서 어떤 행위가 일어날지 파악할 수 있다.

@Transactional
private void createUser(String userEmail, Token token) {

    userRepository.findUserByEmailAndDelFalse(userEmail)
            .ifPresentOrElse(
                existUser -> updateTokens(token, existUser),
                () -> saveUser(String email)
        );
}

private void updateUser(Token token, User user) {
    user.setAccessToken(token.getAccessToken());
    user.setRefreshToken(token.getRefreshToken());
}

private void saveUser(String email) {
    User newUser = new User(userEmail);
    userRepository.save(newUser);
}

 

 

ifPresentOrElse 내부

Optional 클래스 내부에 선언된 ifPresentOrElse 메서드

 

Consumer : 첫 번째 인자

Consumer<T>는 T 타입의 객체를 인자로 받고 리턴 값은 없다. 예제 코드에서 T 타입 객체는 User 의 인스턴스, existUser 로 볼 수 있다.

 

userRepository.findUserByEmailAndDelFalse(userEmail) 의 값이 T 객체이며 해당 값이 null 이 아닐 때 함수형 인터페이스 Consumer 의 accept() 를 호출한다.

 

Runnable : 두 번째 인자

두번째 인자 Runnable은 인자를 받지 않고, 리턴값도 없는 함수형 인터페이스다. 위의 Consumer 는 이름대로 consume 할 객체 T 를 받지만, Runnable 은 어떤 인자도 받지 않는다. 입력도 출력도 없는 함수로 볼 수 있다.

 

아래처럼 사용할 수 있다.

Runnable runnable = () -> System.out.println("run anything!");
runnable.run();

Output:

run anything!

 

 

 

 

간단한 리팩토링 예제 코드만 쓰려고 했는데 어쩌다 보니 함수형 인터페이스 Runnable 과 Consumer 를 알아봤다. 함수형 인터페이스를 잘 사용하면 코드를 깔끔히 사용할 수 있겠다.

 

 


* ref

함수형 인터페이스 예시 코드 : https://codechacha.com/ko/java8-functional-interface/#3-runnable