Programming/스프링(spring) - Enterprise

스프링(spring)/ +더하기 1(검색 시스템과 알림 시스템)

esoog Polaris 2023. 8. 2. 23:01
반응형

1. 검색하는 방법에 대해 생각해보다가 검색은 한 번에, 출력은 원하는 것만 하는 것이 편해 보였다.

그래서 단순히 하나의 키워드로 데이터베이스 검색 시스템 다듬어 보자. 먼저 임포트 시킬 것들.

부트스트랩과 제이쿼리

<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<!-- 부가적인 테마 -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<!-- jquery 사용 -->
<script
	src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

 

 

그리고, 입력란과 버튼을 하나 만든다. 

<input type="text" class="form-control" placeholder="검색어를 입력해 주세요" name="key"
    aria-label="Recipient's username" aria-describedby="basic-addon2">
<button class="btn btn-info" id="searchBtn">검색</button>

 

 

그리고 자바스크립트로 버튼 행위 제어

 

	    document.getElementById("searchBtn").onclick = function() {
	        let keyword = document.getElementsByName("key")[0].value;
	        if (keyword.trim() === "") {
	            return;
	        } else {
	            // 검색어가 입력된 경우 검색 기능 수행
	            let url = "/board/search?keyword=" + keyword;
	            location.href = encodeURI(url);
	        }
	    };

* 빈칸이면 아무 동작도 안하게, 아니면 작동 로직 구현.

 

 

 

 

2. 그럼 js로 보내진 search url을 처리해줘야 한다.

해당 Controller에서 검색 매핑을 시켜 중간 처리를 한다.

 

// 검색
	@RequestMapping(value = "/search", method = RequestMethod.GET)
	public void getSearch(Model model, @RequestParam(value = "keyword", required = false, defaultValue = "") String keyword) throws Exception {
		
	    List<BoardVO> list = service.search(keyword);
	    model.addAttribute("list", list);
	}

 

 

 

 

3. 다음으로 곧장 따르는 DAO와 Service~

 

//검색
public List<BoardVO> search(String keyword) throws Exception;

 

 

그리고 구현 DAOImpl 파일에는

 

// 검색
@Override
public List<BoardVO> search(String keyword) throws Exception {
    HashMap<String, Object> data = new HashMap<>();
    data.put("keyword", keyword);
    return sql.selectList(namespace + ".search", data);
}

*많은 정보가 들어와서 단순히 배열에 넣으면 찾기 곤란해질 수 있다. 그래서 HasMap으로 키:값 쌍으로 넣어둔다.

 

 

그리고 구현 ServiceImpl 파일은 예상하겠지만, DAO로 넘겨주지만 하면 된다.

 

@Override
public List<BoardVO> search(String keyword) throws Exception {
	 return  dao.search(keyword);
}

 

 

 

 

 

4. 이제 데이터를 실제로 처리 상호작용하는 매퍼로~

 

