[2] 회원가입 및 로그인 구현
들어가며
로그인 기능을 통해 사용자를 식별후 등록,삭제,수정 및 댓글 달기 기능을 사용할수 있도록 하기위해서 게시판에 사용자를 등록하여 이용할수 있도록 로그인및 회원가입을 간단하게 구현해보자
(회원가입 및 로그인은 Spring Mvc 방식으로 구현)
프로젝트 환경
[SpringBoot ] 무작정 (REST API)CRUD게시판을 만들어 보자 준비 [1]
[1] 프로젝트 준비 들어가며 프레임워크나, 언어, DB등을 이론적로 공부를 하고난후에 한번 토이 프로젝트를 통해서 실습을 해보고싶었다. 그래서 이번에 간단한 CRUD를 통해 게시판을 만들어봤다
back-stead.tistory.com
회원가입
Entity
Member.class
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member {
@Id
@GeneratedValue
@Column(name = "member_Id")
private Long id;
private String username;
private String password;
}
JPA의 @Entity를 통해서 엔티티를 생성한다.
MemberDto.class
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberDto {
private Long id;
@NotBlank(message = "이름을 입력하세요")
private String username;
@NotBlank(message = "비밀번호를 입력하세요")
private String password;
}
실제 엔티티 객체를 통해 데이터를 전달하지않고 Dto( Data Transfer Object )를 통해 데이터를 주고 받기 위해서 MemberDto를 생성한다.
Repository
MemberRepository
@Repository
public interface MemberRepository extends JpaRepository<Member,Long> {
}
- 스프링 데이터 JPA를 사용하여 MemberRepository를 생성한다.
- @Repository 에노테이션을 사용해 스프링빈에 등록되도록한다.
Service
MemberService
public interface MemberService {
Member saveEntity(Member member);
Member saveDto(MemberDto memberDto);
}
MemberServiceImpl
@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Override
public Member saveEntity(Member member) {
return memberRepository.save(member);
}
@Override
public Member saveDto(MemberDto memberDto) {
Member member = Member.builder()
.username(memberDto.getUsername())
.password(memberDto.getPassword())
.build();
return saveEntity(member);
}
}
스프링의 "의존성 역전 원칙(Dependency Inversion Principle)" 법칙을 따르기위해 MemberSerive를 구현하는 MemberServiceImpl클래스를 생성
- saveEntity : 실제 엔테티를로 저장
- saveDto : dto를 통해 엔티티에 저장
Html
joinMember.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header">
</head>
<style>
.error{
color: firebrick;
}
</style>
<body>
<form th:action="@{/createMember}"method="post" th:object="${member}">
<div>
<label th:for="username">아이디</label>
<input type="text" th:field="*{username}"placeholder="아이디를 입력하세요">
<div class="error" th:errors="*{username}" ></div>
</div>
<div>
<label th:for="password">비밀번호</label>
<input type="password" th:field="*{password}" placeholder="비밀번호를 입력하세요">
<div class="error" th:errors="*{password}"></div>
</div>
<button type="submit">회원가입하기</button>
</form>
</body>
</html>
로그인
Dto
LoginDto
@Data
public class LoginDto {
private String username;
private String password;
}
사용자 이름과 비밀번호를 전송할 Dto생성
Repository
MemberRepository
Member findByUsername(String username);
- findByUsername : Member엔티티에서 Username을 찾는다.
Service
MemberServiceImpl
@Override
public Member findByUsername(String username) {
return memberRepository.findByUsername(username);
}
public boolean login(LoginDto loginDto){ //로그인 구현
String username = loginDto.getUsername();
String password = loginDto.getPassword();
Member byUsername = memberRepository.findByUsername(username);
if(byUsername!=null) {
if (byUsername.getPassword().equals(password)) {
return true;
}
}
return false;
}
MemberSeirvce에 findByUsername을 정의하고 Impl에 구현한다.
- login : loginDto에서 넘어온 username과 password를 통해서 db에 값이 일치하면 true 일치하지않으면 false를 반환한다.
Controller
MemberController
@GetMapping("/login")
public String getLogin(HttpServletRequest request, Model model){
//현재 페이지를 가져와 세션에 저장
String referer = request.getHeader("Referer");
request.getSession().setAttribute("prevPage", referer);
log.info("uri={}",referer);
model.addAttribute("login",new LoginDto());
return "member/login";
}
@PostMapping("/login")
public String postLogin(@ModelAttribute("login") LoginDto loginDto,HttpServletRequest request, HttpSession session,Model model){
boolean login = memberService.login(loginDto);
if (login){
String username = loginDto.getUsername();
Member member = memberService.findByUsername(username);
session.setAttribute("loginMember",member);
//저장한 이전페이지 주소를 가져온다
String prevPage = (String) request.getSession().getAttribute("prevPage");
//세션에 페지이 주소 삭제
request.getSession().removeAttribute("prevPage");
return "redirect:" + (prevPage != null ? prevPage : "/"); //이전페이지가 있다면 이전페이지로 없다면 / 페이지로
}
model.addAttribute("error", "비밀번호 또는 아이디가 올바르지 않습니다.");
return "member/login";
}
@PostMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("loginMember");
return "redirect:/";
}
- getLogin() : 로그인성공시에 원래 있던 페이지로 돌아가기 위해서 로그인페이지로 들어오기 전 URL을 세선에 저장한다.
- postLogin() : 로그인을 성공하면 세션에 로그인한 사용자 정보를 저장하고, requset에 저장된 이전 페이지 정보를 가져와 이전페이지로 리다이렉트한다. 실패하면 오류메시지를 모델에 담아서 뷰에 전달한다.
- logout() : 로그아웃을 실행하면 세션에 저장한 사용자 정보를 지운다.
HomeController
@GetMapping("/")
public String home(Model model, HttpSession session) {
Member loginMember = (Member) session.getAttribute("loginMember");
model.addAttribute("loginMember", loginMember);
return "home";
}
세션에서 loginMember로 저장된 사용자 정보를 가져와서 뷰에 넘겨준다.
Html
home.html
<h1>게시판 토이 프로젝트</h1>
<a href="#" th:href="@{/board}"> 게시판으로 이동하기</a>
<div th:if="${loginMember==null}">
<a th:href="@{/createMember}">회원가입</a>
<a th:href="@{/login}">로그인</a>
</div>
<div th:if="${loginMember!=null}">
<div th:text="'사용자 :'+${loginMember.username}"></div>
<form th:action="@{/logout}" method="post">
<button type="submit">로그아웃</button>
</form>
</div>
코드를 변경하고 사용자 정보가 <th:if>를 사용하여 로그인한 사용자 정보가 있다면, 사용자 이름과 로그아웃 버튼을 보여주고 없다면 회원가입과 로그인이 보여주게한다.
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header"></head>
<style>
.login {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-heading {
font-weight: 300;
}
body {
font-family: 'DungGeunMo-Regular';
}
</style>
<script th:inline="javascript">
</script>
<body>
<div class="container-fluid ps-md-0">
<div class="login">
<div class="container align-content-center"> <!-- 이 부분에 text-center 클래스를 추가하여 로그인 폼을 가운데로 정렬합니다. -->
<div th:if="${error}" class="alert alert-danger" role="alert">
<p th:text="${error}" style="text-align: center"></p>
</div>
<div class="col-md-9 col-lg-8 mx-auto">
<a href="/" style="text-decoration: none; color: #0D0D0D;">
<h1 class="text-center">로그인</h1>
</a>
<form th:action="@{/login}" method="post" th:object="${login}">
<div class="form-floating mb-3 text-center">
<input type="text" th:field="*{username}" placeholder="Username"/>
</div>
<div class="form-floating mb-3 text-center">
<input type="password" th:field="*{password}" placeholder="Password"/>
</div>
<div class="text-center">
<button class="btn btn-primary" type="submit" >로그인</button>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
실행
다음으로
다음 포스팅에서는 게시판의 엔티티를 만들고 사용자와 연관관계 매핑을 해주며, 게시판의 기본적인 틀을 만들어볼 예정입니다.