유저 가입
로그인 (1)
user 테이블 생성
CREATE TABLE `mybatis`.`user` (
`email` VARCHAR(100) NOT NULL,
`password` VARCHAR(200) NOT NULL,
`name` VARCHAR(100) NOT NULL,
PRIMARY KEY (`email`)
);
User클래스, UserMapper 인터페이스 생성
- User -
@Data // get set toString
@AllArgsConstructor // 모든 필드변수로 생성자 생성
public class User {
private String email;
private String password;
private String name;
}
- UserMapper -
@Mapper
public interface UserMapper {
@Insert("INSERT INTO user VALUES(#{email}, #{password}, #{name})")
public int insert(User user);
@Update("UPDATE user SET password = #{password} WHERE email = #{eamil}")
public int update(User user);
@Delete("DELETE FROM user WHERE email = #{email}")
public int delete(User user);
@Select("SELECT count(*) FROM user")
public int count();
@Select("SELETE * FROM user")
public List<User> selectAll();
@Select("SELECT * FROM user WHERE email = #{email}")
public User selectByEmail(String email);
}
board와 달리 login쪽은 매퍼 xml파일없이 현재 자바매퍼에서 쿼리처리를 모두 할 예정.
- UserController -
@Controller
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/register")
public Object getUsersView(@ModelAttribute User user) {
// register의 user객체를 model로 전달
return "register";
}
}
- register.html -
<div class="card-header">
<h4 class="font-weight-bolder">가입하기</h4>
<p class="mb-0" th:text="${message} ?: '가입양식을 작성해주세요'"></p>
</div>
<div class="card-body bg-white">
<form role="form" th:action="@{/register}" method="post" th:object="${user}">
<div class="input-group input-group-outline mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" th:field="*{name}" required />
</div>
<div class="input-group input-group-outline mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" th:field="*{email}" required />
</div>
<div class="input-group input-group-outline mb-3">
<label class="form-label">Password</label>
<input type="password" class="form-control" th:field="*{password}" required />
</div>
<div class="form-check form-check-info text-start ps-0">
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" checked />
<label class="form-check-label" for="flexCheckDefault"> I agree the <a href="javascript:;" class="text-dark font-weight-bolder">Terms and Conditions</a> </label>
</div>
<div class="text-center">
<button type="submit" class="btn btn-lg bg-gradient-primary btn-lg w-100 mt-4 mb-0">가입하기</button>
</div>
</form>
</div>
<div class="card-footer bg-white text-center pt-0 px-lg-2 px-1">
<p class="mb-2 text-sm mx-auto">
이미 계정이 있습니까?
<a th:href="@{/pages/login.html}" class="text-primary text-gradient font-weight-bold">로그인</a>
</p>
</div>
</div>
th:text="${message} ?: '가입양식을 작성해주세요'"
: message가 없으면 '가입양식을 작성해주세요' 문구가 나타남.
th:object="${user}"
model객체를 통해 user의 정보를 받아 form내의 각 field에 데이터를 할당.
required
로 반드시 입력을 해야 넘어갈 수 있도록 해줌.
- UserController -
@PostMapping("/register")
public String postUser(User user, Model model, HttpSession session, RedirectAttributes attr) {
// 클라이언트 단 또는 서버 단에서 데이터 유효성 체크를 적용하는 것을 권장
User duplicatedUser = userMapper.selectByEmail(user.getEmail()); // 이메일이 같은 유저가 없으면 null
if (duplicatedUser == null) {
// 이메일 중복이 아니므로 가입처리
userMapper.insert(user);
attr.addFlashAttribute("message", "가입되었습니다.");
return "redirect:/login";
} else {
// 이메일 중복이므로 메시지와 함께 리다이렉트로 돌아감
attr.addFlashAttribute("message", "email 중복입니다.");
return "redirect:/register";
}
}
- register.html -
<style>
input:invalid:focus {
/* input태그의 조건이 충족되지 않았을때 */
background-image: linear-gradient(rgb(172, 255, 193), lightblue) !important;
}
</style>
<input type="text" class="form-control" th:field="*{name}" minlength="2" maxlength="5" required />
<input type="email" class="form-control" th:field="*{email}" required />
<input type="password" class="form-control" th:field="*{password}" minlength="4" maxlength="10" required />
간단한 유효성검사이므로 클라이언트에서 진행.
input:invalid:focus
는 입력한 값이 input태그의 조건에 맞지 않을 경우 실행. 여기서는 background컬러가 들어감.
!important
속성을 사용해 부트스트랩보다 우선순위를 높게 적용
minlength="?"
: 최소 글자수를 지정
maxlength="?"
: 최대 글자수를 지정
가입하기에 성공했을 때.
email이 중복일때는 message가 나타난 로그인 화면으로 돌아간다.
- Login -
@Data
@AllArgsConstructor
public class Login {
private String email;
private String password;
private String error; // 오류메시지를 저장하는 문자열
}
- LoginService -
@Service
public class LoginService {
@Autowired
private UserMapper userMapper;
// 인증하기 메서드 : 실패할 경우 login 객체에 error메시지 입력
public void authenticate(Login login) {
// 이메일 검색으로 유저찾기
User user = userMapper.selectByEmail(login.getEmail());
if (user == null) {
login.setError("이메일이 존재하지 않습니다.");
} else {
if (!user.getPassword().equals(login.getPassword())) {
login.setError("패스워드가 틀립니다.");
} else {
login.setError(null); // 에러없음(이메일 인증됨)
}
}
}
// 유저 찾기 메서드 : 이메일로 유저 찾기
public User findUserByEmail(String email) {
User user = userMapper.selectByEmail(email);
return user;
}
}
UserMapper를 이용
- LoginController -
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
@GetMapping("/login")
public String getLoginView(@ModelAttribute Login login) {
// 로그인페이지에 Login객체 login을 바인딩
return "login";
}
/**
* 로그인 처리 (인증되었을 경우 user객체를 세션에 저장)
* @param login
* @param model
* @param session
* @return
*/
@PostMapping("/login")
public String postLogin(Login login, Model model, HttpSession session) {
loginService.authenticate(login); // 인증 메서드 실행(실패시 에러메시지 입력됨)
if (login.getError() != null) {
model.addAttribute("message", login.getError());
return "login"; // 로그인 실패
} else {
User user = loginService.findUserByEmail(login.getEmail());
session.setAttribute("user", user); // 세션에 인증된 유저를 저장
System.out.println(user.toString()); // 유저 출력
return "redirect:/board/list";
}
}
@GetMapping("/logout")
public String getLogout(HttpSession session) {
session.removeAttribute("user");
//session.remove ? ("user");
return "redirect:/login";
}
}
- login.html -
<style>
input:invalid:focus {
/* input태그의 조건이 충족되지 않았을때 */
background-image: linear-gradient(rgb(172, 255, 193), lightblue) !important;
}
</style>
<div class="card-body">
<p class="text-primary" th:if="${message}" th:text="${message}"></p>
<form role="form" class="text-start" th:action="@{/login}" method="post" th:object="${login}">
<div class="input-group input-group-outline my-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" th:field="*{email}" required />
</div>
<div class="input-group input-group-outline mb-3">
<label class="form-label">Password</label>
<input type="password" class="form-control" th:field="*{password}" minlength="4" maxlength="10" required />
</div>
<div class="form-check form-switch d-flex align-items-center mb-3">
<input class="form-check-input" type="checkbox" id="rememberMe" />
<label class="form-check-label mb-0 ms-2" for="rememberMe">Remember me</label>
</div>
<div class="text-center">
<button type="submit" class="btn bg-gradient-primary w-100 my-4 mb-2">로그인</button>
</div>
<p class="mt-4 text-sm text-center">
계정이 없습니까?
<a th:href="@{/register}" class="text-primary text-gradient font-weight-bold">가입하기</a>
</p>
</form>
</div>
http://localhost:8080/login 에서 앞서 가입한 eamil로 로그인
로그인에 성공하면 게시글리스트페이지로 이동한다.
로그인 실패시 message가 출력되며 로그인 페이지로 돌아간다.