<!-- 검색 -->
<select id="search" parameterType="hashMap" resultType="com.project.model.BoardVO">
    SELECT idx, title, text, nick, date, hcount
    FROM board
    WHERE title LIKE CONCAT('%', #{keyword}, '%')
        OR text LIKE CONCAT('%', #{keyword}, '%')
    ORDER BY idx DESC
</select>

* 조건에 CONCAT('%', #{keyword}, '%')을 써야 '%'를 일반 명령어로 연결 시킨다.

단순히 LIKE '%#{keyword}%'는 문자 %를 검색어로 포함하여 검색한다.

원하는 조건을 묶어 두면 안에서 키워드는 모두 검색 된다.

 

 

 

 


1. 이번에는 알림 시스템. 예를들어, 비밀번호나, 로그인 동작에 따른 브라우저와 상호작용하는 알림이 필요할 때가 있다.

사실 회원 시스템이 있는 웹에서는 어느 페이지든 로그인 정보 혹은 로그인이 가능한 링크가 존재하는 게 편해보인다.

그래서 편하게 상단에 고정시켜 사용하면 좋을 듯 하다. 로그인, 로그, 마이페이지 등.

 

	<div class="logBox">
		<div class="log">
			<c:set var="member" value="${sessionScope.member}" />
			<!-- 세션스코프는 서블릿에서 제공하는 스코프 중 하나인데 서블릿에서 이미 저장되어 있다. -->
			<c:if test="${not empty member}">
				<p class="userLog">${member.id}님환영합니다.</p>
				<button class="btn btn-info" id="logoutBtn">로그아웃</button>
			</c:if>
			<div class="update">
				<a href="/account/update">
					<button class="btn btn-info" id="updateBtn">마이페이지</button>
				</a>
			</div>
		</div>

		<div class="login">
			<a href="/account/login">
				<button class="btn btn-info" id="loginBtn">로그인</button>
			</a>
		</div>
	</div>

* 로그 안에서이뤄지는 로그아웃과 회원정보수정 페이지, 그리고 로그인 까지 묶어서~

 

 

그리고 자바스크립트로 행위 제어~(jquery)

 

		$(document).ready(function() {
		    $("#logoutBtn").on("click", function() {
		        // 로그아웃 버튼 클릭 시 확인 창 띄우기
		        if (confirm("로그아웃하시겠습니까?")) {
		            // 확인 버튼을 누르면 /logout 경로로 POST 요청을 보냄
		            $.post("/logout", function(data) {
		                // 로그아웃 성공 시 홈으로 리다이렉트
		                location.href = "/";
		            }).fail(function() {
		                // 로그아웃 실패 시 에러 메시지를 표시할 수 있음
		                alert("로그아웃 실패!");
		            });
		        }
		    });
		});

		$(document).ready(function() {
			// 로그인 버튼과 로그아웃 버튼을 각각 변수로 가져옵니다.
			var loginBtn = $("#loginBtn");
			var logoutBtn = $("#logoutBtn");
			var updateBtn = $("#updateBtn");

			// 로그인 상태인 경우
			if (${not empty member}) {
				loginBtn.hide(); // 로그인 버튼 숨기기
				logoutBtn.show(); // 로그아웃 버튼 보이기
				updateBtn.show();
			} else {
				loginBtn.show(); // 로그인 버튼 보이기
				logoutBtn.hide(); // 로그아웃 버튼 숨기기
				updateBtn.hide();
			}
		});

 

 

 

 

 

 

2. 다음으로는 세션말고 데이터베이스와 상호작용 하여 어떠한 메시지가 필요할 경우.

jsp파일에서 jstl을 사용하여 메시지를 건네 받으면 되는데; 

 

<c:if test="${msg == false}">
    <script>
        $(document).ready(function() {
            alert("로그인 실패! 아이디와 비밀번호 확인해주세요.");
        });
    </script>
</c:if>

* jstl문법 안에 스크립트를 이렇게 쓰면 된다. 신기하다. 

* ${msg == false} 이 문법도 좀 특이한데; 서버사이드에서 조건 일치 검사하는 문법이란다.

또는 ajax를 사용하여서도 서버사이드와 소통할 수 있는데, 이건 조금 더 긴 느낌이 있다.

 

 

근데 이 "msg"메시지를 어디서 만드냐 하면~ 예를들어 어떤 form을 post로 전송하고, 컨트롤러 단에서 처리한다면, 

 

	@PostMapping("/account/login")
	public String login(UserVO vo, HttpServletRequest req, RedirectAttributes rttr) throws Exception{
		
		HttpSession session = req.getSession();
		UserVO login = userService.login(vo);

		if (login == null || !pwdEncoder.matches(vo.getPwd(), login.getPwd())) {
			rttr.addFlashAttribute("msg", false);
	        return "redirect:/account/login"; // 리다이렉트하여 로그인 페이지로 돌아감
		}else {
			session.setAttribute("member", login);
		}
		// member라는 이름으로 login 객체를 저장함.
		
		return "redirect:/";
	}

*예전에 한 번 공부한 적 있는데, RedirectAttributes rttr파라미터를 설정해서, 
직접적으로 HTTP로 데이터를 커스텀해서 전송할 수있다.

rttr.addFlashAttribute("msg", false); 이렇게 하면, 리다이렉트시 rttr 데이터 정보가 포함되어 보내진다.

"msg"라는 이름으로 false값을 보낸다.

그러니까 어떠한 정보를 처리하여 결과를 모델 객체로서 보내는 Model(데이터 객체)과는 다른 파라미터이다.

객체의 타입을 신경 쓸 필요 없이 제어가 가능해진다.~

 

 

 

 

3. 다음으로는 서버와 비동기적으로 상호작용 하려면 언젠가 ajax와 마주친다.

간단히 살펴보자.

 

*먼저 제일 위의 $(document).ready(function() { 는

HTML이 모두 준비된 후에 실행되게 하는 함수다. 이건 동기적인 통신인데, 데이터를 주고 받는 작업의 경우 시간차가 있기에 안전하게 이렇게 사용한다. 이 함수를 사용 않고 나머지 함수들을 쓰면, 비동기적으로 통신이 이뤄진다.

 

<script type="text/javascript">
		$(document).ready(function() {
			var idChecked = false;
			var nickChecked = false;
			
			// 취소 버튼
			$(".cancle").on("click", function() {
				location.href = "/";
			});
			
			// 모든 입력란에서 스페이스바 무시
			$("input").on("keydown", function(e) {
				if (e.keyCode === 32) {
					e.preventDefault();
				}
			});

			/* 아이디와 닉네임 중복확인 */
			$("#checkIdBtn").click(function() {
				var id = $("#id").val().trim();
				if (id === "") {
					alert("아이디를 입력해주세요.");
					$("#id").focus();
					return;
				}
				$.ajax({
					type : "POST",
					url : "/checkId",
					data : {
						id : id
					},
					success : function(response) {
						if (response == "available") {
							alert("사용 가능합니다.");
							$("#id").prop("readonly", true); // 읽기 전용으로 만들기
							idChecked = true;
						} else {
							alert("중복되는 아이디입니다.");
							idChecked = false;
						}
					},
					error : function() {
						alert("서버와 통신 중 오류가 발생했습니다.");
					}
				});
			});

			$("#checkNickBtn").click(function() {
				var nick = $("#nick").val().trim();
				if (nick === "") {
					alert("닉네임을 입력해주세요.");
					$("#nick").focus();
					return;
				}
				$.ajax({
					type : "POST",
					url : "/checkNick",
					data : {
						nick : nick
					},
					success : function(response) {
						if (response == "available") {
							alert("사용 가능합니다.");
							$("#nick").prop("readonly", true); // 읽기 전용으로 만들기
							nickChecked = true;
						} else {
							alert("중복되는 닉네임입니다.");
							nickChecked = false;
						}
					},
					error : function() {
						alert("서버와 통신 중 오류가 발생했습니다.");
					}
				});
			});

			// 읽기 전용 만들기
			$("#reCheckId").click(function() {
				$("#id").prop("readonly", false); // 읽기 전용 해제하기
				idChecked = false;
			});
			$("#reCheckNick").click(function() {
				$("#nick").prop("readonly", false); // 읽기 전용 해제하기
				nickChecked = false;
			});

			// 최종 점검 로직
			$("#submit").on("click", function() {

				var id = $("#id").val().trim();
				if (id === "") {
					alert("아이디를 입력해주세요.");
					$("#id").focus();
					return false;
				}

				var password = $("#pwd").val().trim();
				if (password === "") {
					alert("패스워드를 입력해주세요.");
					$("#pwd").focus();
					return false;
				}
				var confirmPassword = $("#confirmPwd").val().trim();
				if (password !== confirmPassword) {
					alert("패스워드가 일치하지 않습니다.");
					return false;
				}

				var nick = $("#nick").val().trim();
				if (nick === "") {
					alert("닉네임을 입력해주세요.");
					$("#nick").focus();
					return false;
				}

				var email = $("#email").val().trim();
				if (email === "") {
					alert("이메일 입력해주세요.");
					$("#email").focus();
					return false;
				}

				if (idChecked == true && nickChecked == true) {
				} else {
					alert("아이디와 닉네임 중복 확인을 완료해야 합니다.");
					return false;
				}
			});
		})
	</script>

 

 

*jquery 함수 기본 형태는

$("선택자id").함수(function(){ 

    실행문

});

*trim()은 공백을 제거하고 문자를 구분하기 위함이다.

 

 

* 그리고 위에서는 순수자바스크립트 말고 제이쿼리로 ajax를 사용했는데,

ajax 기본 형태는 이렇다.

$.ajax({
    type: 'GET',
    url: '/example',
    success: function(responseData) {
      // 응답 데이터를 처리하는 로직을 작성
    },
    error: function(xhr, status, error) {
      // 오류 처리 로직을 작성
    }

});

 

확인. 나이스.

 

 

 

 

728x90