ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링MVC - 2편] 로그인 처리1 - 쿠키, 세션
    스프링&스프링부트 2025. 3. 4. 20:30

    패키지 구조 설계

    package 구조

    • hello.login
      • domain
        • item
        • member
        • login
      • web
        • item
        • member
        • login

    도메인이 가장 중요하다

    도메인 = 화면, UI, 기술 인프라 등등의 영역은 제외한 시스템이 구현해야 하는 핵심 비즈니스 업무 영역을 말한다

     

    향후 web 다른 기술로 바꾸어도 도메인은 그대로 유지할 있어야 한다

    이렇게 하려면 web domain 알고있지만 domain web 모르도록 설계해야 한다

    이것을 web domain의존하지만, domain web 의존하지 않는다고 표현한다

     

     

    화면

    HomeController - home() 수정

    @GetMapping("/")
    public String home() {
    	return "home";
    }

     

    - templates/home.html 추가

     

     

     

    회원 가입

    Member

    @Data
    public class Member {
    	private Long id;
    
    	@NotEmpty
    	private String loginId; // 로그인 ID
    
    	@NotEmpty
    	private String name; // 사용자 이름
    
    	@NotEmpty
    	private String password;
    }

     

     

    MemberRepository

    /**
    * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
    */
    @Slf4j
    @Repository
    public class MemberRepository {
    	
        private static Map<Long, Member> store = new HashMap<>();
    	private static long sequence = 0L; //static 사용
    
    	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(m -> m.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(@Valid @ModelAttribute Member member, BindingResult result) {
    		if (result.hasErrors()) {
    			return "members/addMemberForm";
    		}
            
    		memberRepository.save(member);
    		return "redirect:/";
    	}
    }

    - 회원 가입 템플릿 : templates/members/addMemberForm.html 생성 

     

     

     

    회원용 테스트 데이터 추가

    TestDataInit

    @Component
    @RequiredArgsConstructor
    public class TestDataInit {
    	private final ItemRepository itemRepository;
    	private final MemberRepository memberRepository;
    
    	@PostConstruct
    	public void init() {
    		itemRepository.save(new Item("itemA", 10000, 10));
    		itemRepository.save(new Item("itemB", 20000, 20));
    
    		Member member = new Member();
    		member.setLoginId("test");
    		member.setPassword("test!");
    		member.setName("테스터");
    		memberRepository.save(member);
    	}
    }

     

     

     

    로그인 기능

    LoginService

    @Service
    @RequiredArgsConstructor
    public class LoginService {
    	private final MemberRepository memberRepository;
    
    	/**
    	* @return null이면 로그인 실패
    	*/
    	public Member login(String loginId, String password) {
    		return memberRepository.findByLoginId(loginId)
    						.filter(m -> m.getPassword().equals(password))
    						.orElse(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") LoginForm form) {
    		return "login/loginForm";
    	}
        
    	@PostMapping("/login")
    	public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult) {
    		if (bindingResult.hasErrors()) {
    			return "login/loginForm";
    		}	
            
    		Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
    		log.info("login? {}", loginMember);
    		if (loginMember == null) {
    			bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
    			return "login/loginForm";
    		}
            
    		// 로그인 성공 처리 TODO
    		return "redirect:/";
    	}
    }

    - 로그인 템플릿 : templates/login/loginForm.html 생성

     

     

     

    로그인 처리하기 - 쿠키 사용

    쿠키에는 영속 쿠키와 세션 쿠키가 있다

    • 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
    • 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지

     

     

     

    LoginController - login()

    로그인 성공시 세션 쿠키를 생성

    @PostMapping("/login")
    public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
    	if (bindingResult.hasErrors()) {
    		return "login/loginForm";
    	}
        
    	Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
    	log.info("login? {}", loginMember);
    	if (loginMember == null) {
    		bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
    		return "login/loginForm";
    	}
        
    	// 로그인 성공 처리
    	// 쿠키에 시간 정보를 주지 않으면 세션 쿠키(브라우저 종료시 모두 종료)
    	Cookie idCookie = new Cookie("memberId",
    	String.valueOf(loginMember.getId()));
    	response.addCookie(idCookie);
    	return "redirect:/";
    }

     

     

     

     

     

     

    728x90
Designed by Tistory.