BackEnd/Spring MVC

[Spring] 로그인 처리1 - 쿠키, 세션 - 1

샤아이인 2022. 3. 9.

내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.

 

1. 회원 가입

우선 데이터를 저장할 Member entity부터 만들어야 한다.

 

● Member

@Data
public class Member {

    private Long id;

    @NotEmpty
    private String loginId; // 로그인 ID
    @NotEmpty
    private String name;
    @NotEmpty
    private String password;
}
 

● MemberRepository

MemberRepository는 Member를 저장해두는 저장소 역할을 한게된다.

@Slf4j
@Repository
public class MemberRepository {

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    public Member save(Member member){
        member.setId(++sequence);
        log.info("save : member={}", member);
        store.put(member.getId(), member);
        return member;
    }

    public Member findById(Long id){
        return store.get(id);
    }

    public Optional<Member> findByLoginId(String loginId){
        return findAll().stream()
                .filter(member -> member.getLoginId().equals(loginId))
                .findFirst();
    }

    public List<Member> findAll(){
        return new ArrayList<>(store.values());
    }

    public void clearStore(){
        store.clear();
    }
}
 

● MemberController

@Controller
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberController {

    private final MemberRepository memberRepository;

    @GetMapping("/add")
    public String addForm(@ModelAttribute("member") Member member){
        return "members/addMemberForm";
    }

    @PostMapping("/add")
    public String save(@Validated @ModelAttribute Member member, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "members/addMemberForm";
        }

        memberRepository.save(member);
        return "redirect:/";
    }
}
 

이후 회원가입 뷰 템플릿을 추가해주면 끝난다 (templates/members/addMemberForm.html ), 이 과정은 생략.

 

BeanValidation 기능 덕분에 검증이 매우 쉬워졌다.

 

2. 로그인 기능

로그인 기능을 개발해보자. 우선 세션등록이나 쿠키는 나중에 생각하고, 우선적으로 로그인 ID, 비밀번호를 입력하는 부분에 집중하자.

우선 로그인을 처리해주는 서비스 부터 구현해야 한다. 다음 코드를 살펴보자.

 

● LoginService

@Service
@RequiredArgsConstructor
public class LoginService {

    private final MemberRepository memberRepository;

    public Member login(String loginId, String password){
//        Optional<Member> findMemberOptional = memberRepository.findByLoginId(loginId);
//        Member member = findMemberOptional.get();
//
//        if(member.getPassword() == password){
//            return member;
//        }
//
//        return null;

        return memberRepository.findByLoginId(loginId)
                .filter(m -> m.getPassword().equals(password))
                .orElse(null);
    }
}
 

로그인의 핵심 비즈니스 로직은 ID로 회원을 조회한 다음에 파라미터로 넘어온 password와 비교해서 같으면 회원을 반환하고,

만약 password가 다르면 null 을 반환하게 하는 것 이다.

 

● LoginForm

@Data
public class LoginForm {

    @NotEmpty
    private String loginId;
    @NotEmpty
    private String password;
}

 

로그인 데이터를 전달하게 될 객체이다.

 

● LoginController

@Slf4j
@Controller
@RequiredArgsConstructor
public class LoginController {

    private final LoginService loginService;

    @GetMapping("/login")
    public String loginForm(@ModelAttribute LoginForm form){
        return "login/loginForm";
    }

    @PostMapping("/login")
    public String login(@Validated @ModelAttribute LoginForm form, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "login/loginForm";
        }

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

        if(loginMember == null){
            bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
            return "login/loginForm";
        }

        // TODO 로그인 성공 처리

        return "redirect:/";
    }
}
 

Get 요청으로 "/login"에 대한 접근이 온다면, 로그인 html 화면을 보여주고,

Post 요청으로 온다면 이는 로그인에 대한 정보가 담겨서 넘어온 것 이다.

 

이렇게 넘어온 데이터를 LoginForm form 으로 ArgumentResolver를 통해 객체로 생성되어 받는다.

또한 @Validated 가 적용되어 있기 때문에 검증또한 가능하다.

 

이후 로그인 서비스(비지니스 컴포넌트)를 통하여 로그인 을 하여 해당 loginMember를 찾아온다.

만약 해당 Member가 없다면 다시 로그인 폼으로 이동하며, Member가 있다면 홈으로 리다이렉트 하면 된다.

 

로그인에 실패하면 bindingResult.reject() 를 사용해서 글로벌 오류( ObjectError )를 생성한다.

그리고 정보를 다시 입력하도록 로그인 폼을 뷰 템플릿으로 사용한다.

 

중간에 TODO는 세션이나, 쿠키 처리를 위한 부분이다. 이는 뒤에서 다룬다.

 

(loginForm.html 코드는 생략)

댓글