본문 바로가기
내가 만난 error/해결

[MING-MARKET] 로그인 : 없는 아이디 입력시 오류 처리(Optional 클래스 사용) - NoSuchElementException

by 피자보다 치킨 2022. 6. 27.

현재 문제점

 

1. 로그인시 잘못된 아이디(없는 아이디)를 입력하게 되면

 2. NoSuchElementException 예외가 터져버린다.

에러메시지

java.util.NoSuchElementException: No value present at java.base/java.util.Optional.get(Optional.java:148) ~[na:na] at project.toyproject.service.LoginService.login(LoginService.java:31) ~[classes/:na] at 

 

코드보기 (JPA 사용)

 

MemberRepository

/**
 *로그인시 회원 조회
*TODO
*코드 리팩토링 예정(람다함수, stream사용해보기)
 */
public Optional<Member> findByloginId(String userId) {
    List<Member> members = em.createQuery("select m from Member m", Member.class)
            .getResultList();
    for (Member m : members) {
        if (m.getUserId().equals(userId)) { //값이 있을 경우
            return Optional.of(m);
        }
    }
    return Optional.empty(); //값이 없으면 null
}

 

LoginService

/**
 *로그인
*/
public Member login(String userId, String password) {
    Optional<Member> findMemberOptional = memberRepository.findByloginId(userId);

    //아이디 조회해서 해당 아이디 정보가 있을 경우( 없으면 null 반환받음)
    Member member = findMemberOptional.get();
    if (member.getPass().equals(password)) { //비밀번호가 (일치) 있을 경우
        return member;
    } else {
        return null; //비밀번호가 일치하지 않을 경우 null 반환
    }
}

LoginController

@PostMapping("/login")
public String login(@Valid @ModelAttribute("form") LoginDto form,
                    BindingResult result,
                    @RequestParam(defaultValue = "/") String redirectURL,
                    HttpServletRequest request) {
    if (result.hasErrors()) {
        return "/members/login";
    }

    Member loginMember = loginService.login(form.getUserId(), form.getPassword());


    //로그인 실패시 (null)
    if (loginMember == null) {
        result.reject("loginFail", "아이디 또는 비밀번호가 일치하지 않습니다");
        return "/members/login";
    }

    //로그인 성공처리
    Address address = loginMember.getAddress();
    MemberDto.SessionMemberData memberData = new MemberDto.SessionMemberData(
            loginMember.getId(), loginMember.getUserId(), loginMember.getNickname(), loginMember.getUsername());

    //기존 세션이 있으면 세션을 반환, 없으면 새로운 세션을 생성
    HttpSession session = request.getSession();
    //세션에 로그인 회원 정보를 보관 (쿠키에 key: JSESSIONID , value: UUID 로 들어감)
    session.setAttribute(LOGIN_MEMBER, memberData);

    return "redirect:" + redirectURL;
}

 

 

 

MemberService 코드를 다시 보자

/**
 *로그인
*/
public Member login(String userId, String password) {
    Optional<Member> findMemberOptional = memberRepository.findByloginId(userId);

    //아이디 조회해서 해당 아이디 정보가 있을 경우( 없으면 null 반환받음)
    Member member = findMemberOptional.get();
    if (member.getPass().equals(password)) { //비밀번호가 (일치) 있을 경우
        return member;
    } else {
        return null; //비밀번호가 일치하지 않을 경우 null 반환
    }
}

우선 findMemberOptional.get()으로 Optional 객체에 저장된 값에 접근한다.

여기서 저장된 값이 있다면 if문으로 넘어갈 것이다.

하지만 Optional 객체에 저장된 값이 empty(비어있는 값)이면 NoSuchElementException 예외가 발생한다.

해당 아이디가 없을 경우 결국 if문이 실행되기 전에 예외가 터져버리는 것이다.

따라서 get()메소드를 호출하기 전에 Optional 객체에 저장된 값이 empty인지 아닌지를 먼저 확인한 후 호출해야한다.

 

 

문제해결

 

메소드 설명
static <T> Optional<T> empty() 아무런 값도 가지지 않는 비어있는 Optional 객체를 반환함.
T get() Optional 객체에 저장된 값을 반환함.
boolean isPresent() 저장된 값이 존재하면 true를 반환하고, 값이 존재하지 않으면 false를 반환함.
static <T> Optional<T> of(T value) null이 아닌 명시된 값을 가지는 Optional 객체를 반환함.
static <T> Optional<T> ofNullable(T value) 명시된 값이 null이 아니면 명시된 값을 가지는 Optional 객체를 반환하며, 명시된 값이 null이면 비어있는 Optional 객체를 반환함.
T orElse(T other) 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함.
T orElseGet(Supplier<? extends T> other) 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 람다 표현식의 결괏값을 반환함.
<X extends Throwable> T
orElseThrow(Supplier<? extends X>  exceptionSupplier)
저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 예외를 발생시킴.

출처 - 코딩의 시작, TCP School

 

get() 메소드를 호출하기 전에

isPresent()를 사용하여 객체에 저장된 값이 empty(비어있는 값)인지 아닌지를 확인할 것이다.

 

 

LoginService

public Member login(String userId, String password) {
    Optional<Member> findMemberOptional = memberRepository.findByloginId(userId);

    //아이디 조회해서 해당 아이디 정보가 있을 경우( 없으면 empty 반환받음)
    if (!findMemberOptional.isPresent()) {
        return null;
    }

    Member member = findMemberOptional.get();

    if (member.getPass().equals(password)) { //비밀번호가 (일치) 있을 경우
        return member;
    } else {
        return null; //비밀번호가 일치하지 않을 경우 null 반환
    }
}

boolean isPresent(): 입력받은 아이디 정보가 존재하면 true, 존재하지 않을 경우 false 반환 

 

if문으로 아이디 정보가 없을 경우 null을 리턴한다.

다시 로그인을 시도해본다.

아주 잘 작동이 된다!!:)

 

 

 

Optional<T> 클래스
 Integer, Double 클래스처럼 'T' 타입의 객체를 포장해주는 래퍼 클래스
 모든 타입의 참조 변수를 저장할 수 있다.
 이러한 Optional 객체를 사용하면 복잡한 조건문 없이 null 값으로 인해 발생하는 예외를 처리할 수 있다.
 다양한 예제는 아래 링크 참조

코딩의 시작, TCP School

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 

댓글