21.02.28~03.01 Untact프로젝트(페이징~history.back(), location.replace() 도입까지)

2021. 3. 1. 20:47JAVA/Spring & Vue APP 프로젝트(백엔드)

# NOTE

/* 랜덤 게시물 추가하는 쿼리(새로운 방식) */

INSERT INTO article
(regDate, updateDate, boardId, memberId, title, `body`)
SELECT NOW(), NOW(), FLOOR(RAND() * 2) + 1, FLOOR(RAND() * 2) + 1, CONCAT('제목_', FLOOR(RAND() * 1000) + 1), CONCAT('내용_', FLOOR(RAND() * 1000) + 1)
FROM article;
다이나믹 SQL
-<if test:조건></if>와 같이 특정 조건 등에 의해 쿼리를 동적으로 수정하도록 한 SQL
<CORS>

우리가 앱을 개발하고, 그 앱이 우리 서버와 통신하려면 CORS를 꼭 허용해주어야 한다.
기본적으로 타 사이트에서 우리 서버에 있는 데이터를 ajax로 요청하고
가져 오는 것은 막혀있다.
따라서 이것을 허용해 줄 필요가 있는데 이를 CORS허용? 이라 한다?

// CORS 허용하는 소스코드
	@Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
    
    
    
-----------------------------------------------
<codepen으로 잘 나오는지 실험>

console.clear();

$.get(
  'http://localhost:8024/usr/article/list?boardId=1',
  function(data) {
    for ( let key in data.body.articles ) {
      const article = data.body.articles[key];
      console.log(article);
      const html = `<div>ID : ${article.id}, ${article.title}</div>`;
      $('#articles').prepend(html);
    }
  },
  'json'
);
< GET vs POST 요청 방식 차이 비교 >

<1.GET의 요청 방식>
예제)
<form action="http://localhost:8024/usr/home/main" method="GET">
 <input type="hidden" name="age" value="11">
 <input type="submit"  value="전송">
</form>

http://localhost:8024/usr/home/main?age=11

GET HTTP Request(요청서)
--- Header ---
주소 : http://localhost:8024/usr/home/main
쿼리 파라미터 : age=11
---Body ---
없음

**부가정보들이 Header에 붙는다.**

---------------------------

<2.POST의 요청 방식>
예제)
<form action="http://localhost:8024/usr/home/main" method="POST">
 <input type="hidden" name="age" value="11">
 <input type="submit"  value="전송">
</form>

http://localhost:8024/usr/home/main?age=11

POST HTTP Request(요청서)
--- Header ---
주소 : http://localhost:8024/usr/home/main
쿼리 파라미터 : 없음
---Body ---
age=11

**부가정보들이 Body붙는다.**
<history.back()과 location.replace()의 차이점>

- history.back() : 바로 이전의 url로 이동, 이전 기록 유지
- location.replace() : 지정한 url로 이동, 이전 기록 삭제

# 주요 소스코드

<AuthKey 개념 도입>

@Component("beforeActionInterceptor") // 컴포넌트 이름 설정
public class BeforeActionInterceptor implements HandlerInterceptor {
	@Autowired
	private MemberService memberService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		//HttpSession session = request.getSession();
		int loginedMemberId = 0;
		Member loginedMember = null;

		String authKey = request.getParameter("authKey");

		//파라미터로 authKey가 들어왔으면
		if (authKey != null && authKey.length() > 0) {
			//authKey정보를 통해 해당 회원 검색하기
			loginedMember = memberService.getMemberByAuthKey(authKey);

			//authKey가 일치하는 회원이 없다면
			if (loginedMember == null) {
				//인증되지 않은 회원
				request.setAttribute("authKeyStatus", "invalid");
			} else {
				//authKey가 일치한다면 인증된 회원
				request.setAttribute("authKeyStatus", "valid");
				//인증된 회원의 id를 저장(=세션 만료와 상관없이 저장되는 정보)
				loginedMemberId = loginedMember.getId();
			}
		} 
		//파라미터로 authKey가 들어오지 않았다면
		else {
			//session 가져오기
			HttpSession session = request.getSession();
			request.setAttribute("authKeyStatus", "none");

			//session에 로그인 정보 저장(=세션이 만료되면 초기화되는 정보)
			if (session.getAttribute("loginedMemberId") != null) {
				loginedMemberId = (int) session.getAttribute("loginedMemberId");
				loginedMember = memberService.getMember(loginedMemberId);
			}
		}

		// 로그인 여부에 관련된 정보를 request에 담는다.
		boolean isLogined = false;
		boolean isAdmin = false;
		//int loginedMemberId = 0;
		//Member loginedMember = null;

		if (loginedMember != null) {
			//loginedMemberId = (int) session.getAttribute("loginedMemberId");
			isLogined = true;
			//loginedMember = memberService.getMember(loginedMemberId);
			isAdmin = memberService.isAdmin(loginedMemberId);
		}

		request.setAttribute("loginedMemberId", loginedMemberId);
		request.setAttribute("isLogined", isLogined);
		request.setAttribute("isAdmin", isAdmin);
		request.setAttribute("loginedMember", loginedMember);

