chat gpt와 대화하다 보니 mapper.xml 파일까지 생략 가능하다는 걸 알게 되었다.
대신에 sql문은 interface에서 작성하면 되고, 매핑 오류가 철저히 없어지는 효과가 보였다.
A. 기획의도:
이미 로그인 중이면 로그인, 회원가입 버튼은 비출력
. 로그인 중 해당 id 상단 출력
공통적으로 로그인한 관리자 & 사용자 모두 홈, 전체상품, 로그아웃 버튼이 보임
관리자만 상품관리, 회원관리 페이지 보이고 진입 가능
. 추후 사용자만 볼수있는 페이지도 있어야겠지~
로그아웃했을 때의 화면:
로그인 실패 화면:
+) 로그아웃 즉시 세션이 무효화돼서 로그인 전과 동일하게 '회원가입' 버튼이 보이는 게 어쩔수없나봐..
B. 소스코드:
1, LoginVo
@Data
public class LoginVo {
private String username;
private String password;
private String loginType; // admin 또는 user
}
2, AdminMapper.java #interface
ㄴ db 호출
ㄴ 인증 메서드 (authenticate) 정의
@Mapper
public interface AdminMapper {
@Select("SELECT COUNT(*) FROM admin "
+ "WHERE aid = #{username} "
+ "AND AES_DECRYPT(encryption, 'encryption_key') = #{password}")
int authenticate(@Param("username") String username,
@Param("password") String password);
}
/*
AES_DECRYPT: 해독 - 암호화 해제
AES_ENCRYPT: 암호화
MyBatis를 사용하여 암호화된 비밀번호를 검증하려면
데이터베이스 쿼리를 통해 암호를 해독하고 비교해야 함
*/
username, password: 입력 받고 db와 비교할 값
aid, encryption: db에서 지정한 로그인 정보 필드명.
해당 필드명을 애초에 각각 username, password로 통일했으면 더 편하지 않았을까 싶다가도
한편으론 다르게 지정한 것도 뭐가 뭔지 구분하기에 도움이 되지 않나 싶기도?
결론적으로는 나 통일파다. 다음 플젝엔 통일하도록 하자. (편한 건 최고)
3, AdminService.java
ㄴ 인증결과 반환
입력된 (username, password) 짝이 db에 존재하면 해당 레코드 건수 반환
@Service
public class AdminService {
@Autowired
private AdminMapper adminMapper;
public boolean authenticate(String username, String password) {
int count = adminMapper.authenticate(username, password);
return count > 0;
}
}
User도 마찬가지로 UserMapper & UserService 2종세트 별도 준비
4, LoginCtrl
@RequestMapping("/login/")
@Controller
public class LoginCtrl {
@Autowired
private AdminService adminService;
@Autowired
private UserService userService;
@Autowired
HttpSession session;
@GetMapping("loginForm") //로그인 페이지 진입
public String loginForm() {
System.out.println("loginForm");
return "login/loginForm";
}
@PostMapping("login") //로그인 정보 제출
public String login(LoginVo vo) {
System.out.println("login");
String username = vo.getUsername();
String password = vo.getPassword();
String loginType = vo.getLoginType();
boolean authenticated;
if ("admin".equals(loginType)) { //관리자 로그인 시
authenticated = adminService.authenticate(username, password); //관리자 로그인 정보 비교
if (authenticated) { // 관리자 로그인 성공, count > 0
//프론트에 username, loginType 전달
session.setAttribute("username", username);
session.setAttribute("loginType", loginType);
System.out.println("관리자 로그인 성공: " + username+" / loginType: "+loginType);
return "redirect:/prd/prdList"; //관리자 로그인 후 상품관리 페이지로 이동
}
} else if ("user".equals(loginType)) { //사용자 로그인 시
authenticated = userService.authenticate(username, password); //사용자 로그인 정보 비교
if (authenticated) { //사용자 로그인 성공, count > 0
//프론트에 username, loginType 전달
session.setAttribute("username", username);
session.setAttribute("loginType", loginType);
System.out.println("사용자 로그인 성공: " + username+" / loginType: "+loginType);
return "redirect:/prd/prdList4user"; //사용자 로그인 후 전체상품 페이지로 이동
}
}
// 로그인 실패 (else 필요x)
System.out.println("로그인 실패");
return "login/loginAgain"; //로그인 실패하면 다시 로그인 페이지로 이동
}
@GetMapping("logout")
public String logout() {
System.out.println("logout");
session.invalidate();
return "login/logout"; //이름만 logout이지, 실은 다시 login 유도하는 거임 ㅎ
}
}
C. Templates:
1, top.html
<html xmlns:th="http://www.thymeleaf.org">
<div align="center">
<div th:if="${session.username != null}">
<span th:utext="'<strong>' + ${session.username} + '</strong>님 환영합니다.'"></span>
</div>
</div>
...
<nav>
<!-- 1. 전체 공개 -->
<a href="/index">🏠 홈</a> 
<a href="/prd/prdList4user">🛍️ 전체 상품</a> 
<!-- 1.1. 로그인 전 (이미 로그인 중이면 출력 필요x) -->
<div th:if="${session.username == null}" style="display:inline;">
<a href="/login/loginForm">➡️ 로그인</a> 
<a href="/user/joinForm">😎 회원가입</a>
</div>
<!-- 1.1. 로그인 전 끝 -->
<!-- 1. 전체 공개 끝-->
<!-- 2. 로그인 중에만 보이는 영역 -->
<!-- 2.1. 로그인한 계정 중, admin만 볼 수 있는 영역 (user에게는 비출력) -->
<div th:if="${session.username != null and session.loginType == 'admin'}" style="display:inline;">
<div class="dropdown">
<button class="dropbtn">
<span class="dropbtn_icon">🖥️</span> 상품 관리
</button>
<div class="dropdown-content">
<a href="/prd/prdList">상품 목록</a>
<a href="/prd/addPrdForm">상품 추가</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">
<span class="dropbtn_icon">💟</span> 회원 관리
</button>
<div class="dropdown-content">
<a href="/user/userList">회원 목록</a>
</div>
</div>
</div>
<!-- 2.1. admin만 볼 수 있는 영역 끝 -->
<!-- 2.2. 로그인한 계정 중, user만 볼 수 있는 영역-->
<!-- cart, mypage 등 -->
<!-- 2.2. user만 볼 수 있는 영역 끝-->
<!-- 2.3. 로그인 중에만 보이는 영역 (admin & user 공통) -->
<div th:if="${session.username != null}" style="display:inline;">
<a href="/login/logout">⬅️ 로그아웃</a>
</div>
<!-- 2.3. 로그인 중에만 보이는 영역 (admin & user 공통) 끝 -->
<!-- 2. 로그인 중에만 보이는 영역 -->
</nav>
2, loginForm.html
<form action="/login/login" method="post">
<div class="form-container">
<div class="radio-container">
<input type="radio" id="admin" name="loginType" value="admin" checked>
<label for="admin" class="radio-label">관리자 로그인</label>
 
