오늘의 코딩순서
(폴더: oBootMybatis01)
(0819 일지)
3. AOP
- LogAop.class
7. UpLoad(이미지 올리기)
- application.yml + UpLoadFormStart.jsp + UploadController.class + uploadResult.jsp
+++ 이미지 업로드 후 삭제 방법
- uploadResult.jsp + UploadController.class
+++ 이미지 백업 방법
- UploadController.class
8. Ajax Form Test
- ajaxForm.jsp + EmpController.class
1. helloText
- EmpRestController.class + EmpService.interface + EmpServiceImpl.class
2. sample/sendV02(객체)
- SampleVO.class + EmpRestController.class
3. sendV03
- EmpRestController.class + EmpServiceImpl.class
4. getDeptName(controller)
- EmpController.class + EmpService.interface + EmpServiceImpl.class + LogAop.class
+ EmpDao.interface+ EmpDaoImpl.class + EmpDept.xml
(여기부터는 실무!⭐)
5. listEmpAjaxForm(ajax JSP 텍스트 연동)
- listEmpAjaxForm.jsp + EmpController.class + EmpRestController.class
+ EmpService.interface + EmpServiceImpl.class + EmpDao.interface+ EmpDaoImpl.class + Emp.xml
(0820 일지)
6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
- listEmpAjaxForm2.jsp + EmpController.class => 조회
- listEmpAjaxForm2.jsp + EmpRestController.class => 삭제1. 결과를 text로 받음
- listEmpAjaxForm2.jsp + EmpRestController.class => 삭제2. 결과를 객체로 받음
7. listEmpAjaxForm3(ajax List를 Controller로 전송)
- listEmpAjaxForm3.jsp + EmpController.class => empLISTTest 전송 버튼
- listEmpAjaxForm3.jsp + EmpController.class => empLIST 전송 버튼
오늘의 코딩 포인트
(0819 일지)
3. AOP
- LogAop.class
Tip)
- @component: 스프링 빈으로 등록함
- @Aspect: Advice+PointCut, 해당 클래스가 부가기능을 제공하는 Aspect 클래스라는 것을 명시함
이 때 Aspect 클래스는 Advice를 구현하는 메서드와 Pointcut을 포함함 - @Pointcut: 핵심 메서드 경로를 설정해주어 일일이 공통 메서드에 넣을 필요가 없도록 해줌
- @Around: 타겟의 메서드가 호출되기 이전(before) 시점과 이후 (after) 시점에 모두 처리해야 할 필요가 있는 부가기능을 정의함
package com.oracle.oBootMybatis01.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAop {
// com.oracle.oBootMybatis01.dao package 안의 EmpDao이름을 가진 모든 것
@Pointcut("within(com.oracle.oBootMybatis01.dao.EmpDao*)")
private void pointcutMethod() {
}
@Around("pointcutMethod()")
public Object loggerAop(ProceedingJoinPoint joinPoint) throws Throwable {
String signatureStr = joinPoint.getSignature().toShortString();
System.out.println(signatureStr + " is start.");
long st = System.currentTimeMillis();
try {
// 핵심 관심사(비즈니스 업무)
Object obj = joinPoint.proceed();
return obj;
} finally {
long et = System.currentTimeMillis();
System.out.println(signatureStr + " is finished.");
System.out.println(signatureStr + "경과시간 : " + (et - st));
}
}
}
7. UpLoad(이미지 올리기)
- application.yml
Tip) 파일 사이즈 용량 정하기
server:
port: 8387
# Oracle Connect
spring:
#File Size
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@localhost:1521/xe
username: scott
password: tiger
#JPA Setting
jpa:
show-sql: true
hibernate:
ddl-auto: update
# View Resolver
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
# gmail Transfer
mail:
host: smtp.gmail.com
port: 587
username: rollout147@gmail.com
password: bbwt lmdi vhew burk
properties:
mail:
smtp:
auth: true
starttls.enable: true
# Mybatis
mybatis:
config-location: classpath:configuration.xml
mapper-locations: classpath:mappers/*.xml
- UpLoadFormStart.jsp
Tip)- UUID
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
UpLoad Image : <img alt="UpLoad Image" src="${pageContext.request.contextPath}/upload/${savedName}">
<form id="form1" action="uploadForm" method="post" enctype="multipart/form-data">
<!-- 이미지나 멀티미디어 파일은 꼭 post와 multipart/form-data로 해야함 -->
<input type="file" name="file1"><p>
<input type="text" name="title"><p>
<input type="submit">
</form>
</body>
</html>
- UploadController.class
package com.oracle.oBootMybatis01.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
public class UploadController {
// 0. UploadForm 시작화면
@RequestMapping(value = "upLoadFormStart")
public String upLoadFormStart(Model model) {
System.out.println("UploadController upLoadFormStart Start...");
return "upLoadFormStart";
}
// 1. 이미지 업로드
@RequestMapping(value = "uploadForm", method = RequestMethod.GET)
public void uploadForm() {
System.out.println("uploadForm GET Start...");
System.out.println();
}
@RequestMapping(value = "uploadForm", method = RequestMethod.POST)
public String uploadForm(HttpServletRequest request, Model model)
throws IOException, Exception {
Part image = request.getPart("file1");
InputStream inputStream = image.getInputStream();
// 파일 확장자 구하기
String fileName = image.getSubmittedFileName();
String[] split = fileName.split("\\.");
String originalName = split[split.length - 2];
String suffix = split[split.length - 1];
System.out.println("fileName->"+fileName);
System.out.println("originalName->"+originalName);
System.out.println("suffix->"+suffix);
// Servlet을 상속 받지 못했을 때 realPath를 불러오는 방법
String uploadPath = request.getSession().getServletContext().getRealPath("/upload/");
System.out.println("uploadForm POST Start...");
String savedName = uploadFile(originalName, inputStream, uploadPath, suffix);
// uploadFile에서 create 자동완성해서 아래 private 만들기
// Service ==> DB CRUD
log.info("Return savedName: " + savedName);
model.addAttribute("savedName", savedName);
return "uploadResult";
}
private String uploadFile(String originalName,
InputStream inputStream,
String uploadPath,
String suffix) throws IOException {
// universally unique identifier (UUID).
UUID uid = UUID.randomUUID();
// requestPath = requestPath + "/resources/image";
System.out.println("uploadPath->"+uploadPath);
// Directory 생성
File fileDirectory = new File(uploadPath);
if (!fileDirectory.exists()) {
// 신규 폴더(Directory) 생성
fileDirectory.mkdirs();
System.out.println("업로드용 폴더 생성 : " + uploadPath);
}
String savedName = uid.toString() + "_" + originalName + "." + suffix;
log.info("savedName: " + savedName);
// 임시파일 생성
File tempFile = new File(uploadPath+savedName);
// 생성된 임시파일에 요청으로 넘어온 file의 inputStream 복사
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
int read;
byte[] bytes = new byte[1024];
// 더 큰 용량의 파일을 올리려면 yml에서 설정 변경하기
while ((read = inputStream.read(bytes)) != -1) {
// -1: 파일이 끝날때까지 요청
// Target File에 요청으로 넘어온 file의 inputStream 복사
outputStream.write(bytes, 0, read);
// backup 파일에 요청으로 넘어온 file의 inputStream 복사
// outputStream.write(bytes, 0, read);
}
} finally {
System.out.println("UpLoad The End");
}
// outputStream3.close();
return savedName;
}
}
위처럼 이미지를 올리고 제출을 누르면 ↓↓↓
Console에 나온 경로를 따라가면 이미지가 폴더에 업로드 된 것을 확인 가능함
- uploadResult.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Image : ${savedName}<p>
UpLoad Image : <img alt="UpLoad Image" src="${pageContext.request.contextPath}/upload/${savedName}">
</body>
</html>
+++ 이미지 업로드 후 삭제 방법
- uploadResult.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Image : ${savedName}<p>
UpLoad Image : <img alt="UpLoad Image" src="${pageContext.request.contextPath}/upload/${savedName}">
<a href="uploadFileDelete?delFile=${savedName}">upLoad 삭제</a>
</body>
</html>
- UploadController.class
package com.oracle.oBootMybatis01.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
public class UploadController {
// 2. 올린 이미지 삭제
@RequestMapping(value = "uploadFileDelete", method = RequestMethod.GET)
public String uploadFileDelete(HttpServletRequest request, Model model) {
String uploadPath = request.getSession().getServletContext().getRealPath("/upload/");
String delFile = request.getParameter("delFile");
// uploadResult.jsp의 delFile과 연결됨
System.out.println("uploadFileDelete GET Start...");
String deleteFile = uploadPath + delFile;
System.out.println("uploadFileDelete deleteFile->"+deleteFile);
int delResult = upFileDelete(deleteFile);
model.addAttribute("deleteFile", deleteFile);
model.addAttribute("delResult", delResult);
return "uploadResult";
}
private int upFileDelete(String deleteFileName) {
int result = 0;
log.info("upFileDelete result->" + deleteFileName);
File file = new File(deleteFileName);
if( file.exists()) {
if(file.delete()) {
System.out.println("파일삭제 성공");
result = 1;
} else {
System.out.println("파일삭제 실패");
result = 0;
}
} else {
System.out.println("삭제할 파일이 존재하지 않습니다");
result = -1;
}
return result;
}
}
+++ 이미지 백업 방법, 백업폴더에서 삭제 방법
- UploadController.class
package com.oracle.oBootMybatis01.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
public class UploadController {
// 0. UploadForm 시작화면
@RequestMapping(value = "upLoadFormStart")
public String upLoadFormStart(Model model) {
System.out.println("UploadController upLoadFormStart Start...");
return "upLoadFormStart";
}
// 1. 이미지 업로드
@RequestMapping(value = "uploadForm", method = RequestMethod.GET)
public void uploadForm() {
System.out.println("uploadForm GET Start...");
System.out.println();
}
@RequestMapping(value = "uploadForm", method = RequestMethod.POST)
public String uploadForm(HttpServletRequest request, Model model)
throws IOException, Exception {
Part image = request.getPart("file1");
InputStream inputStream = image.getInputStream();
// 파일 확장자 구하기
String fileName = image.getSubmittedFileName();
String[] split = fileName.split("\\.");
String originalName = split[split.length - 2];
String suffix = split[split.length - 1];
System.out.println("fileName->"+fileName);
System.out.println("originalName->"+originalName);
System.out.println("suffix->"+suffix);
// Servlet을 상속 받지 못했을 때 realPath를 불러오는 방법
String uploadPath = request.getSession().getServletContext().getRealPath("/upload/");
System.out.println("uploadForm POST Start...");
String savedName = uploadFile(originalName, inputStream, uploadPath, suffix);
// uploadFile에서 create 자동완성해서 아래 private 만들기
// Service ==> DB CRUD
log.info("Return savedName: " + savedName);
model.addAttribute("savedName", savedName);
return "uploadResult";
}
private String uploadFile(String originalName,
InputStream inputStream,
String uploadPath,
String suffix) throws IOException {
// universally unique identifier (UUID).
UUID uid = UUID.randomUUID();
// requestPath = requestPath + "/resources/image";
System.out.println("uploadPath->"+uploadPath);
// Directory 생성
File fileDirectory = new File(uploadPath);
if (!fileDirectory.exists()) {
// 신규 폴더(Directory) 생성
fileDirectory.mkdirs();
System.out.println("업로드용 폴더 생성 : " + uploadPath);
}
String savedName = uid.toString() + "_" + originalName + "." + suffix;
log.info("savedName: " + savedName);
// 임시파일 생성
File tempFile = new File(uploadPath+savedName);
// --------------------------------------------
// Backup File 생성
File tempFile3 = new File("C:/BACKUP/"+savedName);
FileOutputStream outputStream3 = new FileOutputStream(tempFile3);
// --------------------------------------------
// 생성된 임시파일에 요청으로 넘어온 file의 inputStream 복사
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
// -1: 파일이 끝날때까지 요청
// Target File의 요청으로 넘어온 file의 inputStream 복사
outputStream.write(bytes, 0, read);
// backup 파일의 요청으로 넘어온 file의 inputStream 복사
outputStream.write(bytes, 0, read);
}
} finally {
System.out.println("UpLoad The End");
}
outputStream3.close();
return savedName;
}
}
8. Ajax Form Test
- ajaxForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>다양한 Ajax Test</h1>
<a href="/helloText">helloText</a><p>
<a href="/sample/sendVO2?deptno=123">sample/sendVO2(객체)</a><p>
<a href="/sendVO3">sendVO3</a><p>
<a href="/getDeptName?deptno=10">getDeptName(controller)</a><p>
<a href="/listEmpAjaxForm">listEmpAjaxForm(ajax JSP 연동)</a><p>
<a href="/listEmpAjaxForm2">listEmpAjaxForm2(ajax JSP 객체리스트 Get)</a><p>
<a href="/listEmpAjaxForm3">listEmpAjaxForm3(ajax List를 Controller로 전송)</a><p>
</body>
</html>
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// ----------------------------------
// 8. Ajax Form Test
// ajaxForm Test 입력화면
@RequestMapping(value = "ajaxForm")
public String ajaxForm(Model model) {
System.out.println("EmpController ajaxForm Start...");
return "ajaxForm";
}
}
1. helloText
- EmpRestController.class
package com.oracle.oBootMybatis01.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
@RequestMapping("/helloText")
public String helloText() {
System.out.println("EmpRestController Start...");
String hello = "안녕";
// StringConverter
return hello;
}
}
2. sample/sendV02(객체)
- SampleVO.class
package com.oracle.oBootMybatis01.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SampleVO {
private Integer mno;
private String firstName;
private String lastName;
}
- EmpRestController.class
package com.oracle.oBootMybatis01.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.SampleVO;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
// 2. sendVO2
@RequestMapping("/sample/sendVO2")
public SampleVO sendVO2(Dept dept) {
System.out.println("@RestController dept.getDeptno()->"+dept.getDeptno());
SampleVO vo = new SampleVO();
vo.setFirstName("길동");
vo.setLastName("홍");
vo.setMno(dept.getDeptno());
return vo;
}
}
https://mycodingdiary1219.tistory.com/manage/newpost/67?type=post&returnURL=ENTRY
Online JSON Viewer and Formatter에 붙여넣어서 보면↓↓↓
3. sendV03
- EmpRestController.class
package com.oracle.oBootMybatis01.controller;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.SampleVO;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
// 3. sendVO3
@RequestMapping("/sendVO3")
public List<Dept> sendVO3() {
System.out.println("@RestController sendVO3 Start...");
List<Dept> deptList = es.deptSelect();
return deptList;
}
}
- EmpServiceImpl.class
package com.oracle.oBootMybatis01.service;
import java.util.HashMap;
import java.util.List;
import org.springframework.stereotype.Service;
import com.oracle.oBootMybatis01.dao.DeptDao;
import com.oracle.oBootMybatis01.dao.EmpDao;
import com.oracle.oBootMybatis01.dao.Member1Dao;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class EmpServiceImpl implements EmpService {
private final EmpDao ed;
private final DeptDao dd;
private final Member1Dao md;
// 서로 다른 Interface(DAO)를 받은 것이기 때문에 두 개 이상 가능
// (0812)현장 HW 2-1
// +++ Ajax Form Test의 EmpRestController에서 sendVO3와도 연결됨
@Override
public List<Dept> deptSelect() {
List<Dept> deptList = null;
System.out.println("EmpServiceImpl deptSelect Start...");
deptList = dd.deptSelect();
System.out.println("EmpServiceImpl deptSelect deptList.size()->"+deptList.size());
return deptList;
}
Online JSON Viewer and Formatter에 붙여넣어서 보면↓↓↓
4. getDeptName(controller)
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// ----------------------------------
// 8. Ajax Form Test
// ajaxForm Test 입력화면
@RequestMapping(value = "ajaxForm")
public String ajaxForm(Model model) {
System.out.println("EmpController ajaxForm Start...");
return "ajaxForm";
}
// 8-4.getDeptName
// (0819) 현장 HW 1-1
@ResponseBody
// 객체를 돌려주기 위해
@RequestMapping(value = "getDeptName")
public String getDeptName(Dept dept, Model model) {
System.out.println("deptno->"+dept.getDeptno());
String deptName = es.deptName(dept.getDeptno());
System.out.println("deptName->"+deptName);
return deptName;
}
}
- EmpService.interface
package com.oracle.oBootMybatis01.service;
import java.util.HashMap;
import java.util.List;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
public interface EmpService {
// (0819) 현장 HW 1-1. Ajax Form Test
String deptName(int deptno);
}
- EmpServiceImpl.class
package com.oracle.oBootMybatis01.service;
import java.util.HashMap;
import java.util.List;
import org.springframework.stereotype.Service;
import com.oracle.oBootMybatis01.dao.DeptDao;
import com.oracle.oBootMybatis01.dao.EmpDao;
import com.oracle.oBootMybatis01.dao.Member1Dao;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class EmpServiceImpl implements EmpService {
private final EmpDao ed;
private final DeptDao dd;
private final Member1Dao md;
// 서로 다른 Interface(DAO)를 받은 것이기 때문에 두 개 이상 가능
// (0819) 현장 HW 1-2. Ajax Form Test- getDeptName
@Override
public String deptName(int deptno) {
System.out.println("EmpServiceImpl deptName Start");
return ed.deptName(deptno);
}
}
- LogAop.class
package com.oracle.oBootMybatis01.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAop {
// com.oracle.oBootMybatis01.dao package 안의 EmpDao이름을 가진 모든 것
@Pointcut("within(com.oracle.oBootMybatis01.dao.EmpDao*)")
private void pointcutMethod() {
}
@Around("pointcutMethod()")
public Object loggerAop(ProceedingJoinPoint joinPoint) throws Throwable {
String signatureStr = joinPoint.getSignature().toShortString();
System.out.println(signatureStr + " is start.");
long st = System.currentTimeMillis();
try {
// 핵심 관심사(비즈니스 업무)
Object obj = joinPoint.proceed();
return obj;
} finally {
long et = System.currentTimeMillis();
System.out.println(signatureStr + " is finished.");
System.out.println(signatureStr + "경과시간 : " + (et - st));
}
}
@Before("pointcutMethod()")
public void beforeMethod() {
System.out.println("AOP beforeMethod Start...");
}
}
- EmpDao.interface
package com.oracle.oBootMybatis01.dao;
import java.util.List;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
public interface EmpDao {
// (0819) 현장 HW 1-1. Ajax Form Test- getDeptName
String deptName(int deptno);
}
- EmpDaoImpl.class
package com.oracle.oBootMybatis01.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class EmpDaoImpl implements EmpDao {
// Mybatis DB 연동
private final SqlSession session;
// (0819) 현장 HW 1-2. Ajax Form Test- getDeptName
// mapper --> EmpDept(tkDeptName)
@Override
public String deptName(int deptno) {
System.out.println("EmpDaoImpl deptName Start...");
String resultStr = "";
try {
System.out.println("EmpDaoImpl deptName deptno->"+deptno);
resultStr = session.selectOne("tkDeptName", deptno);
System.out.println("EmpDaoImpl deptName resultStr->"+resultStr);
} catch (Exception e) {
System.out.println("EmpDaoImpl deptName Exception->"+e.getMessage());
}
return resultStr;
}
}
- EmpDept.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.oracle.oBootMybatis01.EmpDeptMapper">
<select id="tkDeptName" parameterType="int" resultType="java.lang.String">
SELECT dname
FROM dept
WHERE deptno=#{deptno}
</select>
</mapper>
(여기부터는 실무!⭐)
5. listEmpAjaxForm(ajax JSP 텍스트 연동)
- listEmpAjaxForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
function getDeptName(pDeptno) {
console.log(pDeptno)
// alert("pDeptno->"+pDeptno);
// 행동강령 : Ajax로 부서번호 보내고 부서명 받음
$.ajax(
{
url:"<%=request.getContextPath()%>/getDeptName",
data:{deptno : pDeptno},
dataType: 'text',
success: function(deptName){
// alert("success ajax Data->"+deptName);
$('#deptName').val(deptName); /* input Tag */
$('#msg').html(deptName); /* span id Tag */
}
}
);
}
</script>
</head>
<body>
<h2>회원 정보</h2>
<table >
<tr><th>사번</th><th>이름</th><th>업무</th><th>부서</th><th>근무지</th></tr>
<c:forEach var="emp" items="${listEmp}">
<tr><td>${emp.empno }</td><td>${emp.ename }</td>
<td>${emp.job }</td>
<td>${emp.deptno}
<input type="button" id="btn_idCheck" value="부서명" onmouseover="getDeptName(${emp.deptno })">
</td>
<td>${empDept.loc }</td>
</tr>
</c:forEach>
</table>
deptName: <input type="text" id="deptName" readonly="readonly"><p>
Message : <span id="msg"></span><p>
RestController sendVO2: <input type="text" id="RestDept" readonly="readonly"><p>
RestController sendVO2: sendVO2<input type="button" id="btn_Dept" value="부서명"
onclick="getDept(10)"><p>
</body>
</html>
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// 8-5. listEmpAjaxForm(ajax JSP 연동)
@RequestMapping(value = "listEmpAjaxForm")
public String listEmpAjaxForm(Model model) {
Emp emp = new Emp();
System.out.println("EmpController listEmpAjaxForm Start...");
// Parameter emp ==> page만 추가 Setting
emp.setStart(1); // 시작시 1
emp.setEnd(10); // 시작시 10
List<Emp> listEmp = es.listEmp(emp);
System.out.println("EmpController listEmpAjaxForm listEmp.size()->"+listEmp.size());
model.addAttribute("result", "kkk");
model.addAttribute("listEmp", listEmp);
return "listEmpAjaxForm";
}
}
- EmpService.interface + EmpServiceImpl.class + EmpDao.interface+ EmpDaoImpl.class + Emp.xml
- listEmpAjaxForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
function getDeptName(pDeptno) {
console.log(pDeptno)
// alert("pDeptno->"+pDeptno);
// 행동강령 : Ajax로 부서번호 보내고 부서명 받음
$.ajax(
{
url:"<%=request.getContextPath()%>/getDeptName",
data:{deptno : pDeptno},
dataType: 'text',
success: function(deptName){
// alert("success ajax Data->"+deptName);
$('#deptName').val(deptName); /* Input Tag */
$('#msg').html(deptName); /* span id Tag */
}
}
);
}
function getDept(pDeptno) {
alert("pDeptno->"+pDeptno);
// (0819) 현장HW 2
// sample/sendVO2"
// id : RestDept 결과 값 넣어주기
console.log(pDeptno)
$.ajax(
{
url:"sample/sendVO2",
// EmpRestController의 "/sample/sendVO2"와 연결됨
data:{deptno : pDeptno},
// 위 두 줄은 서버의 Parameter
dataType: 'json',
success: function(sampleVo){
resultStr = sampleVo.firstName + " " + sampleVo.lastName + " " + sampleVo.mno;
alert("ajax getDept resultStr->"+resultStr);
$('#RestDept').val(resultStr); // Input Tag
// 아래의 id="RestDept"와 연결됨
}
}
);
}
</script>
</head>
<body>
<h2>회원 정보</h2>
<table >
<tr><th>사번</th><th>이름</th><th>업무</th><th>부서</th><th>근무지</th></tr>
<c:forEach var="emp" items="${listEmp}">
<tr><td>${emp.empno }</td><td>${emp.ename }</td>
<td>${emp.job }</td>
<td>${emp.deptno}
<input type="button" id="btn_idCheck" value="부서명" onmouseover="getDeptName(${emp.deptno })">
</td>
<td>${empDept.loc }</td>
</tr>
</c:forEach>
</table>
deptName: <input type="text" id="deptName" readonly="readonly"><p>
Message : <span id="msg"></span><p>
RestController sendVO2: <input type="text" id="RestDept" readonly="readonly"><p>
RestController sendVO2: sendVO2<input type="button" id="btn_Dept" value="부서명"
onclick="getDept(10)"><p>
</body>
</html>
- EmpRestController.class
package com.oracle.oBootMybatis01.controller;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.SampleVO;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
// 2. sendVO2
@RequestMapping("/sample/sendVO2")
public SampleVO sendVO2(Dept dept) {
System.out.println("@RestController dept.getDeptno()->"+dept.getDeptno());
SampleVO vo = new SampleVO();
vo.setFirstName("길동");
vo.setLastName("홍");
vo.setMno(dept.getDeptno());
return vo;
}
}
- listEmpAjaxForm.jsp
Tip)- JSON.stringify: jsp에서 stringify로 보내면 아래의 Controller처럼 @RequestBody를 입력하여 풀어서 받아줘야 함
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
// listEmpAjaxForm2(ajax JSP 객체리스트 Get)
function getDeptName(pDeptno) {
console.log(pDeptno)
// alert("pDeptno->"+pDeptno);
// 행동강령 : Ajax로 부서번호 보내고 부서명 받음
$.ajax(
{
url:"<%=request.getContextPath()%>/getDeptName",
data:{deptno : pDeptno},
dataType: 'text',
success: function(deptName){
// alert("success ajax Data->"+deptName);
$('#deptName').val(deptName); /* Input Tag */
$('#msg').html(deptName); /* span id Tag */
}
}
);
}
// listEmpAjaxForm(ajax JSP 텍스트 연동)
function getDept(pDeptno) {
alert("pDeptno->"+pDeptno);
// (0819) 현장HW 2
// sample/sendVO2"
// id : RestDept 결과 값 넣어주기
console.log(pDeptno)
$.ajax(
{
url:"sample/sendVO2",
// EmpRestController의 "/sample/sendVO2"와 연결됨
data:{deptno : pDeptno},
// 위 두 줄은 서버의 Parameter
dataType: 'json',
success: function(sampleVo){
resultStr = sampleVo.firstName + " " + sampleVo.lastName + " " + sampleVo.mno;
alert("ajax getDept resultStr->"+resultStr);
$('#RestDept').val(resultStr); // Input Tag
// 아래의 id="RestDept"와 연결됨
}
}
);
}
function empWriteBtn() {
var empno = $('#empno').val();
// var sendData = JSON.stringify($('#empTrans').serialize());
// alert('sendData->'+sendData);
var sendData = $('#empTrans').serializeArray();
// Json Data Conversion;
sendData3 = jsonParse(sendData);
alert('sendData3->'+sendData3);
console.log('sendData3->'+sendData3);
$.ajax({
url: 'empSerializeWrite',
type: 'POST',
contentType: 'application/json;charset=UTF-8',
data: JSON.stringify(sendData3),
dataType: 'json',
success: function(response) {
if(response.writeResult > 0) {
alert("성공");
}
}
});
}
function jsonParse(sendData2) {
obj = {};
if (sendData2) {
jQuery.each(sendData2, function() {
obj[this.name] = this.value;
alert('this.name->'+this.name);
alert('this.value->'+this.value);
});
}
return obj;
}
</script>
</head>
<body>
<h2>회원 정보</h2>
<table >
<tr><th>사번</th><th>이름</th><th>업무</th><th>부서</th><th>근무지</th></tr>
<c:forEach var="emp" items="${listEmp}">
<tr><td>${emp.empno }</td><td>${emp.ename }</td>
<td>${emp.job }</td>
<td>${emp.deptno}
<input type="button" id="btn_idCheck" value="부서명" onmouseover="getDeptName(${emp.deptno })">
</td>
<td>${empDept.loc }</td>
</tr>
</c:forEach>
</table>
deptName: <input type="text" id="deptName" readonly="readonly"><p>
Message : <span id="msg"></span><p>
RestController sendVO2: <input type="text" id="RestDept" readonly="readonly"><p>
RestController sendVO2: sendVO2<input type="button" id="btn_Dept" value="부서명"
onclick="getDept(10)"><p>
<h2>Serialize Test</h2>
<form name="empTrans" id="empTrans">
<input type="hidden" id="empno" name="empno" value="1234">
<input type="hidden" id="ename" name="ename" value="시리얼">
<input type="hidden" id="sal" name="sal" value="1000">
<input type="button" value="Ajax Serialize 확인" onclick="empWriteBtn()">
</form>
</body>
</html>
- EmpController.class
Tip)- @Valid: 유효성 검증
- @RequestBody로 들어오는 객체에 대한 제약조건을 검증하도록 지시하는 어노테이션
이 검증의 세부적인 사항은 객체 안에 정의를 해두어야 함 - 기본적으로 컨트롤러에서만 동작하며 기본적으로 다른 계층에서는 검증이 되지 않음
- 다른 계층에서 파라미터를 검증하기 위해서는 @Validated와 결합되어야 함
↳ @Validated?- @Validated는 JSR 표준 기술이 아니며 Spring 프레임워크에서 제공하는 어노테이션 및 기능
- 입력 파라미터의 유효성 검증은 컨트롤러에서 최대한 처리하고 넘겨주는 것이 좋지만, 개발을 하다보면 불가피하게 다른 곳에서 파라미터를 검증해야 할 수 있음 => Spring에서는 이를 위해 AOP 기반으로 메소드의 요청을 가로채서 유효성 검증을 진행해주는 @Validated를 제공함
- 사용방법: 클래스에 @Validated를 붙여주고, 유효성을 검증할 메소드의 파라미터에 @Valid를 붙여주면 유효성 검증이 진행됨
- 대표적으로 @RequestBody는 Json 메세지를 객체로 변환해주는 작업인 ArgumentResolver의 구현체인
RequestResponseBodyMethodProcessor가 처리하며,
이 내부에서 @Valid로 시작하는 어노테이션이 있을 경우에 유효성 검사를 진행함
=> 검증에 오류가 있다면 MethodArgumentNotValidException 예외가 발생하게 되고,
디스패처 서블릿에 기본으로 등록된 예외 리졸버(Exception Resolver)인 DefaultHandlerExceptionResolver에 의해 400 BadRequest 에러가 발생함
- @RequestBody로 들어오는 객체에 대한 제약조건을 검증하도록 지시하는 어노테이션
- @Valid: 유효성 검증
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// 8-5. listEmpAjaxForm(ajax JSP 연동)
@RequestMapping(value = "listEmpAjaxForm")
public String listEmpAjaxForm(Model model) {
Emp emp = new Emp();
System.out.println("EmpController listEmpAjaxForm Start...");
// Parameter emp ==> page만 추가 Setting
emp.setStart(1); // 시작시 1
emp.setEnd(10); // 시작시 10
List<Emp> listEmp = es.listEmp(emp);
System.out.println("EmpController listEmpAjaxForm listEmp.size()->"+listEmp.size());
model.addAttribute("result", "kkk");
model.addAttribute("listEmp", listEmp);
return "listEmpAjaxForm";
}
@ResponseBody
@RequestMapping(value = "empSerializeWrite")
public Map<String, Object> empSerializeWrite(@RequestBody @Valid Emp emp) {
System.out.println("EmpController empSerializeWrite Start...");
System.out.println("EmpController empSerializeWrite emp->"+emp);
int writeResult = 1;
// int writeResult = kkk.writeEmp(emp);
// String followingProStr = Interger.toString(followingPro);
Map<String, Object> resultMap = new HashMap<>();
System.out.println("EmpController empSerializeWrite writeResult->"+writeResult);
resultMap.put("writeResult", writeResult);
return resultMap;
}
}
(0820 일지)
6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
1) 조회
- listEmpAjaxForm2.jsp
Tip) varStatus="status": 전체 column에 index를 걸 수 있게 해줌
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function getListDept() {
alert("getListDept Run...");
var str = "";
var str2 = "";
$.ajax(
{
url:"/sendVO3",
dataType:'json',
success:function(deptList) {
var jsonStr = JSON.stringify(deptList);
alert("jsonStr->"+jsonStr);
$('#dept_list_str').append(jsonStr);
str += "<select name='dept'>";
$(deptList).each(
//each: 향상형 for문처럼 작동하여 multirow에 각각 영향을 미치도록 하는 것
function() {
str2 = "<option value='"+this.deptno + "'> "+this.dname+"</option>";
str += str2;
}
)
str += "</select><p>"
alert("combobox str->"+str);
// 맨 아래 combobox와 연결됨
$('#dept_list_combobox').append(str);
}
}
);
}
</script>
</head>
<body>
<h2>회원정보</h2>
<table>
<tr>
<th>번호</th><th>사번</th><th>이름</th>
<th>업무</th><th>부서</th>
</tr>
<c:forEach var="emp" items="${listEmp}" varStatus="status">
<tr id="emp${status.index}"> <td>emp${status.index}</td>
<td>
<input type="hidden" id="deptno${status.index}" value="${emp.deptno}">
<input type="text" id="empno${status.index}" value="${emp.empno}">${emp.empno}<p>
</td>
<td>
<input type="text" id="ename${status.index}" value="${emp.ename}">${emp.ename}<p>
</td>
<td>
${emp.job}
</td>
<td>
${emp.deptno}
<input type="button" id="btn_idCheck2" value="사원 Row Delete"
onclick="getEmpnoDelete(${status.index})">
</td>
</tr>
</c:forEach>
</table>
RestController LISTVO3: <input type="button"
id="btn_Dept3"
value="부서명 LIST"
onclick="getListDept()"><p>
dept_list_str: <div id="dept_list_str"></div>
dept_list_combobox:
<div id="dept_list_combobox"></div>
<!-- combobox안에 값이 통째로 들어감 -->
<h1>The End </h1>
</body>
</html>
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// 8-6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
@RequestMapping(value = "listEmpAjaxForm2")
public String listEmpAjaxForm2(Model model) {
System.out.println("EmpController listEmpAjaxForm2 Start...");
Emp emp = new Emp();
// Paramemter emp ==> Page만 추가 Setting
emp.setStart(1); // 시작시 1
emp.setEnd(15); // 시작시 15
List<Emp> listEmp = es.listEmp(emp);
System.out.println("EmpController listEmpAjaxForm2 listEmp.size()->"+listEmp.size());
model.addAttribute("listEmp", listEmp);
return "listEmpAjaxForm2";
}
}
2) 삭제1: 결과를 text로 받음
- listEmpAjaxForm2.jsp (현장 HW) => 사원 Row Delete 버튼 누르면 데이터 삭제되도록 ajax 로직 완성하기
Tip) JSP에서 stringify가 아닌 객체로 보냈기 때문에, Controller에서 @RequestBody가 아닌 객체 그대로 받아서 사용할 수 있음
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function getListDept() {
alert("getListDept Run...");
var str = "";
var str2 = "";
$.ajax(
{
url:"/sendVO3",
dataType:'json',
success:function(deptList) {
var jsonStr = JSON.stringify(deptList);
alert("jsonStr->"+jsonStr);
$('#dept_list_str').append(jsonStr);
str += "<select name='dept'>";
$(deptList).each(
//each: 향상형 for문처럼 작동하여 multirow에 각각 영향을 미치도록 하는 것
function() {
str2 = "<option value='"+this.deptno + "'> "+this.dname+"</option>";
str += str2;
}
)
str += "</select><p>"
alert("combobox str->"+str);
// 맨 아래 combobox와 연결됨
$('#dept_list_combobox').append(str);
}
}
);
}
function getEmpnoDelete(pIndex) {
// (0820)현장 HW 1-1
// url -> empnoDelete
// parm -> empno : selEmpno
// 성공하면 -> Delete Tag
var selEmpno = $("#empno"+pIndex).val();
var selEname = $("#ename"+pIndex).val();
$.ajax(
{
url: "/empnoDelete",
data: {empno : selEmpno , ename : selEname},
dataType: 'text',
success: function(data) {
alert(".ajax getDeptDelete data->"+data);
if (data == '1') {
// 성공하면 아래 라인 수행하기
$('#emp'+ pIndex).remove(); /* Delete Tag */
}
}
}
);
}
</script>
</head>
<body>
<h2>회원정보</h2>
<table>
<tr>
<th>번호</th><th>사번</th><th>이름</th>
<th>업무</th><th>부서</th>
</tr>
<c:forEach var="emp" items="${listEmp}" varStatus="status">
<tr id="emp${status.index}"> <td>emp${status.index}</td>
<td>
<input type="hidden" id="deptno${status.index}" value="${emp.deptno}">
<input type="text" id="empno${status.index}" value="${emp.empno}">${emp.empno}<p>
</td>
<td>
<input type="text" id="ename${status.index}" value="${emp.ename}">${emp.ename}<p>
</td>
<td>
${emp.job}
</td>
<td>
${emp.deptno}
<input type="button" id="btn_idCheck2" value="사원 Row Delete"
onclick="getEmpnoDelete(${status.index})">
</td>
</tr>
</c:forEach>
</table>
RestController LISTVO3: <input type="button"
id="btn_Dept3"
value="부서명 LIST"
onclick="getListDept()"><p>
dept_list_str: <div id="dept_list_str"></div>
dept_list_combobox:
<div id="dept_list_combobox"></div>
<!-- combobox안에 값이 통째로 들어감 -->
<h1>The End </h1>
</body>
</html>
- EmpRestController.class
Tip) JSP에서 stringify가 아닌 객체로 보냈기 때문에, Controller에서 @RequestBody가 아닌 객체 그대로 받아서 사용할 수 있음
package com.oracle.oBootMybatis01.controller;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.SampleVO;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
// 8-6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
// (0820)현장 HW 1-2
@RequestMapping("/empnoDelete")
public String empnoDelete(Emp emp) {
System.out.println("@RestController empnoDelete Start...");
System.out.println("@RestController empnoDelete emp->"+emp);
int delStatus = es.deleteEmp(emp.getEmpno());
String delStatusStr = Integer.toString(delStatus);
return delStatusStr;
}
}
삭제 버튼 누른 뒤↓↓↓
3) 삭제2: 결과를 객체로 받음
- listEmpAjaxForm2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function getListDept() {
alert("getListDept Run...");
var str = "";
var str2 = "";
$.ajax(
{
url:"/sendVO3",
dataType:'json',
success:function(deptList) {
var jsonStr = JSON.stringify(deptList);
alert("jsonStr->"+jsonStr);
$('#dept_list_str').append(jsonStr);
str += "<select name='dept'>";
$(deptList).each(
//each: 향상형 for문처럼 작동하여 multirow에 각각 영향을 미치도록 하는 것
function() {
str2 = "<option value='"+this.deptno + "'> "+this.dname+"</option>";
str += str2;
}
)
str += "</select><p>"
alert("combobox str->"+str);
// 맨 아래 combobox와 연결됨
$('#dept_list_combobox').append(str);
}
}
);
}
function getEmpnoDelete(pIndex) {
// (0820)현장 HW 1-1
// url -> empnoDelete
// parm -> empno : selEmpno
// 성공하면 -> Delete Tag
var selEmpno = $("#empno"+pIndex).val();
var selEname = $("#ename"+pIndex).val();
// 결과를 text로 받음
/* $.ajax(
{
url: "/empnoDelete",
data: {empno : selEmpno , ename : selEname},
// stringify가 아닌 객체로 보냈기 때문에,
// Controller에서 @RequestBody가 아닌 객체 그대로 받아서 사용할 수 있음
dataType: 'text',
success: function(data) {
alert(".ajax getDeptDelete data->"+data);
if (data == '1') {
// 성공하면 아래 라인 수행하기 --> Delete Tag
$('#emp'+ pIndex).remove();
}
}
}
);
}
*/
// 결과를 객체로 받음
$.ajax(
{
url:"/empnoDelete03",
data:{ empno : selEmpno
, ename : selEname
},
dataType:'json',
success:function(response){
alert(".ajax getDeptDelete data->"+response.delStatus);
if (response.delStatus == '1') {
// 성공하면 아래라인 수행
$('#emp'+pIndex).remove(); /* Delete Tag */
}
}
}
);
}
</script>
</head>
<body>
<h2>회원정보</h2>
<table>
<tr>
<th>번호</th><th>사번</th><th>이름</th>
<th>업무</th><th>부서</th>
</tr>
<c:forEach var="emp" items="${listEmp}" varStatus="status">
<tr id="emp${status.index}"> <td>emp${status.index}</td>
<td>
<input type="hidden" id="deptno${status.index}" value="${emp.deptno}">
<input type="text" id="empno${status.index}" value="${emp.empno}">${emp.empno}<p>
</td>
<td>
<input type="text" id="ename${status.index}" value="${emp.ename}">${emp.ename}<p>
</td>
<td>
${emp.job}
</td>
<td>
${emp.deptno}
<input type="button" id="btn_idCheck2" value="사원 Row Delete"
onclick="getEmpnoDelete(${status.index})">
</td>
</tr>
</c:forEach>
</table>
RestController LISTVO3: <input type="button"
id="btn_Dept3"
value="부서명 LIST"
onclick="getListDept()"><p>
dept_list_str: <div id="dept_list_str"></div>
dept_list_combobox:
<div id="dept_list_combobox"></div>
<!-- combobox안에 값이 통째로 들어감 -->
<h1>The End </h1>
</body>
</html>
- EmpRestController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.SampleVO;
import com.oracle.oBootMybatis01.service.EmpService;
import lombok.RequiredArgsConstructor;
@RestController
// @Controller + @ResponseBody
@RequiredArgsConstructor
public class EmpRestController {
private final EmpService es;
// 8-6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
// 결과를 text로 받음
// (0820)현장 HW 1-2
@RequestMapping("/empnoDelete")
public String empnoDelete(Emp emp) {
// jsp에서 stringify로 보내지 않았기 때문에,
// Controller에서 @RequestBody가 아닌 객체 그대로 받아서 사용할 수 있음
System.out.println("@RestController empnoDelete Start...");
System.out.println("@RestController empnoDelete emp->"+emp);
int delStatus = es.deleteEmp(emp.getEmpno());
String delStatusStr = Integer.toString(delStatus);
return delStatusStr;
}
// 8-6. listEmpAjaxForm2(ajax JSP 객체리스트 Get)
// 결과를 객체로 받음
@RequestMapping("/empnoDelete03")
public Map<String, Object> empnoDelete03 (Emp emp) {
//jsp에서 stringify로 보내면 Controller에서 @RequestBody를 입력하여 풀어서 받아줘야 함
System.out.println("@RestController empnoDelete03 Start...");
System.out.println("@RestController empnoDelete03 emp->"+emp);
int delStatus = es.deleteEmp(emp.getEmpno());
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("delStatus", delStatus);
return resultMap;
}
}
삭제 버튼 누른 뒤↓↓↓
7. listEmpAjaxForm3(ajax List를 Controller로 전송)
1) empLISTTest 전송 버튼
- listEmpAjaxForm3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function getEmpListUpdateTest() {
alert("getEmpListUpdateTest Run...")
// Group 번호 가져오기
var arr = new Array();
var item;
<c:forEach items="${listEmp}" var="item">
arr.push({ empno:"${item.empno}"
, ename:"${item.ename}"
, deptno:"${item.deptno}"
});
</c:forEach>
for (var i=0; i<arr.length;) {
alert("arr.empno->"+ i + " : " + arr[i].empno
+ "arr.ename->"+ i + " : " + arr[i].ename);
i++;
if(i > 2) return;
}
}
</script>
</head>
<body>
<h2>회원 정보3</h2>
<table id="empList">
<tr><th>번호</th><th>사번</th><th>이름</th><th>업무</th><th>부서</th></tr>
<c:forEach var="emp" items="${listEmp}" varStatus="status">
<tr id="empListRow"><td>emp${status.index}</td>
<td>
<input type="hidden" class="deptno" id="deptno" name="deptno" value="${emp.deptno }">
<input type="text" class="empno" id="empno" name="empno" value="${emp.empno }">${emp.empno }</td>
<td><input type="text" class="ename" id="ename" name="ename" value="${emp.ename }">${emp.ename }</td>
<td>${emp.job }</td><td>${emp.deptno }
</td>
</tr>
</c:forEach>
</table>
RestController LISTVO3: <input type="button" id="btn_Dept3"
value="empLISTTest 전송 "
onclick="getEmpListUpdateTest()"><p>
RestController LISTVO3: <input type="button" id="btn_Dept3"
value="empLIST 전송 "
onclick="getEmpListUpdate()"><p>
<h1>The End </h1>
</body>
</html>
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// 8-7. listEmpAjaxForm3(ajax List를 Controller로 전송)
@RequestMapping(value = "listEmpAjaxForm3")
public String listEmpAjaxForm3(Model model) {
System.out.println("listEmpAjaxForm3 Start...");
Emp emp = new Emp();
// Paramemter emp ==> Page만 추가 Setting
emp.setStart(1); // 시작시 1
emp.setEnd(15); // 시작시 15
List<Emp> listEmp = es.listEmp(emp);
System.out.println("EmpController listEmpAjaxForm3 listEmp.size()->"+listEmp.size());
model.addAttribute("listEmp", listEmp);
return "listEmpAjaxForm3";
}
}
2) empLIST 전송 버튼으로 업데이트
- listEmpAjaxForm3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
// JavaScript에서 empList를 수정한 Data를 JSON 데이터로 전환
// empController로 보내기 예제
function getEmpListUpdate() {
alert("getEmpListUpdate Run...")
let empList = [];
const inputs = document.querySelectorAll('input[name="empno"], input[name="ename"], input[name="deptno"]');
for (let i = 0; i <inputs.length; i += 3) {
const empno = inputs[i+1].value;
const ename = inputs[i+2].value;
const deptno = inputs[i+3].value;
// 불러온 값들을 JSON 객체 형태로 만듬
const empItem = {
"empno":empno
, "ename":ename
, "deptno":deptno
};
// alert("ename->"+ename);
// JSON 객체를 배열 안에 넣어둔다
empList.push(empItem);
if(i > 5) break;
}
alert("JSON.stringify(empList)->"+JSON.stringify(empList));
if (empList.length > 0) {
$.ajax({
url: 'empListUpdate',
contentType: 'application/json',
data: JSON.stringify(empList), //JSON 객체를 불러와서 stringify() 함수 안에 배열
method: 'POST',
dataType: 'json',
success: function(response) {
console.log(response.updateResult);
}
});
alert("Ajax empListUpdate 수행...");
}
}
</script>
</head>
<body>
<h2>회원 정보3</h2>
<table id="empList">
<tr><th>번호</th><th>사번</th><th>이름</th><th>업무</th><th>부서</th></tr>
<c:forEach var="emp" items="${listEmp}" varStatus="status">
<tr id="empListRow"><td>emp${status.index}</td>
<td>
<input type="hidden" class="deptno" id="deptno" name="deptno" value="${emp.deptno }">
<input type="text" class="empno" id="empno" name="empno" value="${emp.empno }">${emp.empno }</td>
<td><input type="text" class="ename" id="ename" name="ename" value="${emp.ename }">${emp.ename }</td>
<td>${emp.job }</td><td>${emp.deptno }
</td>
</tr>
</c:forEach>
</table>
RestController LISTVO3: <input type="button" id="btn_Dept3"
value="empLISTTest 전송 "
onclick="getEmpListUpdateTest()"><p>
RestController LISTVO3: <input type="button" id="btn_Dept3"
value="empLIST 전송 "
onclick="getEmpListUpdate()"><p>
<h1>The End </h1>
</body>
</html>
- EmpController.class
package com.oracle.oBootMybatis01.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oracle.oBootMybatis01.model.Dept;
import com.oracle.oBootMybatis01.model.DeptVO;
import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.model.EmpDept;
import com.oracle.oBootMybatis01.model.Member1;
import com.oracle.oBootMybatis01.service.EmpService;
import com.oracle.oBootMybatis01.service.Paging;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class EmpController {
private final EmpService es;
private final JavaMailSender mailSender;
// 2) empLIST 전송 버튼으로 업데이트
@ResponseBody
@RequestMapping(value = "empListUpdate")
public Map<String, Object> empListUpdate(@RequestBody @Valid List<Emp> listEmp) {
System.out.println("EmpController empListUpdate Start...");
int updateResult = 1;
for(Emp emp : listEmp) {
System.out.println("EmpController empListUpdate emp->"+emp);
// int writeResult = kkk.listUpdateEmp(emp);
}
Map<String, Object> resultMap = new HashMap<>();
System.out.println("EmpController empListUpdate updateResult->"+updateResult);
resultMap.put("updateResult", updateResult);
return resultMap;
}
}
질문목록
1. return의 뜻
@RequestMapping("/helloText")
public String helloText() {
System.out.println("EmpRestController Start...");
String hello = "안녕";
// StringConverter
return hello;
}
- 자바에서 메서드가 실행을 마친 후 호출한 쪽으로 값을 돌려보내는 것 혹은 값을 반환한다는 것을 의미함
- 메서드가 작업을 끝내고 그 결과를 반환하는 것
- 위의 로직을 예시로 보자면, return hello;는 helloText() 메서드가 끝나면서 "안녕"이라는 문자열을 호출한 쪽으로 반환(되돌려주는)한다는 뜻. 이 경우 "안녕"이란 값이 메서드의 최종 결과로서 사용될 수 있도록 전달됨.
- 이 메서드가 호출된다면, 호출된 곳에서는 "안녕"이라는 값을 사용할 수 있게 됨
2. new의 뜻
@RequestMapping("/sample/sendVO2")
public SampleVO sendVO2(Dept dept) {
System.out.println("@RestController dept.getDeptno()->"+dept.getDeptno());
SampleVO vo = new SampleVO();
vo.setFirstName("길동");
vo.setLastName("홍");
vo.setMno(dept.getDeptno());
return vo;
}
↳ 먼저 이 로직에 대한 설명
: SampleVO 객체인 vo를 new SampleVO()로 생성한 후, 그 객체의 firstName, lastName, mno 필드에 값을 설정,
그 다음 vo 객체를 반환함.
즉, 이 메서드가 실행되면 호출한 쪽에서는 SampleVO 객체를 받을 수 있으며, 그 객체에는 "길동", "홍", dept.getDeptno()에서 가져온 값이 각각 설정되어 있는 상태가 됨.
1. 코드 뜯어보기: SampleVO 클래스의 새 객체를 생성하고, 그 객체를 vo라는 변수에 저장한다는 뜻
SampleVO(클래스의 이름) vo(SampleVO 객체에 대한 참조(레퍼런스)를 저장할 변수)
= new(새로운 객체를 생성할 때 사용됨) SampleVO()(SampleVO 클래스의 생성자를 호출하여 새로운 SampleVO 객체를 만드는 과정);
2. new 연산자를 사용하는 이유
=> 새로 객체를 생성해서 그 객체의 속성이나 메서드에 접근하기 위해서⭐⭐
- 기본 자료형(int, char, boolean 등)을 사용할 때는 필요 없지만, 클래스(참조 자료형)로 객체를 생성할 때는 반드시 new를 사용해야 함
- 클래스의 설계도를 기반으로 메모리에 새로운 객체를 생성하고, 그 객체에 대한 참조를 변수에 저장할 수 있도록 함
ex) SampleVO vo; => 이 선언 만으로는 변수 vo에 아무런 값이 할당되지 않으며, vo는 단지 SampleVO 타입의 객체를 가리킬 수 있는 참조 변수로 선언된 상태일 뿐임 - 자바에서 새로운 객체를 메모리에 생성할 때 사용됨
객체는 클래스라는 설계도를 기반으로 만들어지는데, new 연산자를 통해 이 설계도를 토대로 실제로 메모리에 객체를 할당하게 됨
+ 이 과정에서 객체의 생성자가 호출되어 객체의 초기화 작업도 이루어짐 - 객체가 생성되면 그 객체의 속성(필드)이나 메서드(동작)에 접근할 수 있음
예를 들어, SampleVO vo = new SampleVO();라고 하면, 이제 vo를 통해 SampleVO 클래스에 정의된 모든 속성과 메서드에 접근할 수 있게 됨 - new 연산자를 활용하면 독립적인 객체를 여러 개 만들 수 있음. 이로 인해 만들어진 객체들 각각이 고유한 데이터를 가지거나 독립적인 동작을 할 수 있도록 할 수 있음
ex) SampleVO vo1 = new SampleVO();와 SampleVO vo2 = new SampleVO();는 SampleVO 클래스의 두 개의 독립된 객체를 생성함. 이 두 객체는 서로 다른 메모리 위치에 존재하며, 각각 독립적인 상태를 가짐 - new 연산자를 사용하지 않는 방법: 이미 생성된 객체 재사용, 싱글톤 패턴 사용, 팩토리 메서트 패턴
3. new 연산자의 앞 뒤의 이름은 같아야하나? => 같아야함
- 앞의 클래스 이름: 변수가 어떤 타입의 데이터를 가리킬 수 있는지를 지정해줌.
위의 경우에서 vo 변수는 SampleVO 타입의 객체를 참조할 수 있음.
즉, vo는 SampleVO 클래스의 객체를 가리키는 변수로 선언된 것 - 뒤의 새로 선언된 객체 이름: 실제로 SampleVO 클래스의 새로운 객체를 생성하는 구문임.
new 키워드와 함께 사용하여 SampleVO 클래스의 생성자를 호출하고, 그 결과로 SampleVO 객체를 만들어냄.
수업교재
1. FrameWork ⭐⭐
3. AOP (Aspect Oriented Programming)
1. 개념
- 관점(Aspect) 지향 프로그래밍, 즉 관점을 기준으로 다양한 기능을 분리하여 보는 프로그래밍
↳ 관점?: 부가 기능과 그 적용처를 정의하고 합쳐서 모듈로 만든 것
↳ 관점지향?: 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 (공통된 로직이나 기능을 하나의 단위로 묶는 것)것 - AOP의 목적
- 부가 기능을 핵심 기능에서 분리해 한 곳으로 관리하도록 하고, 이 부가 기능을 어디에 적용할지 선택하는 기능을 합한 하나의 모듈
=> 기존 프로젝트에 부가 기능을 추가할 때, 부가 기능을 적용해야할 클래스가 100개라면 100개에 모두 똑같은 부가 기능 코드를 추가 해야하며 단순 호출이 아닌 구문이라면 더욱 복잡해짐.
이를 위해 AOP를 사용하여 관리를 용이하게 함 - 객체지향 프로그래밍을 보완하기 위해 쓰임
- 핵심 비즈니스 로직의 코드와 횡단 관심사의 코드를 분리하여, 코드의 간결성을 높이고 변경에 유연함과 무한한 확장이 가능하도록 하는 것
- 소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 발견할 수 있는 데 이것을 흩어진 관심사 (Crosscutting Concerns)라 부르는데, 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 취지
- 부가 기능을 핵심 기능에서 분리해 한 곳으로 관리하도록 하고, 이 부가 기능을 어디에 적용할지 선택하는 기능을 합한 하나의 모듈
2. 특징
- 모듈화: 횡단 관심사를 포괄적이고 체계적으로 모듈화
- 캡슐화: 횡단 관심사는 Aspect라는 새로운 단위로 캡슐화하여 모듈화가 이루어짐
- 단순화: 핵심 모듈은 더 이상 횡단 관심사의 모듈을 직접 포함하지 않으며 횡단 관심사의 모든 복잡성은 Aspect로 분리
- 스프링 빈에만 AOP를 적용 가능
- 프록시 패턴 기반의 AOP 구현체, 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위해서임
3. AOP 적용 방식
- 표준자바 클래스: 스프링의 경우 스프링 내에서 작성되는 모든 Advice는 표준 자바 클래스로 작성
- Runtime 시점에서의 Advice 적용: Spring에서는 자체적으로 런타임 시에 위빙하는 “프록시 기반의 AOP”를 지원
즉, 컴파일, 클래스 로딩, main() 메서드의 실행 이후에 자바가 제공하는 범위내에 부가 기능을 적용하는 방식임.
이미 런타임 중이라 코드를 조작하기 어려워 스프링, 컨테이너, DI, 빈 등 여러 개념과 기능을 총동원 - AOP 연맹의 표준 준수: 스프링은 AOP연맹의 인터페이스를 구현
- 메소드 단위 조인포인트만 제공: 필드 단위의 조인포인트 등 다양한 조인포인트를 제공해 주는 AspectJ와 다르게 메소드 단위 조인포인트만 제공
- 스프링에서 AOP 구현 방법 : proxy를 이용하여 부가 기능을 적용함
다만, 프록시는 메서드 실행 시점에서만 다음 타겟을 호출할 수 있기 때문에 런타임 시점에 부가기능을 적용하는 방식은 메서드의 실행 지점으로 제한됨
4. 주요 요소
- 핵심 관심(Core Concern)
- 시스템이 추구하는 핵심 기능 및 가치
- Business 업무
- 횡단 관심(Cross-cutting)
- 핵심 관심에 공통적으로 적용되는 부가적인 요구사항
- 보안, 인증, 로그작성, 정책 적용 등
- Cross-cutting Concern
- Aspect
- 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함
- 프로그램의 핵심관심사에 걸쳐 적용되는 공통 프로그램의 영역, 즉 여러 객체에 공통으로 적용되는 기능
- 특정 상황(point-cut)과 그 상황에서 수행할 작업 (advice)의 집합
- Point-cut 과 Advice를 합쳐 놓은 클래스 형태의 코드
- 특정 관심사에 관련된 코드만을 캡슐화
- Target
- Aspect를 적용하는 곳 (클래스, 메서드 등)
- Joint Point
- 관심사를 구현한 코드에 끼워 넣을수 있는 프로그램의 Event
- Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
- 애플리케이션 실행 흐름에서의 특정 포인트 (ex. 클래스 초기화, 메서드 호출, 예외 발생 등)
- AOP를 적용할 수 있는 모든 지점 (스프링에서는 메서드 실행 지점으로 제한)
- Point-Cut
- JointPoint의 상세한 스펙을 정의한 것
- 관심사가 주 프로그램의 어디에 횡단할 것인지를 나타내는 위치
- - aop:pointcut id=“pub1” expression=“within(com.oracle.aop1.St*)” />
- Advice
- 관심사를 구현하는 코드, 결합점에 삽입되어 동작할수 있는 코드
- 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
- Point-cut에 의해 매칭된 joint point에 실행할 작업
- BEFORE, AROUND, AFTER의 실행 위치 지정
- Weaving
- Joint Point 에 해당하는 Advice를 삽입하는 과정
- Aspect와 핵심 관심사를 엮는(weave)것을 의미
오늘의 숙제
'Spring' 카테고리의 다른 글
2024_08_21_수_SPRING 시험(클라우드 서버 백엔드 개발) (0) | 2024.08.21 |
---|---|
2024_08_20_화~08_21_수 (0) | 2024.08.20 |
2024_08_14_수 (0) | 2024.08.14 |
2024_08_13_화 (0) | 2024.08.13 |
2024_08_12_월 (0) | 2024.08.12 |