프로젝트/게시판 프로젝트

Optional을 올바르게 활용해보기

Junuuu 2022. 5. 24. 09:31
반응형

Optional이란?

간단하게 말하자면 값이 null일 수도 아닐 수도 있는 객체입니다.

즉, null이 될 수도 있는 객체를 포장해주는 Wrapper Class입니다.

 

여기까지만 읽었을 때 이해가 되지 않는다면 다음 글을 읽고 오면 좋습니다.

https://junuuu.tistory.com/90

 

[Java] Optional이란?

Optional이란? Java 8에서 최초로 도입되었습니다. 'T' 타입의 객체를 포장해 주는 래퍼 클래스로 모든 타입의 참조 변수를 저장할 수 있습니다. Optional은 "값이 존재할 수도 있지만 안 할 수도 있는

junuuu.tistory.com

 

기존의 코드

@Override
    public boolean login(MemberLoginRequestDTO memberLoginRequestDTO) {
        Optional<Member> userId = memberRepository.findByUserId(memberLoginRequestDTO.getUserId());
        //Optional 사용했으니 isPresent 대신 userId.orElseThrow()를 쓰는게 좋아보인다
        
        if(!userId.isPresent()){
            return false;
        }
        return userId.get().getPassword().equals(memberLoginRequestDTO.getPassword());
    }

기존의 코드는 Optional을 사용하였지만 isPresent를 사용했습니다.

이렇게 사용하면 userId != null으로 사용하는 것과 크게 다를 것이 없습니다.

 

 

리팩터링 한 코드

 static boolean result;

    @Override
    public boolean login(MemberLoginRequestDTO memberLoginRequestDTO) {
        Optional<Member> userId = memberRepository.findByUserId(memberLoginRequestDTO.getUserId());
        userId.ifPresent((member) -> {
            result = member.getPassword().equals(memberLoginRequestDTO.getPassword());
        });

        return result;
    }

ifPresent 메서드를 활용하여 userId가 존재한다면 패스워드 검증을 통해 true, false를 result에 기록하도록 하였습니다.

또한 내부의 result값을 전달하기 위해서 전역 변수인 result를 선언하였습니다.

 

하지만 동시성을 고려해 보았을 때 많은 문제가 있습니다.

 

1번 공유되는 result 변수에 만약 2개의 사용자가 동시에 들어와서 다른 사용자의 true 값을 들고 로그인해버리는 경우가 발생할 수 있습니다.

 

1번 사용자는 아이디와 패스워드가 일치하여 result = true로 변환시키고 2번 사용자는 존재하지 않는 아이디를 입력하여서 바로 return result가 되는데 이때 원래라면 false가 반환되어야 하는데 true가 반환될 수 있습니다.

 

또한 userId 대신에 userId를 통해 member를 조회하여 Member 객체를 가져오기 때문에 user라는 변수명을 사용하는 것이 바람직해 보입니다.

 

다시 리팩토링한 코드

 @Override
    public boolean login(MemberLoginRequestDTO memberLoginRequestDTO) {
        Optional<Member> user = memberRepository.findByUserId(memberLoginRequestDTO.getUserId());

        String userPassword = user.orElseThrow(() -> new IllegalArgumentException())
                                  .getPassword();

        return BCrypt.checkpw(memberLoginRequestDTO.getPassword(), userPassword);
    }

orElseThrow 메서드를 사용하여 만약 user가 Null이라면 IllegalArgumentException을 발생시키고 그렇지 않으면 비밀번호를 얻어와서 Bcrypt.checkpw를 통해 암호화된 비밀번호와 입력받은 비밀번호를 검사합니다

 

Optional을 잘 이용하고 가독성이 높은 코드가 되었습니다.

 

 

결론

최근 꾸준히 람다식을 공부하고 있는데 프로젝트에 잘 적용해보고 싶습니다.