Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
Tags
- springboot
- mysql
- openAI
- llm
- 게시판
- crud
- fastapi
- 해외봉사
- 회고
- Java
- 프로그래머스
- 로그인
- 부트스트랩
- Dockerfile
- 프로젝트
- cors
- 서버 꺼짐
- Spring
- 커밋 메시지
- 알고리즘
- docker
- spring boot
- 코딩테스트
- Lv.2
- 우테코
- 쿠키로그인
- 네팔
- LV2
- 세션로그인
- OOM
Archives
- Today
- Total
s00jin 님의 블로그
3. [로그인] 쿠키 로그인 구현하기 - Spring/SpringBoot 본문
쿠키란 무엇인가
간단하게 말하면 사용자 정보 같은 파일이 로컬에 저장되는 것이다!
사용자의 장치에 다운로드 되고 브라우저에 저장되는 작은 텍스트 파일
- 로그인 성공 시 서버가 쿠키에 사용자 정보를 넣어줌
- 클라이언트 측에서 다음 요청을 할 때마다 이 쿠키를 서버에 같이 보내줌
- 서버에서는 이 쿠키를 확인해 로그인 했는지 확인
쿠키 생성
- 쿠키 생성
- new Cookie() 메서드 사용
- Cookie cookie = new Cookie(”키”, “값”);
- Cookie cookie = new Cookie(”userId”, String.valueOf(user.getId()));
- 유효 시간 설정
- cookie.setMaxAge(60*60);
- 1시간 설정 ⬆️
- response(응답)에 쿠키 태우기
- response.addCookie(cookie);
- response는 HttpServletResponse의 객체
쿠키 확인
- @CookieValue 어노테이션 사용
- @CookieValue(name = "userId", required = false) Long userId
쿠키 파기
- 똑같은 Key 값을 넣어주고 Value 값은 null을 넣어 새로 쿠키 생성
- Cookie cookie = new Cookie("userId", null);
- setMaxAge(0)을 통해 유효 시간을 0초로 설정
- cookie.setMaxAge(0);
- response에 addCookie를 통해 다시 넣어주기 → 쿠키 파기됨
- response.addCookie(cookie);
더보기
[Spring]Cookie와 활용법(읽기, 생성 및 저장)
⬆️ 이 블로그에 쿠키에 대한 설명이 잘 나와있는것 같으니 더 궁금하면 확인!
구현
UserRepository
package org.mySite.user.repository;
public interface UserRepository extends JpaRepository<User, Long> {
// 1개라도 존재하면 바로 탈출 existsBy~
boolean existsByLoginId(String loginId);
boolean existsByNickname(String nickname);
Optional<User> findByLoginId(String loginId);
Optional<User> findById(Long Id);
}
UserService
package org.mySite.user.service;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User getLoginUser(Long loginRequest){
User user = userRepository.findById(loginRequest)
.orElseThrow(() -> new IllegalArgumentException("UserId not found"));
return user;
}
// ------ 회원가입 --------
// loginId 중복 체크
public boolean checkLoginId(String loginId) {
// 1건이라도 있으면 바로 스캔 종료 후 true 리턴
return userRepository.existsByLoginId(loginId);
}
// nickname 중복 체크
public boolean checkNickname(String nickname) {
return userRepository.existsByNickname(nickname);
}
// JoinRequest을 입력 받아 User로 변환
public void join(JoinRequest request) {
userRepository.save(request.toEntity());
}
// ------로그인-------
public User login(LoginRequest request){
// 일치하는 User 찾기
User user = userRepository.findByLoginId(request.getLoginId())
.orElseThrow(() -> new IllegalArgumentException("User not found"));
// 찾은 User 비밀번호와 입력된 비밀번호 일치하는지 확인
if(!user.getPassword().equals(request.getPassword())) {
return null;
}
return user;
}
}
CookieLoginController
package org.mySite.user.controller;
@Controller
@RequiredArgsConstructor
@RequestMapping("/cookie-login")
public class CookieLoginController {
private final UserService userService;
// 쿠키 로그인 페이지
@GetMapping(value = {"", "/"})
// @CookieValue 어노테이션을 통해 쿠키값 받아오기 가능
public String home(@CookieValue(name = "userId", required = false) Long userId, Model model){
model.addAttribute("loginType", "cookie-login");
model.addAttribute("pageName", "쿠키 로그인");
// 로그인 구현 전 임시 추가
//model.addAttribute("nickname", "kk");
if (userId != null) {
User loginUser = userService.getLoginUser(userId);
model.addAttribute("nickname", loginUser.getNickname());
}
return "home";
}
// 회원가입 페이지
@GetMapping("/join")
public String joinPage(Model model){
model.addAttribute("loginType", "cookie-login");
model.addAttribute("pageName", "쿠키 로그인");
model.addAttribute("joinRequest", new JoinRequest());
return "join";
}
// 회원가입
@PostMapping("/join")
public String join(@Valid @ModelAttribute JoinRequest joinRequest, BindingResult bindingResult, Model model){
model.addAttribute("loginType", "cookie-login");
model.addAttribute("pageName", "쿠키 로그인");
// loginId 중복 체크
if(userService.checkLoginId(joinRequest.getLoginId())) {
bindingResult.addError(new FieldError("joinRequest", "loginId", "중복된 아이디입니다."));
}
// 닉네임 중복 체크
if (userService.checkNickname(joinRequest.getNickname())){
bindingResult.addError(new FieldError("joinRequest", "nickname", "중복된 닉네임입니다."));
}
// password와 passwordCheck 확인
if (!joinRequest.getPassword().equals(joinRequest.getPasswordCheck())) {
bindingResult.addError(new FieldError("joinRequest", "passwordCheck", "비밀번호가 일치하지 않습니다."));
}
if (bindingResult.hasErrors()) {
return "join";
}
userService.join(joinRequest);
return "redirect:/cookie-login";
}
// 로그인 페이지
@GetMapping("/login")
public String loginPage(Model model) {
model.addAttribute("loginType", "cookie-login");
model.addAttribute("pageName", "쿠키 로그인");
model.addAttribute("loginRequest", new LoginRequest());
return "login";
}
// 로그인
@PostMapping("/login")
public String login(@ModelAttribute LoginRequest loginRequest, BindingResult bindingResult, HttpServletResponse response, Model model) {
model.addAttribute("loginType", "cookie-login");
model.addAttribute("pageName", "쿠키 로그인");
User user = userService.login(loginRequest);
// System.out.println("입력된 로그인 ID: " + loginRequest.getLoginId());
// 틀릴 경우 global error return
if(user == null){
bindingResult.reject("loginFail", "로그인 아이디 또는 비밀번호가 틀렸습니다.");
}
if(bindingResult.hasErrors()) {
return "login";
}
// 로그인 성공 => 쿠키 생성
Cookie cookie = new Cookie("userId", String.valueOf(user.getId()));
cookie.setMaxAge(60*60); // 유효 시간 1시간
response.addCookie(cookie);
return "redirect:/cookie-login";
}
// -----------로그아웃 ------------------
@GetMapping("/logout")
public String logout(HttpServletResponse response, Model model){
model.addAttribute("pageName", "쿠키 로그인");
model.addAttribute("loginType", "cookie-login");
Cookie cookie = new Cookie("userId", null);
cookie.setMaxAge(0);
response.addCookie(cookie);
return "redirect:/cookie-login";
}
// ------------ 회원 정보 ------------------
@GetMapping("/info")
public String infoPage(@CookieValue(name = "userId", required = false) Long userId, Model model){
model.addAttribute("pageName", "쿠키 로그인");
model.addAttribute("loginType", "cookie-login");
User user = userService.getLoginUser(userId);
if (user == null){
return "redirect:/cookie-login/login";
}
model.addAttribute("user", user);
return "info";
}
// --------- 관리자 페이지 --------------
@GetMapping("/admin")
public String adminPage(@CookieValue(name = "userId", required = false) Long userId, Model model){
model.addAttribute("pageName", "쿠키 로그인");
model.addAttribute("loginType", "cookie-login");
User user = userService.getLoginUser(userId);
if (user == null){
return "redirect:/cookie-login/login";
}
if (!user.getRole().equals(UserRole.ADMIN)){
return "redirect:/cookie-login";
}
return "admin";
}
}
템플릿
admin
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="|${pageName}|"></title>
</head>
<body>
<div>
<h1><a th:href="|/${loginType}|">[[${pageName}]]</a></h1><hr>
<h2>관리자 페이지</h2>
<h3>인가에 성공하였습니다.</h3>
</div>
</body>
</html>
home
<!DOCTYPE html>
<html lang="ko">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="|${pageName}|"></title>
</head>
<body>
<div>
<h1><a th:href="|/${loginType}|">[[${pageName}]]</a></h1>
<hr>
<div th:if="${nickname == null}">
<h3>로그인 상태가 아닙니다.</h3>
<button th:onclick="|location.href='@{/{loginType}/join (loginType=${loginType})}'|">회원 가입</button>
<button th:onclick="|location.href='@{/{loginType}/login (loginType=${loginType})}'|">로그인</button>
</div>
<div th:unless="${nickname == null}">
<h3>[[${nickname}]]님 환영합니다!</h3>
<button th:onclick="|location.href='@{/{loginType}/info (loginType=${loginType})}'|">유저 정보</button>
<button th:onclick="|location.href='@{/{loginType}/admin (loginType=${loginType})}'|">관리자 페이지</button>
<button th:onclick="|location.href='@{/{loginType}/logout (loginType=${loginType})}'|">로그아웃</button>
</div>
</div>
</body>
</html>
info
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="|${pageName}|"></title>
</head>
<body>
<div>
<h1><a th:href="|/${loginType}|">[[${pageName}]]</a></h1><hr>
<h2>유저 정보</h2>
<div>
<div th:text="|loginId : ${user.loginId}|"></div>
<div th:text="|nickname : ${user.nickname}|"></div>
<div th:text="|role : ${user.role}|"></div>
</div>
</div>
</body>
</html>
join
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="|${pageName}|"></title>
</head>
<body>
<div>
<h1><a th:href="|/${loginType}">[[${pageName}]]</a></h1><hr>
<h2>회원 가입</h2>
<form th:method="post" th:action="|@{/{loginType}/join (loginType=${loginType})}|" th:object="${joinRequest}">
<div>
<label th:for="loginId">로그인 아이디: </label>
<input type="text" th:field="*{loginId}" th:errorclass="error-input"/>
<div class="error-class" th:errors="*{loginId}"></div>
</div>
<br>
<div>
<label th:for="password">비밀번호 : </label>
<input type="password" th:field="*{password}" th:errorclass="error-input"/>
<div class="error-class" th:errors="*{password}"></div>
</div>
<br/>
<div>
<label th:for="passwordCheck">비밀번호 체크 : </label>
<input type="password" th:field="*{passwordCheck}" th:errorclass="error-input"/>
<div class="error-class" th:errors="*{passwordCheck}"></div>
</div>
<br/>
<div>
<label th:for="nickname">닉네임 : </label>
<input type="text" th:field="*{nickname}" th:errorclass="error-input"/>
<div class="error-class" th:errors="*{nickname}"></div>
</div>
<br/>
<button type="submit">회원 가입</button>
</form>
</div>
</body>
</html>
<style>
.error-class {
color: red;
}
.error-input {
border-color: red;
}
</style>
login
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="|${pageName}|"></title>
</head>
<body>
<div>
<h1><a th:href="|/${loginType}|">[[${pageName}]]</a></h1> <hr/>
<h2>로그인</h2>
<form th:method="post" th:action="|@{/{loginType}/login (loginType=${loginType})}|" th:object="${loginRequest}">
<div>
<label th:for="loginId">로그인 아이디 : </label>
<input type="text" th:field="*{loginId}"/>
</div>
<br/>
<div>
<label th:for="password">비밀번호 : </label>
<input type="password" th:field="*{password}"/>
</div>
<div th:if="${#fields.hasGlobalErrors()}">
<br/>
<div class="error-class" th:each="error : ${#fields.globalErrors()}" th:text="${error}"/>
</div>
<br/>
<button type="submit">로그인</button>
<button type="button" th:onclick="|location.href='@{/{loginType}/join (loginType=${loginType})}'|">회원가입</button><br>
</form>
</div>
</body>
</html>
<style>
.error-class {
color: red;
}
.error-input {
border-color: red;
}
</style>
'프로젝트 > 하고 싶은거 다해보는 내 사이트' 카테고리의 다른 글
| 4. [로그인] 세션 로그인 구현하기 - Spring/SpringBoot (1) | 2025.07.07 |
|---|---|
| 3-1. [로그인/에러] 쿠키 로그인 시 500 에러 (whitelabel error page / 500) (0) | 2025.07.01 |
| 2. [로그인] 로그인 구현 전 설계하기 (0) | 2025.06.26 |
| 1. [MySQL/SpringBoot] 로컬 MySQL 연결하기 (0) | 2025.06.26 |
| 0. [SpingBoot] 초기 설정 및 서버 헬스 체크 구현하기 (0) | 2025.06.26 |