		return HandlerInterceptor.super.preHandle(request, response, handler);
	}
}

<Admin 개념 도입>

@Controller
public class AdmMemberController {

	@Autowired
	private MemberService memberService;
	
	@RequestMapping("/adm/member/login")
	//@ResponseBody: @ResponseBody를 안하면 /WEB-INF/jsp/adm/member/login.jsp를 찾는다.
	public String doLogin() {
		return "adm/member/login";
	}
	
	@RequestMapping("/adm/member/doLogin")
	@ResponseBody
	public String doLogin(String loginId, String loginPw, HttpSession session) {
		//HttpSession session
		//servlet에서와는 달리 스프링에선 session을 바로 요청해서 가져올 수 있다.
		// ex) servlet에서는 requst를 통해 session을 요청하고 다시 HttpSession로 session 값을 가져왔었다.
		
		if (loginId == null) {
			//return new ResultData("F-1", "loginId를 입력해주세요.");
			return Util.msgAndBack("loginId를 입력해주세요.");
		}

		Member existingMember = memberService.getMemberByLoginId(loginId);

		if (existingMember == null) {
			//return new ResultData("F-2", "존재하지 않는 로그인아이디 입니다.", "loginId", loginId);
			return Util.msgAndBack("존재하지 않는 로그인아이디 입니다.");
		}

		if (loginPw == null) {
			//return new ResultData("F-1", "loginPw를 입력해주세요.");
			return Util.msgAndBack("loginPw를 입력해주세요.");
		}

		if (existingMember.getLoginPw().equals(loginPw) == false) {
			//return new ResultData("F-3", "비밀번호가 일치하지 않습니다.");\
			return Util.msgAndBack("비밀번호가 일치하지 않습니다.");
		}
		
		if ( memberService.isAdmin(existingMember) == false ) {
			//return new ResultData("F-4", "관리자만 접근할 수 있는 페이지 입니다.");
			return Util.msgAndBack("관리자만 접근할 수 있는 페이지 입니다.");
		}

		//세션에 로그인 회원 id 등록
		session.setAttribute("loginedMemberId", existingMember.getId());

		String msg = String.format("%s님 환영합니다.", existingMember.getNickname());

		//return new ResultData("S-1", String.format("%s님 환영합니다.", existingMember.getNickname()));
		return Util.msgAndReplace(msg, "../home/main");
	}
	
	@RequestMapping("/adm/member/doModify")
	@ResponseBody
	public ResultData doModify(@RequestParam Map<String, Object> param, HttpServletRequest req) {

		if (param.isEmpty()) {
			return new ResultData("F-2", "수정할 회원정보를 입력해주세요.");
		}

		int loginedMemberId = (int) req.getAttribute("loginedMemberId");
		param.put("id", loginedMemberId);

		return memberService.modifyMember(param);
	}
	
}

<login.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>

<%@ include file="../part/head.jspf"%>

<script>
	const LoginForm__checkAndSubmitDone = false;
	function LoginForm__checkAndSubmit(form) {
		if (LoginForm__checkAndSubmitDone) {
			return;
		}
		form.loginId.value = form.loginId.value.trim();
		if (form.loginId.value.length == 0) {
			alert('로그인아이디를 입력해주세요.');
			form.loginId.focus();
			return;
		}
		if (form.loginPw.value.length == 0) {
			alert('로그인비번을 입력해주세요.');
			form.loginPw.focus();
			return;
		}
		form.submit();
		LoginForm__checkAndSubmitDone = true;
	}
</script>
<section class="section-login">
	<div
		class="container mx-auto min-h-screen flex items-center justify-center">
		<div class="w-full">
			<div class="logo-bar flex justify-center mt-3">
				<a href="#" class="logo"> <span><i
						class="fas fa-people-arrows"></i></span> <span>UNTACT</span>
				</a>
			</div>
			<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mt-4"
				action="doLogin" method="POST"
				onsubmit="LoginForm__checkAndSubmit(this); return false;">
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>로그인아이디</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="text" placeholder="로그인 아이디를 입력해주세요."
							name="loginId" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>로그인비번</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border border-red rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="password"
							placeholder="로그인 비밀번호를 입력해주세요." name="loginPw" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>로그인</span>
					</div>
					<div class="p-1">
						<input
							class="bg-blue-500 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded"
							type="submit" value="로그인" />
					</div>
				</div>
			</form>
		</div>
	</div>
</section>

<%@ include file="../part/foot.jspf"%>

<history.back(), location.replace() 도입>

/* Util.java */

	public static String msgAndBack(String msg) {
		StringBuilder sb = new StringBuilder();
		sb.append("<script>");
		sb.append("alert('" + msg + "');");
		sb.append("history.back();");
		sb.append("</script>");

		return sb.toString();
	}

	public static String msgAndReplace(String msg, String url) {
		StringBuilder sb = new StringBuilder();
		sb.append("<script>");
		sb.append("alert('" + msg + "');");
		sb.append("location.replace('" + url + "');");
		sb.append("</script>");

		return sb.toString();
	}