쿠키 생성하기
첫 번째 파라미터: 이름(Map의 key - 식별, 유일무이)
두 번째 파라미터: 값(문자열만 가능함)
생성했다고 해서 실제 로컬 PC에 내려가지 않음
Cookie c = new Cookie(”cmem_id”, “tomato”);
// 쿠키 타임 설정이 가능함(ex. 장바구니 보관은 n일동안 유지)
c.setMaxAge(60); // 단위는 초단위이다
// 쿠키의 적용 범위
c.setPath(); // 생략 가능
요청
http://localhost:9000/member/cindex.jsp
upmu[0] = member
upmu[1] = cindex.jsp
응답
void → doGet(req, res), doPost(req, res)
String → “redirect:upmu[0] 업무명/upmu[1] 메소드이름 or 페이지이름
ModelAndView
pageMove[0] = member (upmu[0]과 같음)
pageMove[1] = memberList or memberList.jsp or XXX.st3(또다른 URL)
글쓰기를 할 때 예시
action(select) → jsp → 글쓰기버튼 → jsp(폼) → 저장 → action(insert) → 성공1, 실패0 → 성공하면 action(select) →(forward사용)→ jsp
글쓰기
jsp(폼)
→ action(insert)
→ action(select: memberList.st3)
→(forward: pojo는 ModelAndView[스프링], setAttribute, 스프링부트는 Model, ModelMap, ViewResolver)
→ jsp
글수정 - 글 상세 보기에서 시작
action(select 1건)
→ 수정버튼
→ jsp(입력화면)
→ action(update)
→ action(select)
→ jsp
글삭제 - 글 상세 보기에서 시작
삭제버튼
→ jsp(비밀번호 비교-오라클에서 가져온 비번과 상세보기 화면에서 가져온 비번 비교)
→ action(delete from 테이블명 where board_no=:x)
→ action(select)
→ jsp(새로고침)
→ window.location.href=”a.jsp”; (새로고침됨)
→ jsp는 SPA가 아니지만 React는 SPA
→ useState()덕분에 return으로 화면출력(State가 바뀔 때마다 return이 다시 호출, 화면이 그려짐, 리렌더링)
리액트의 핵심 컨셉
페이지를 여러 개의 컴포넌트로 쪼갠다
HackerNewsPage.jsx → HackerNewsList.jsx(select n건) → map을 사용해 for문 돌림 → HackerNewsRow.jsx(select 1건) → 출력 return(<><h3></h3></>);
useState가 바뀌면 리액트는 비교알고리즘을 통해서 dataset을 비교하고 바뀐 값이 있으면 return을 다시 호출한다
state가 변하면 화면도 리렌더링 된다
useState(0)
useState({}) - 객체(1건) select * from member where mem_id=tomato → 자바에선 MemberVo, Map<String, Object>
useState([]) - 객체배열[{}, {}, {}]
글조회 - 글목록에서 시작
키워드 입력 → 조회버튼 → action → jsp
SELECT
mem_id, mem_name
FROM BOOK_MEMBER
WHERE mem_id =:x
AND mem_pw =:y;
--파라미터의 개수 파악(2개)
--아이디, 비번
request.getParameter(); 반복돼서 불편
→ HashMapBinder 공통코드 작성함
→ (@RequestParam Map<String, Object>) 사용자가 입력한 값을 스프링 컨테이너가 자동으로 Map에 담아주는 어노테이션(해당 코드를 파라미터에 넣어줌)
프로젝트 sitemap
루트페이지 / → root를 들어올 때 인증절차 필요(백엔드에서 처리해 줌)
→ AuthController(쿠키-그냥 제공됨, 세션-req필요)
리액트기준
index.html → <div>만 가지고 있음
index.js
<BrowserRouter>
<App /> // 실제 시작이 되는 페이지
<BrowserRouter />
그다음 App.jsx 연결됨
<Routes>
<Route path="URL패턴" exact="true" />
</Routes>
@GetMapping(), @PostMapping() → 파라미터 안에 들어오는 것이 path와 같은 것
exact에 true가 있으면 URL이 정확히 일치해야 함
Route를 통해 페이지 이동(jsx)
→ return가지고있고, 그 return으로 화면 출력됨
→ 화면전환을 위해서는 react-router-dom이 제공하는 <Link>을 쓰든지 UseNavigate를 사용해야 함
→ location.href 같은 거 쓰면 안 됨!! SPA를 위해 navigate 사용해야 한다!(useNavigate는 부분적 화면전환용!)
로그인에 필요한 페이지 2개 - true, false일 경우
쿠키값 == null이면 로그인버튼(인증 전)
쿠키값 != null이면 로그아웃버튼(값을 가지고 있음, 인증 후)
절대경로- 처음부터 다 적음
상대경로- 현재 내가 바라보는 경로에서부터 적음
./ → 현재 내가 작성하는 페이지 의미
../ → 현재 내가 있는 폴더에서 상위폴더를 의미
쿠키삭제하기(로그아웃 담당)
로그인
window.location.href=”./login.st3” → 상대경로
window.location.href=”/member/login.st3” → 절대경로
로그아웃
window.location.href=”./logout.st3”
st3 앞에 있는 이름은 메소드명 → logout은 MemberController.java에 선언된 메소드 이름이다
→ ./logout.st3으로 요청이 오면 ActionSupport가 인터셉트함(web.xml에 *.st3으로 설정)
→ upmu[0] = member, upmu[1] = logout
→ MemberController.java - Controller3.java(인터페이스) 추상클래스, 인터페이스 중심의 코드 전개를 위해 설계
→HandlerMapping.java
<쿠키 로그아웃 - HandlerMapping.java>
package com.pojo.step3;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
public class HandlerMapping {
static Logger logger = Logger.getLogger(HandlerMapping.class);
/***************************************
* @param upmu[](upmu[0]=업무명,폴더명 | upmu[1]=메소드명, 기능명, 페이지이름)
* @param request -> 1-1, 1-2와는 다르게 인터페이스를 implements하지 않는다
* @param response -> HttpServlet
* Q. 어디서 받아오는가?
* @return Object
* 테스트 -> <http://localhost:9000/board3/boardList.st3>
***************************************/
public static Object getController(String[] upmu, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info(upmu[0]+", "+upmu[1]);
Controller3 controller = null;
String path = null;
Object obj = null;
ModelAndView mav = null;
// common
if("common".equals(upmu[0])) {
controller = new CommonController();
// 우편번호 조회
if("zipcodeList".equals(upmu[1])) {
obj = controller.zipcodeList(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
} // end if zipcodeList if문
}
// 게시판 구현
else if("board3".equals(upmu[0])) {
controller = new Board3Controller();
// 게시글 전체 목록 -> html 화면 출력(text/html)
if("boardList".equals(upmu[1])) {
obj = controller.boardList(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
// json 게시글 전체 목록 -> json 화면 출력(application/json)
else if("jsonBoardList".equals(upmu[1])) {
obj = controller.jsonBoardList(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
// 글 입력 - 새글쓰기와 댓글쓰기
else if("boardInsert".equals(upmu[1])) {
obj = controller.boardInsert(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 이미지 업로드 - 리액트 quill editor 이미지 추가
else if("imageUpload".equals(upmu[1])) {
obj = controller.imageUpload(req, res);
logger.info("imageUpload 호출 => " + obj instanceof String);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 이미지 다운로드 - 리액트 quill editor 이미지 추가
else if("imageDownload".equals(upmu[1])) {
obj = controller.imageDownload(req, res);
logger.info("imageDownload 호출 => " + obj instanceof String);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 리액트 -
else if("imageGet".equals(upmu[1])) {
obj = controller.imageGet(req, res);
logger.info("imageGet 호출 => " + obj instanceof String);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 수정 - 첨부파일 수정 유무 고려하기
else if("boardUpdate".equals(upmu[1])) {
obj = controller.boardUpdate(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 삭제 - 첨부파일 삭제 유무 고려하기
else if("boardDelete".equals(upmu[1])) {
obj = controller.boardDelete(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 상세보기
else if("boardDetail".equals(upmu[1])) {
obj = controller.boardDetail(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
} // end of 게시판 구현
// 인증 관리 구현
else if("auth".equals(upmu[0])) {
}
// 회원 관리 구현
else if("member".equals(upmu[0])) {
controller = new MemberController();
// 로그인
if("login".equals(upmu[1])) {
logger.info("login 호출");
obj = controller.login(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
} // end of login
// 로그아웃
else if("logout".equals(upmu[1])) {
logger.info("logout 호출");
obj = controller.logout(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
} // end of logout
} // end of member
// 주문 관리 구현
else if("order".equals(upmu[0])) {
}
return obj;
}
}
<쿠키 로그아웃 - Controller3.java>
package com.pojo.step3;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// javascript기반의 UI API
// 리액트는 바닐라스크립트 + ES6(주요이슈-spread, module, arrow),ES7 + html 섞어쓰기
public interface Controller3 {
public Object login(HttpServletRequest req, HttpServletResponse res);
public Object logout(HttpServletRequest req, HttpServletResponse res);
public Object zipcodeList(HttpServletRequest req, HttpServletResponse res);
public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res);
public Object boardList(HttpServletRequest req, HttpServletResponse res);
public Object boardDetail(HttpServletRequest req, HttpServletResponse res);
public Object imageUpload(HttpServletRequest req, HttpServletResponse res);
public Object imageDownload(HttpServletRequest req, HttpServletResponse res);
public Object imageGet(HttpServletRequest req, HttpServletResponse res);
public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
}
<쿠키 로그아웃 - MemberController.java>
package com.pojo.step3;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.util.HashMapBinder;
public class MemberController implements Controller3 {
Logger logger = Logger.getLogger(MemberController.class);
MemberLogic memberLogic = new MemberLogic();
@Override
public Object login(HttpServletRequest req, HttpServletResponse res) {
logger.info("login 호출");
Map<String, Object> rMap = new HashMap<>();
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
rMap = memberLogic.login(pMap);
logger.info(rMap);
Cookie cmem_id = new Cookie("cmem_id", rMap.get("MEM_ID").toString());
cmem_id.setPath("/");
cmem_id.setMaxAge(60 * 60);
res.addCookie(cmem_id);
Cookie cmem_name = new Cookie("cmem_name", rMap.get("MEM_NAME").toString());
cmem_name.setPath("/");
cmem_name.setMaxAge(60 * 60);
res.addCookie(cmem_name);
return "redirect:./cindex.jsp"; // => member.cindex.jsp
}
@Override
public Object logout(HttpServletRequest req, HttpServletResponse res) {
logger.info("logout 호출");
/*
* 쿠키는 삭제하는 메소드가 따로 없다
* 생성자에 두번째 파라미터에 빈 문자열로 처리하고
* 시간을 0으로 초기화해줘야함
* 도메인도 동일하게 맞춰야 삭제가 됨(주의)
*/
Cookie cmem_id = new Cookie("cmem_id", "");
cmem_id.setPath("/");
cmem_id.setMaxAge(0);
res.addCookie(cmem_id);
Cookie cmem_name = new Cookie("cmem_name", "");
cmem_name.setPath("/");
cmem_name.setMaxAge(0);
res.addCookie(cmem_name);
// navigate = useNavigate("./cindex.jsp") -> 리액트 화면전환용(부분적)
return "redirect:./cindex.jsp";
}
@Override
public Object zipcodeList(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardList(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageUpload(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageDownload(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageGet(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
@Override
public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
@Override
public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
}
<쿠키 로그아웃 - cindex.jsp>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String cmem_id = null;
String cmem_name = null;
// 서버측에서 클라이언트(사용자) 쿠키값 보내달라는 요청
Cookie cs[] = request.getCookies();
int size = 0;
// NullPointerException 방어코드
if(cs!=null){
size = cs.length;
}
for(int i=0;i<size;i++){
// 쿠키이름을 가져온다 - 셍성자의 첫번째 파라미터 자리값
String c_name = cs[i].getName();
// 서버측에서 클라이언트로부터 넘겨받은 문자열을 비교함
if("cmem_id".equals(c_name)){
// 쿠키이름이 cmem_id인 쿠키의 값을 담기
cmem_id = cs[i].getValue();
}
// 한 번더 쿠키값을 꺼내온다 -> 사용자의 이름을 불러줌
if("cmem_name".equals(c_name)){
cmem_name = cs[i].getValue();
}
}
out.print("쿠키에서 꺼내 온 값 ===> " + cmem_id + " , " + cmem_name);
if(cmem_id == null){
%>
<script>
alert("로그인을 먼저 해주세요")
</script>
<%
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Web application[쿠키인증실습 webapp]</title>
<%@ include file="../common/easyUI_common.jsp"%>
<style type="text/css">
a {
text-decoration: none;
}
</style>
<script>
// 로그인 버튼
const login =() =>{
/*
테스트: com.mvc.dao.MemberDao.java
xml: com.mybatis.mapper에 member.xml
SELECT mem_name FROM book_member
WHERE mem_id =:id
AND mem_pw =:pw
*/
// 사용자가 입력한 아이디 가져오기
const user_id = $("#_easyui_textbox_input1").val();
// 사용자가 입력한 비밀번호 가져오기
const user_pw = $("#_easyui_textbox_input2").val();
console.log(user_id + user_pw);
window.location.href = "./login.st3?mem_id=" + user_id + "&mem_pw=" + user_pw;
}
// 로그아웃 버튼
const logout = () => {
// 아래 코드에 ./가 없으면 pageMove배열 매칭이 안되니 주의
window.location.href = "./logout.st3";
}
</script>
</head>
<body>
<h2>웹 어플리케이션 실습</h2>
<div style="margin: 20px 0;"></div>
<div class="easyui-layout" style="width: 1000px; height: 500px;">
<!-- =============== 메뉴 구성 [로그인화면과 트리메뉴] 시작 =============== -->
<div id="p" data-options="region:'west'" title="KH정보교육원"
style="width: 200px; padding: 5px">
<%
// 쿠키값이 null인 경우 - 로그인을 아직 안함
if(cmem_id == null){
%>
<!-- =============== [[ 로그인 화면 ]] =============== -->
<div id="d_login" align="center">
<div style="margin: 3px 0"></div>
<!-- create table member mem_id varchar2(10) DB에서 이런형식 -->
<input id="tb_id" type="text" style="width: 170px">
<script>
$('#tb_id').textbox({
iconCls : 'icon-man',
iconAlign : 'right',
prompt : '아이디'
})
</script>
<div style="margin: 3px 0"></div>
<input id="pb_pw" type="text" style="width: 170px">
<script>
$('#pb_pw').textbox({
iconCls: 'icon-lock',
iconAlign: 'right',
prompt : '비밀번호'
});
</script>
<div style="margin: 3px 0"></div>
<a id="btn_login" href="javascript:login()">로그인</a>
<script>
$('#btn_login').linkbutton({
iconCls : 'icon-man'
});
</script>
<a id="btn_member" href="javascript:memberShip()">회원가입</a>
<script>
$('#btn_member').linkbutton({
iconCls : 'icon-add'
});
</script>
</div>
<!-- =============== [[ 로그인 화면 ]] =============== -->
<%}
else { // 로그인을 한 상태일 경우 %>
<!-- =============== [[ 로그아웃 화면 ]] =============== -->
<div id="d_logout" align="center">
<div style="margin: 3px 0"></div>
<span><%=cmem_name %>님 환영합니다!</span>
<div style="margin: 3px 0"></div> <!-- 외부여백을 위, 아래에 3px -->
<a id="btn_logout" href="javascript:logout()">로그아웃</a>
<script>
$('#btn_logout').linkbutton({
iconCls : 'icon-remove'
});
</script>
<a id="btn_member" href="javascript:memberShip()">정보수정</a>
<script>
$('#btn_member').linkbutton({
iconCls : 'icon-edit'
});
</script>
</div>
<!-- =============== [[ 로그아웃 화면 ]] =============== -->
<%} %>
<!-- =============== 메뉴 구성 [로그인화면과 트리메뉴] 끝 =============== -->
<div style="margin: 3px 0"></div>
<ul id="tre_gym" class="easyui-tree">
<li data-options="state:closed"><span>회원관리</span>
<ul>
<li><a href="#">회원목록</a></li>
<li><a href="#">회원등록</a></li>
<li><a href="#">회원삭제</a></li>
</ul></li>
<li data-options="state:'closed'"><span>쪽지관리</span>
<ul>
<li><a href="#">받은쪽지함</a></li>
<li><a href="#">보낸쪽지함</a></li>
</ul></li>
<li data-options="state:'closed'"><span>기타</span>
<ul>
<li><a href="#">우편번호검색기</a></li>
<li><a href="#">게시판</a></li>
</ul></li>
</ul>
</div>
<!-- 메인화면 [게시판, 온라인시험, 쪽지관리(받은쪽지함, 보낸쪽지함), 회원관리(회원목록, 회원등록, 회원삭제), 우편번호검색기] 시작 -->
<div data-options="region:'center', iconCls:'icon-ok'"
title="TerrGYM2023">
<div style="margin: 5px 0;"></div>
Home > 회원관리 > 회원목록
<hr>
<div style="margin: 25px 0;"></div>
</div>
<!-- 메인화면 [게시판, 온라인시험, 회원관리, 우편번호검색기] 끝 -->
</div>
</body>
</html>
<!--
부트스트랩 - 리액트수업 -> Spring과 리액트 연계 수업 -> 프로젝트적용
반응형 지원, CSS라이브러리 사용
CSS - JS거의없음
jEasyUI
이벤트처리(jquery - 레거시시스템)
자바스크립트 - 표준아님 -> jquery기반이라서
자바스크립트 기반의 UI솔루션 사용하기 - 큰 도움
개발자도구 활용 - 디버깅 -> 왜냐하면 html을 래핑하기때문에
vue.js, reactjs
jEasyUI는 html으로 화면처리, html+js 섞어쓰기, js(최소한의 태그선언 필요)로 화면처리 가능
JSTL - 1.1 -> 1.2 -> 소개(사용x)
로그인 테스트 흐름도
1. intro 아래 index.jsp 실행
2. 아이디와 비밀번호를 입력받는다
3. 로그인 버튼을 누른다 -> 자바스크립트의 login() 호출
4. login.do 호출한다 -> 로그인 처리를 하는 서블릿 호출 - doGet(), doPost()
5. com.mvc.dao.MemberDao 클래스의 인스턴스화
6. 메소드 호출 - 로그인처리
7. MemberDao의 login(Map[member_id(사용자가 입력한 아이디)와 mem_pw(사용자가 입력한 비번)가 있음]) 호출
8. 리턴타입으로 Map을 받아온다(mem_id, mem_name)
9. 8번에서 돌려받은 Map에서, 오라클 서버에서 조회된 아이디와 이름을 세션에 담기
10. 페이지 이동은 sendRedirect나 forward 둘 다 모두 가능하다
단, forward로 응답을 처리한 경우, 인증 후에 다른 서비스를 forward로 처리하는것이 불가하다(주의!)
-->
커넥션 풀(Connection Pool)
데이터베이스와 연결된 커넥션을 미리 만들어놓고 이를 pool로 관리하는 것
필요할 때마다 커넥션 풀의 커넥션을 이용하고 반환하는 기법
미리 만들어놓은 커넥션을 이용하면 비용 줄이고 빠르게 접속 가능
커넥션 수를 제한할 수 있어서 과도한 접속으로 인한 서버 자원 고갈 방지 가능
DB접속모듈을 공통화(분리)해 DB 서버의 환경이 바뀔 경우 유지보수를 쉽게 할 수 있음
HikariCP
가벼운 용량과 빠른 속도를 가지는 JDBC의 커넥션 풀 프레임워크
SpringBoot는 커넥션 풀 관리를 위해 HikariCP를 사용함
https://start.spring.io/ → Dependencies 추가하고 EXPLORE 누른 다음 pom.xml의 dependency 복사해서 프로젝트의 pom.xml에 붙여 넣기(maven 빌드의 경우)
추가된 것 확인 경로 C:\Users\user1\.m2\repository\org\springframework
한글처리 → WEB-INF-lib에 orai18n-19.3.0.0.jar 넣어주기
클라이언트가 요청(스프링부트에서는 XXX.st3같이 붙이지 않을 예정)
→ DispatcherServlet(서블릿을 경유 - 간섭, 관여, interceptor)
→ 여러 업무(xxxController) @Controller(view로 이어짐-React) 혹은 @RestController(String → JSON) 사용
→ 업무별 로직(xxxLogic) 공통된 관심사가 분리되어야 하는 곳, 트랜잭션처리에 관여 @Service
→ Dao(xxxDao) 한 개 혹은 여러 개의 Dao @Repository
클래스이름 앞 어노테이션
@Controller @RestController @RequestMapping @Autowired @Service @Repository
메소드 앞 어노테이션
@GetMapping @PostMapping RestAPI전부(@DeleteMapping, @PutMapping)
메소드 파라미터 안 클래스
@Model, @ModelMap(view를 위해 존재, ModelAndView의 대체안, scope는 request)
메소드 파라미터 안 어노테이션
@RequestParam (Map, String, VO 등 올 수 있음)
xml에서는 namespace 권장 → beans:bean의 beans:부분
Properties prop = new Properties(”/board3/boardList.sp”. “board-controller(아이디)”) → 자바에선 이렇게 스프링에선 prop 사용
bean은 클래스이다 id는 위의 prop 아이디와 일치해야 한다
bean 안의 인스턴스 변수, name=”boardLogic”은 스프링부트의 @Autowired BoardLogic boardLigic = null;과 같다
PropertiesMethodNameResolver는 URL과 메소드 이름을 매칭해 준다
xml과 xml 사이의 의존성주입
xml과 java 사이의 의존성주입 → MemberDao에서 SqlSessionTemplate.xml 주입받은 경우
java와 java 사이의 의존성주입 → @Autowired
@Configutation
xml이 아닌 것도 객체주입 가능
→ 사용자가 정의한 클래스도 ApplicationContext나 BeanFactory의 관리(라이프사이클 관리)를 받을 수 있다
@Autowired
setter객체주입법 대신 만들어짐, 로컬사용
서블릿 컨테이너에 등록되어 있는 빈을 끌어다가 사용하는 어노테이션
@Bean
독립적, 글로벌 사용
서블릿 컨테이너에 빈을 등록하는 어노테이션
Spring Bean
Spring의 DI Container에 의해 관리되는 POJO(Plain Old Java Object)를 Bean이라고 부름
POJO(Plain Old Java Object)로써 Spring 애플리케이션을 구성하는 핵심 객체
Spring IoC 컨테이너(또는 DI 컨테이너)에 의해 생성 및 관리됨
class, id, scope, constructor-arg 등을 주요 속성으로 지님
Bean의 구성 요소를 바탕으로 등록되어 있는 Bean을 싱글톤 객체로 생성하여 관리
@Configuration 어노테이션은 클래스 앞에
@Bean 어노테이션은 메소드 앞에
스프링 컨테이너는 @Configuration이 붙어있는 클래스를 자동으로 빈으로 등록해 두고, 해당 클래스를 파싱 해서(읽어서) @Bean이 있는 메소드를 찾아서 빈을 생성해 줌
@Bean을 사용하는 클래스에는 반드시 @Configuration 어노테이션을 활용하여 해당 클래스에서 Bean을 등록하고자 함을 명시해야 함
@Configuration 안에서 @Bean을 사용해야 싱글톤을 보장받을 수 있으므로 @Bean 어노테이션은 반드시 @Configuration과 함께 사용해야 함
@Bean, @Configuration
수동으로 스프링 컨테이너에 빈을 등록하는 방법
개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용
유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 빈으로 등록할 때 사용
1개 이상의 @Bean을 제공하는 클래스의 경우 반드시 @Configuration을 명시해 주어야 싱글톤이 보장됨
@Component
자동으로 스프링 컨테이너에 빈을 등록하는 방법
스프링의 컴포넌트 스캔 기능이 @Component 어노테이션이 있는 클래스를 자동으로 찾아서 빈으로 등록함
대부분의 경우 @Component를 이용한 자동 등록 방식을 사용하는 것이 좋음
@Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 있음
@Configuration
@PropertySource("classpath:/application.properties") // classpath -> resource -> log4j.properties
public class DatabaseConfiguration {
private static final Logger logger = LogManager.getLogger(DatabaseConfiguration.class);
// 물리적으로 떨어져있는 오라클서버(application.properties에 정의됨)와 연결통로 확보
// POJO에서는 MyBatisConfig.xml이 hikariConfig()역할을 대신함
@Bean
public DataSource dataSource() {
// 인터페이스=구현체클래스 (new HikariConfig()) -> application.properties
DataSource dataSource = new HikariDataSource(hikariConfig());
logger.info("datasource : {}", dataSource);
return dataSource;
}
}
property의 name은 setter 메소드로 사용(관리받을 수 있음)
오라클서버와 커넥션에 필요한 정보
SqlSessionTemplate - SelectOne, SelectList, SelectMap
생성자 객체주입법 constructor-arg → 객체주입받음
<RestMemberController.java>
package com.example.demo.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.logic.BoardLogic;
import com.example.demo.logic.MemberLogic;
import com.google.gson.Gson;
@RestController
@RequestMapping("/member/*")
public class RestMemberController {
Logger logger = LoggerFactory.getLogger(RestMemberController.class);
@Autowired
private MemberLogic memberLogic = null;
@GetMapping("jsonMemberList")
public String jsonMemberList(Model model) {
logger.info("jsonMemberList 호출");
String temp = null;
List<Map<String, Object>> mList = new ArrayList<>();
Map<String, Object> pMap = new HashMap<>();
mList = memberLogic.memberList(pMap);
Gson g = new Gson();
temp = g.toJson(mList);
return temp;
}
}
<MemberLogic.java>
package com.example.demo.logic;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.dao.MemberDao;
/*
* 모델계층(MemberLogic[직접 오라클과 연동하지 않음] + MemberDao[데이터셋])
* 공통된 관심사 분리(AspectJ 프레임워크 - 오픈소스 참고)
* 트랜잭션 처리 지원받음
*/
@Service
public class MemberLogic {
Logger logger = LoggerFactory.getLogger(MemberLogic.class);
@Autowired
private MemberDao memberDao = null;
public List<Map<String, Object>> memberList(Map<String, Object> pMap) {
logger.info("memberList 호출");
List<Map<String,Object>> mList = new ArrayList<>();
mList= memberDao.memberList(pMap);
return mList;
}
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap.toString());
int result = 0;
result = memberDao.memberInsert(pMap);
return result;
}
}
<MemberDao.java>
package com.example.demo.dao;
import java.util.List;
import java.util.Map;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
// @Repository -> MVC패턴 공부를위해 Service를 사용함
@Service
public class MemberDao {
Logger logger = LoggerFactory.getLogger(MemberDao.class);
@Autowired
private SqlSessionTemplate sqlSessionTemplate = null;
public List<Map<String, Object>> memberList(Map<String, Object> pMap) {
logger.info("memberList 호출");
List<Map<String,Object>> mList = sqlSessionTemplate.selectList("memberList", pMap);
return mList;
}
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap.toString());
int result = 0;
return result;
}
}
<member.xml>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo">
<select id="getToday" resultType="string" parameterType="string">
SELECT to_char(sysdate, 'YYYY-MM-DD') FROM dual
</select>
<select id="login" parameterType="map" resultType="string">
select mem_name from book_member
<where>
<if test='mem_id!=null and mem_id.length()>0'>
AND mem_id = #{mem_id}
</if>
<if test='mem_pw!=null and mem_pw.length()>0'>
AND mem_pw = #{mem_pw}
</if>
</where>
</select>
<select id="memberList" parameterType="map" resultType="map">
select mem_id, mem_name from book_member
<where>
<if test='mem_id!=null and mem_id.length()>0'>
AND mem_id = #{mem_id}
</if>
</where>
</select>
</mapper>
<DatabaseConfiguration.java>
package com.example.demo;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/*
* @Configuration
* 사용자 정의 클래스도 ApplicationContext나 BeanFactory의 관리를 받을 수 있다 - 의존성 주입(Dependency Injection)
*
* application.properties - maven방식
* application.yml - gradle방식(반복코드 없음, json포맷이라서)
*
* resource - mapper
* member.xml, board.xml, order.xml
*
* static - CDN
* css, js, images(정적인것들을 static에 넣음)
*/
@Configuration
@PropertySource("classpath:/application.properties") // classpath -> resource -> log4j.properties
public class DatabaseConfiguration {
private static final Logger logger = LogManager.getLogger(DatabaseConfiguration.class);
// @Bean - @Configuration으로 선언된 클래스에서만 사용가능한 어노테이션
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari") // application.properties의 접두어
public HikariConfig hikariConfig() { // 인스턴스화
return new HikariConfig(); // 생성자호출 - 메모리에 로딩됨 - 변수와 메소드를 누릴 수 있음(주입해줌)
}
// 물리적으로 떨어져있는 오라클서버(application.properties에 정의됨)와 연결통로 확보
// POJO에서는 MyBatisConfig.xml이 hikariConfig()역할을 대신함
@Bean
public DataSource dataSource() {
// 인터페이스=구현체클래스 (new HikariConfig()) -> application.properties
DataSource dataSource = new HikariDataSource(hikariConfig());
logger.info("datasource : {}", dataSource);
return dataSource;
}
// @Autowired 의존성 주입
@Autowired
private ApplicationContext applicationContext; // 빈관리 - 이른 인스턴스화 - BeanFactory의 자손 클래스임(기능 더 많다)
/*
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="WEB-INF/mybatis-config.xml"/>
<property name="dataSource" ref="data-source-target"/>
</bean>
위의 코드를 대체하는 것의 아래의 @Bean
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
//classpath는 src/main/resources이고 쿼리가 있는 xml 위치를 설정해주면 된다.
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/*.xml"));
return sqlSessionFactoryBean.getObject();
}
/*
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
위의 코드를 대체하는 것의 아래의 @Bean
*/
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
<application.properties>
server.port=8000
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#spring.http.encoding.charset=UTF-8
#MultipartRequest
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB
#Hikari
spring.datasource.hikari.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.hikari.jdbcUrl=jdbc:oracle:thin:@127.0.0.1:1521:orcl11
spring.datasource.hikari.username=scott
spring.datasource.hikari.password=tiger
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-Idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.ldle-timeout=300000
spring.datasource.hikari.max-lifetime=1200000
spring.datasource.hikari.auto-commit=true
표현언어(Expression Language)
표현 언어는 값을 표현하는 데 사용되는 새로운 스크립트 언어로서 JSP2.0 규약에서 새롭게 추가된 기능
JSP에서 출력을 위해서 표현식을 사용함
${“Hello EL” } <br> <!—EL 표현 언어 -->
<%= “Hello EL” %> <br> <!—JSP 표현식 -->
<% out.println(“Hello EL”); %> <br> <!—JSP 스크립트릿 -->
<EL(표현언어)실습 - cookieCreate.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키 생성하기</title>
</head>
<body>
<%
// 아래 코드는 서버가 바라보는 물리적인 위치에 들어있음
// 서버측에서 실행됨 -> html -> 다운로드(SSR) -> 동적처리(실시간)
Cookie c1 = new Cookie("notebook", "삼성갤럭시북");
c1.setMaxAge(60*5);
c1.setPath("/");
Cookie c2 = new Cookie("hp", "아이폰14");
c2.setMaxAge(60*2);
c2.setPath("/");
Cookie c3 = new Cookie("coffee", "아메리카노");
c3.setMaxAge(60*3);
c3.setPath("/");
response.addCookie(c1);
response.addCookie(c2);
response.addCookie(c3);
%>
</body>
</html>
<EL(표현언어)실습 - cookiePrint.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>EL(표현언어)실습</title>
</head>
<body>
쿠키이름 : ${cookie.notebook.name}, ${cookie.hp.name}, ${cookie.coffee.name}
<br />
쿠키값 : ${cookie.notebook.value}, ${cookie.hp.value}, ${cookie.coffee.value}
</body>
</html>
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day78 (0) | 2023.03.21 |
---|---|
국비 지원 개발자 과정_Day77 (0) | 2023.03.20 |
국비 지원 개발자 과정_Day75 (1) | 2023.03.16 |
국비 지원 개발자 과정_Day74 (1) | 2023.03.15 |
국비 지원 개발자 과정_Day73 (1) | 2023.03.14 |
댓글