로그인 로그아웃까지 다 했으니, 이제 modify부터 구현을 시작해보자.
우선 modify 페이지와 doModify 코드 수정.
@RequestMapping("/usr/article/modify")
public String showModify() {
return "/usr/article/modify";
}
@RequestMapping("/usr/article/doModify")
@ResponseBody
public String doModify(HttpServletRequest req, int id, String title, String body) {
Rq rq = (Rq) req.getAttribute("rq");
Article article = articleService.getArticleById(id);
if (article == null) {
return Ut.jsReplace("F-1", Ut.f("%d번 게시글은 없습니다", id), " / ");
}
ResultData userCanModifyRd = articleService.userCanModify(rq.getLoginedMemberId(), article);
if (userCanModifyRd.isFail()) {
return Ut.jsHistoryBack(userCanModifyRd.getResultCode(), userCanModifyRd.getMsg());
}
if (userCanModifyRd.isSuccess()) {
articleService.modifyArticle(id, title, body);
}
article = articleService.getArticleById(id);
return Ut.jsReplace("S-1", Ut.f("%d번 게시글이 수정되었습니다.", id), "/usr/article/detail?id=" + id);
}
수정을 위해서는 새로운 창이 필요하니 따로 경로를 지정해주고 기능은 기능만 하도록 바꿨다. 기존에 만들었던 login을 참고해서.
JSP.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="pageTitle" value="게시물 수정하기"></c:set>
<%@ include file="../common/head.jspf"%>
<section class="modify-article-section">
<form action="doModify" method="post">
<input type="hidden" name="id" value="${article.id}" />
<div class="form-group">
<label for="title">제목</label>
<input type="text" id="title" name="title" value="${article.title}" required />
</div>
<div class="form-group">
<label for="body">내용</label>
<textarea id="body" name="body" rows="10" required>${article.body}</textarea>
</div>
<div class="form-group">
<input type="submit" value="수정하기" />
</div>
</form>
<div class="actions">
<a href="detail?id=${article.id}" class="btn">취소</a>
</div>
</section>
</body>
</html>
CSS
/* 게시물 수정 스타일 */
.modify-article-section {
width: 60%;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
border: 1px solid #333;
border-radius: 5px;
background-color: #1e1e1e;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.modify-article-form {
width: 80%;
}
.modify-article-section input[type="text"],
.modify-article-section textarea {
width: 100%;
padding: 12px;
background-color: #333;
border: 1px solid #444;
border-radius: 4px;
color: #e0e0e0;
font-size: 16px;
box-sizing: border-box;
}
.modify-article-section .form-group {
margin-bottom: 20px;
}
.modify-article-section input[type="submit"] {
width: 100%;
padding: 12px;
background-color: #2a9d8f;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.modify-article-section input[type="submit"]:hover {
background-color: #219080;
}
.actions {
text-align: right;
margin-top: 20px;
}
.actions .btn {
padding: 10px 20px;
background-color: #264653;
color: #fff;
border-radius: 4px;
text-decoration: none;
margin: 0 5px;
}
.actions .btn:hover {
background-color: #1b3a4b;
}
해서 화면은 이렇게 나온다.
입력후, 수정하기를 클릭하니까 id 파라미터를 전달하지 않아서 오류가 뜬다.
그래서 코드를 다시 손봤다. 생각해보니까 화면을 보겠다고 modify에서 아무것도 구현한게 없었다.
@RequestMapping("/usr/article/modify")
public String showModify(Model model, int id) {
Article article = articleService.getArticleById(id);
if (article == null) {
return "redirect:/usr/article/list";
}
model.addAttribute("article", article);
return "usr/article/modify";
}
Model과 id로 파라미터가 전달 될 수 있게 하고,
<section class="modify-article-section">
<form action="doModify?id=${article.id}" method="post" class="modify-article-form">
<input type="hidden" name="id" value="${article.id}" />
<div class="form-group">
<label for="title">제목</label>
<input type="text" id="title" name="title" value="${article.title}" required />
</div>
<div class="form-group">
<label for="body">내용</label>
<textarea id="body" name="body" rows="10" required>${article.body}</textarea>
</div>
<div class="form-group">
<input type="submit" value="수정하기" />
</div>
</form>
<div class="actions">
<a href="detail?id=${article.id}" class="btn">취소</a>
</div>
</section>
여기서도 경로 지정에 article.id를 추가해줬다.
이제 게시물 수정하기를 클릭하면
원래 내용도 같이 불러오고,
1234 1234 로 수정을 하면,
알림창과 함께,
지정해준 detail 경로도 다시 돌아오게 만들었다. 수정도 잘 된 모습이다.
혹시 모르니까 DB상으로도 수정이 되었는가 확인해보자.
잘 됐다!
이제 write 기능 구현!!
write 관련 코드를 수정중에 가만 생각해보니까 글 작성 버튼이 없으면 접근을 못하니까 우선은 list에서 글 작성 버튼을 만들기로 했다.
저번의 수정 삭제 버튼과, 로그인 로그아웃 버튼이 바뀌는 것을 활용해서 로그인 하면 글 작성 버튼이 보이고, 로그인 상태가 아니라면 글 작성 버튼은 보이지 않도록 만드는게 좋을 것 같아서 그렇게 구현해 보았다.
<c:if test="${rq.isLogined()}">
<div style="text-align: right; margin-bottom: 10px; margin-right: 20px;">
<a href="write" class="btn">게시물 작성하기</a>
</div>
</c:if>
코드는 이렇고 css 도 버튼 스타일로 따로 만들어서 처리했다. 우선 로그인 했을 때 보이는가 확인해보자.
잘 보이니까 로그아웃해보자.
로그아웃하면 글 작성 버튼이 사라진다.
일단 초석은 다졌으니, 본론으로 들어가서 코드를 보자.
@RequestMapping("/usr/article/write")
public String showWrite(Model model) {
return "usr/article/write";
}
@RequestMapping("/usr/article/doWrite")
@ResponseBody
public String doWrite(HttpServletRequest req, String title, String body) {
Rq rq = (Rq) req.getAttribute("rq");
if (Ut.isEmptyOrNull(title)) {
return Ut.jsHistoryBack("F-1", "제목을 입력해주세요");
}
if (Ut.isEmptyOrNull(body)) {
return Ut.jsHistoryBack("F-2", "내용을 입력해주세요");
}
ResultData writeArticleRd = articleService.writeArticle(rq.getLoginedMemberId(), title, body);
int id = (int) writeArticleRd.getData1();
Article article = articleService.getArticleById(id);
return Ut.jsReplace(writeArticleRd.getResultCode(), writeArticleRd.getMsg(), "생성된 게시글");
}
일단은 이렇게 코드를 고쳤고
JSP 구성은 이렇게.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="pageTitle" value="게시물 작성하기"></c:set>
<%@ include file="../common/head.jspf"%>
<section class="article-section">
<form action="doWrite?id=${article.id}" method="post" class="article-form">
<input type="hidden" name="id" value="${article.id}" />
<div class="form-group">
<label for="title">제목</label>
<input type="text" id="title" name="title" value="${article.title}" required />
</div>
<div class="form-group">
<label for="body">내용</label>
<textarea id="body" name="body" rows="10" required>${article.body}</textarea>
</div>
<div class="form-group">
<input type="submit" value="수정하기" />
</div>
</form>
<div class="actions">
<a href="detail?id=${article.id}" class="btn">취소</a>
</div>
</section>
</body>
</html>
modify랑 흡사할테니 그냥 modify의 class도 죄다 저렇게 바꿨다.
기능이 잘 되는가 한번 보자.
기능은 잘 되는데,
경로지정이 잘못됐나보다. 고쳐보자.
아 여기 리턴 값을 제대로 안고쳤다.
@RequestMapping("/usr/article/doWrite")
@ResponseBody
public String doWrite(HttpServletRequest req, String title, String body) {
Rq rq = (Rq) req.getAttribute("rq");
if (Ut.isEmptyOrNull(title)) {
return Ut.jsHistoryBack("F-1", "제목을 입력해주세요");
}
if (Ut.isEmptyOrNull(body)) {
return Ut.jsHistoryBack("F-2", "내용을 입력해주세요");
}
ResultData writeArticleRd = articleService.writeArticle(rq.getLoginedMemberId(), title, body);
int id = (int) writeArticleRd.getData1();
Article article = articleService.getArticleById(id);
return Ut.jsReplace(writeArticleRd.getResultCode(), writeArticleRd.getMsg(),"/usr/article/detail?id=" + id);
}
이제 다시 잘 되는가 확인해보자.
작성한 글로 잘 넘어간다. DB는 아까 확인했으니까 넘어가자.
다음으로는 회원가입 기능을 만들어보자. 일단 아까처럼 main페이지, 가 아니고 head에 로그인 상태가 아닐시에 login 버튼 옆에 join 버튼을 만들어보자.
<c:if test="${!rq.isLogined()}">
<li><a class="mr-4" href="../member/login">LOGIN</a></li>
<li><a class="hover:underline" href="../member/join">JOIN</a></li>
</c:if>
<c:if test="${rq.isLogined()}">
<li><a onclick="if(confirm('로그아웃 하시겠습니까?') == false) return false;" class="mr-4"
href="../member/doLogout">LOGOUT</a></li>
</c:if>
생각해보니까 그냥 다 만들어놔서 login 아래에 쓰기만 하면 된다.
mr-4도 수정해주고. 화면을 보면
로그인을 하면,
보이지 않는다.
@RequestMapping("/usr/member/join")
public String showJoin() {
return "/usr/member/join";
}
화면 구성하기전에 경로로 하나 만들어주고 JSP 생성.
일단 로그인 JSP를 가져와서 약간만 수정하고 화면이 잘 나오는지 확인해보자.
화면 구성을 하면서 css가 너무 복잡해지는 것 같아서 GPT한테 중복코드나 충돌하는 것들 다 정리해달라고 하고 login과 join의 스타일은 어차피 겹칠테니 서로를 통합시켰다.
그래서 join의 화면은 이렇게 보인다.
이제는 기능만 똑바로 구현되게끔 만들면 된다.
@RequestMapping("/usr/member/join")
public String showJoin() {
return "/usr/member/join";
}
@RequestMapping("/usr/member/doJoin")
@ResponseBody
public String doJoin(HttpServletRequest req, String loginId, String loginPw,
String name, String nickname, String cellphoneNum, String email) {
Rq rq = (Rq) req.getAttribute("rq");
if (Ut.isEmptyOrNull(loginId))
return Ut.jsHistoryBack("F-1", Ut.f("아이디를 입력해주세요."));
if (Ut.isEmptyOrNull(loginPw))
return Ut.jsHistoryBack("F-2", Ut.f("비밀번호를 입력해주세요."));
if (Ut.isEmptyOrNull(name))
return Ut.jsHistoryBack("F-3", Ut.f("이름을 입력해주세요."));
if (Ut.isEmptyOrNull(nickname))
return Ut.jsHistoryBack("F-4", Ut.f("닉네임를 입력해주세요."));
if (Ut.isEmptyOrNull(cellphoneNum))
return Ut.jsHistoryBack("F-5", Ut.f("전화번호를 입력해주세요."));
if (Ut.isEmptyOrNull(email))
return Ut.jsHistoryBack("F-6", Ut.f("이메일을 입력해주세요."));
ResultData doJoinRd = memberService.doJoin(loginId, loginPw, name, nickname, cellphoneNum, email);
if (doJoinRd.isFail()) {
return Ut.jsHistoryBack(doJoinRd.getResultCode(), doJoinRd.getMsg());
}
Member member = memberService.getMemberById((int) doJoinRd.getData1());
return Ut.jsReplace("S-1", "회원가입이 완료되었습니다.", "/usr/member/login");
}
우선은 이렇게 만들어줬고, 비밀번호 확인과 입력한 비밀번호가 일치하는지 하지 않는지에 대한 로직만 처리하면 된다.
JSP에서 처리하는 것이 편할 것 같아서 script태그를 써서 만들었다.
<section class="section">
<form action="../member/doJoin" method="POST">
<table class="form-table">
<tbody>
<tr>
<th>아이디</th>
<td><input name="loginId" autocomplete="off" type="text"
placeholder="아이디를 입력해주세요" required /></td>
</tr>
<tr>
<th>비밀번호</th>
<td><input name="loginPw" autocomplete="off" type="text"
placeholder="비밀번호를 입력해주세요" required /></td>
</tr>
<tr>
<th>비밀번호 확인</th>
<td><input name="loginPwConfirm" autocomplete="off" type="text"
placeholder="비밀번호를 입력해주세요" required /></td>
</tr>
<tr>
<th>이름</th>
<td><input name="name" autocomplete="off" type="text"
placeholder="이름을 입력해주세요" required /></td>
</tr>
<tr>
<th>닉네임</th>
<td><input name="nickname" autocomplete="off" type="text"
placeholder="닉네임을 입력해주세요" required /></td>
</tr>
<tr>
<th>전화번호</th>
<td><input name="cellphoneNum" autocomplete="off" type="text"
placeholder="전화번호를 입력해주세요" required /></td>
</tr>
<tr>
<th>이메일</th>
<td><input name="email" autocomplete="off" type="email"
placeholder="이메일을 입력해주세요" required /></td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="회원가입" /></td>
</tr>
</tbody>
</table>
</form>
</section>
<script>
document.getElementById("joinForm").addEventListener("submit", function(event) {
const pw = document.getElementById("loginPw").value;
const pwConfirm = document.getElementById("loginPwConfirm").value;
if (pw !== pwConfirm) {
alert("비밀번호가 일치하지 않습니다. 다시 확인해주세요.");
event.preventDefault();
}
});
</script>
작동확인을 해보자.
패스워드는 일부러 보이게 해뒀다.
회원가입 버튼을 누르면.
안되넹? 뭐가 문제인지 다시 확인하거나 아니면 타 사이트 처럼 입력이 처음부터 일치하지 않으면 막아두는 걸로 구현을 해볼까?
이건 내 능력으로 안될 것 같아서 gpt한테 상세하게 구현해달라고 부탁해서 만들었다.
우선은 테스트하면서 생긴 이상한 회원데이터를 삭제하고 다시 테스트데이터를 만들었고, 수정된 JSP 부터 보고 분석해보자.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="pageTitle" value="회원가입"></c:set>
<%@ include file="../common/head.jspf"%>
<hr />
<section class="section">
<form action="../member/doJoin" method="POST" onsubmit="return validateForm();">
<table class="form-table">
<tbody>
<tr>
<th>아이디</th>
<td><input name="loginId" autocomplete="off" type="text"
placeholder="아이디를 입력해주세요" required /></td>
</tr>
<tr>
<th>비밀번호</th>
<td><input name="loginPw" id="loginPw" autocomplete="off"
type="password" placeholder="비밀번호를 입력해" required /></td>
</tr>
<tr>
<th>비밀번호 확인</th>
<td><input name="loginPwConfirm" id="loginPwConfirm"
autocomplete="off" type="password" placeholder="비밀번호를 다시 입력해"
required /></td>
</tr>
<tr>
<th>이름</th>
<td><input name="name" autocomplete="off" type="text"
placeholder="이름을 입력해주세요" required /></td>
</tr>
<tr>
<th>닉네임</th>
<td><input name="nickname" autocomplete="off" type="text"
placeholder="닉네임을 입력해주세요" required /></td>
</tr>
<tr>
<th>전화번호</th>
<td><input name="cellphoneNum" autocomplete="off" type="text"
placeholder="전화번호를 입력해주세요" required /></td>
</tr>
<tr>
<th>이메일</th>
<td><input name="email" autocomplete="off" type="email"
placeholder="이메일을 입력해주세요" required /></td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="회원가입" /></td>
</tr>
</tbody>
</table>
</form>
</section>
<script>
const pwField = document.getElementById("loginPw");
const pwConfirmField = document.getElementById("loginPwConfirm");
const submitBtn = document.getElementById("submitBtn");
function checkPasswords() {
if (pwField.value === pwConfirmField.value) {
pwField.style.borderColor = "green";
pwConfirmField.style.borderColor = "green";
submitBtn.disabled = false;
} else {
pwField.style.borderColor = "red";
pwConfirmField.style.borderColor = "red";
submitBtn.disabled = true;
}
}
function validateForm() {
if (pwField.value !== pwConfirmField.value) {
alert("비밀번호가 일치하지 않습니다. 확인해주세요.");
return false; // 폼 제출을 막습니다.
}
return true;
}
pwField.addEventListener("input", checkPasswords);
pwConfirmField.addEventListener("input", checkPasswords);
</script>
</body>
</html>
여기서 우리가 주목해야될 것은 당연, from 태그에서 onsubmit="return validateForm();" 이 부분과 script 태그다.
보면 도큐먼트 어쩌구 되어있는데 이건 이거다.
document.getElementById는 자바스크립트에서 제공하는 표준 메서드이고. 이 메서드는 HTML 문서에서 특정 ID를 가진 요소를 선택하고, 그 요소를 나타내는 객체를 반환한다. 그래서 저렇게 사용되어있는 것이고 나머지는 우리가 하는 함수생성 후 구현해 둔 것이다. 찬찬히 살펴보면 뭘 하고 싶어서 만들었는지 이해할 수 있을 것이다.
그리고 다시 봐야될 것은 from 태그에서 onsubmit="return validateForm();"인데 서브밋을 할 때 이 함수가 작동하면서 비밀번호가 일치하는지 일치하지 않는지 판단하고 되돌아가게 하거나 승인한다.
회원가입이 은근 까다로워서 조금 헤멧다. 일단 구현이 제대로 다 되는지 확인해보자. 빈칸은 어차피 required속성으로 html 단계에서 막아버리기 때문에 java까지 넘길 수 없다.
그럼 우리가 확인할 것은 비번 일치하지 않을 때 색 변화와 일치하지 않을 때 validateForm함수가 잘 작동하는지, 모든 조건이 다 맞을 때 올바르게 db에 들어가는지 이다. test3으로 만들 것이고 처음엔 비번 일치하지 않게 만들어보겠다.
함수 잘 작동하고, 색도 일치하지 않으니까 빨간색으로 표시한다.
일치할 때는,
잘뜨고 회원가입을 누르면, 회원가입 완료되었습니다 알림까지 잘 뜬다.
로그인도 잘 되고, DB상으로도,
김영희씨가 잘 들어왔다.
글 작성까지 문제없이 된다!
일단 게시물은 여기까지 하겠다.
'Coding History' 카테고리의 다른 글
국비 지원 IT(웹앱개발) 취업반 강의 51일차 (Spring) (0) | 2024.08.19 |
---|---|
프로그래머스 문제풀이 (0) | 2024.08.18 |
국비 지원 IT(웹앱개발) 취업반 강의 50일차 (Spring) (0) | 2024.08.18 |
Spring Login Logout 구현 (0) | 2024.08.13 |
국비 지원 IT(웹앱개발) 취업반 강의 48일차 (Spring) (1) | 2024.08.12 |