2023. 3. 9. 19:10ㆍBackend/☕️ 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
'Backend > ☕️ Java' 카테고리의 다른 글
[Java] 안전하게 파일 삭제하기 (1) | 2023.12.22 |
---|---|
[Java] 런타임에 메서드를 전달한다는게 무슨 말일까? (0) | 2023.07.17 |
Map 타입의 forEach (feat. Biconsumer) (0) | 2023.01.31 |
InputStream 사용시 발생한 간단 장애 회고 (0) | 2023.01.01 |
[Java] long 에서 int "안전하게" 형변환 / 타입 캐스팅 (0) | 2022.12.17 |