<input type="radio" id="user" name="loginType" value="user">
<label for="user" class="radio-label">사용자 로그인</label>
</div>
<div class="form-group">
<label for="username">ID:</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">비밀번호:</label>
<input type="password" id="password" name="password" required>
</div>
</div>
<br>
<button type="submit" class=button>로그인</button>
 
<button type="button" class=subbtn>회원가입</button>
</form>
3, loginAgain.html
ㄴ 위와 똑같고 '로그인 정보가 올바르지 않습니다. 다시 확인해주세요.' 문구만 추가
4, logout.html
ㄴ 위와 거의 똑같고 '로그아웃하셨습니다.' 문구만 대체로 씀
'회원가입' 버튼은 없애고!
D. Static:
ㄴ 드롭다운 위한 css
.dropdown{
position : relative;
display : inline-block;
}
.dropbtn{
border : none;
background-color: #85B5E1;
color : #ffffff;
padding : 5px;
width : 120px;
text-align: left;
cursor : pointer;
font-size : 16px;
line-height: 25px;
}
.dropdown-content{
display : none;
position : absolute;
z-index : 1; /*다른 요소들보다 앞에 배치*/
background-color: #85B5E1;
min-width : 120px;
}
.dropdown-content a{
display : block;
text-decoration : none;
color : #ffffff;
font-size: 16px;
padding : 5px;
line-height: 30px;
}
.dropdown-content a:hover{
background-color : rgb(174, 190, 232)
}
.dropdown:hover .dropdown-content {
display: block;
}