📌 Aspect

1. 패키지 com.aa.test.aspect 추가
2. 클래스 AutoAspect.java 추가
3. 어노테이션 @Aspect 추가
4. 어노테이션 @Component 추가
5. 아래 코드 추가

	// 반환 Type : ModelAndView
    // 메소드 이름 : auth 로 시작하는 메소드
    // 인자값 : 상관없음
   @Around("execution(org.springframework.web.servlet.ModelAndView auth*(..))")
   public Object loginCheck(ProceedingJoinPoint joinPoint) throws Throwable {
      Object[] args=joinPoint.getArgs();
      //메소드에 전달된 인자중에서 HttpServletRequest 객체를 찾는다.
      for(Object tmp:args) {
      	 // instanceof : "만일 tmp가 HttpServletRequest Type 이라면"을 의미
         if(tmp instanceof HttpServletRequest) {
            //찾았으면 원래 type 으로 casting
            HttpServletRequest request=(HttpServletRequest)tmp;
            //HttpSession 객체의 참조값 얻어와서 로그인 여부를 알아낸다.
            String id=(String)request.getSession().getAttribute("id");
            if(id == null) {//만일 로그인을 하지 않았으면
               //원래 가려던 url 정보 읽어오기
               String url=request.getRequestURI();
               //GET 방식 전송 파라미터를 query 문자열로 읽어오기 ( a=xxx&b=xxx&c=xxx )
               String query=request.getQueryString();
               //특수 문자는 인코딩을 해야한다.
               String encodedUrl=null;
               if(query==null) {//전송 파라미터가 없다면 
                  encodedUrl=URLEncoder.encode(url);
               }else {
                  // 원래 목적지가 /test/xxx.jsp 라고 가정하면 아래와 같은 형식의 문자열을 만든다.
                  // "/test/xxx.jsp?a=xxx&b=xxx ..."
                  encodedUrl=URLEncoder.encode(url+"?"+query);
               }

               //로그인 페이지로 리다일렉트 할수 있는 ModelAndView 객체를 생성해서 
               ModelAndView mView=new ModelAndView();
               mView.setViewName("redirect:/users/loginform.do?url="+encodedUrl);
               return mView;
            }
         }
      }

      //로그인을 했으면 아래의 코드가 수행되고 ModelAndView 객체가 Object type 으로 리턴된다. 
      Object obj=joinPoint.proceed();	//  aspect가 적용된 메소드를 정상 수행함

      return obj;
   }
   @Around("execution(java.util.Map auth*(..))")
   public Object loginCheckAjax(ProceedingJoinPoint joinPoint) throws Throwable {
      Object[] args=joinPoint.getArgs();
      //메소드에 전달된 인자중에서 HttpServletRequest 객체를 찾는다.
      for(Object tmp:args) {
         if(tmp instanceof HttpServletRequest) {
            //찾았으면 원래 type 으로 casting
            HttpServletRequest request=(HttpServletRequest)tmp;
            //HttpSession 객체의 참조값 얻어와서 로그인 여부를 알아낸다.
            String id=(String)request.getSession().getAttribute("id");
            if(id == null) {//만일 로그인을 하지 않았으면
               //예외를 발생 시켜서 정상적인 응답을 받을수 없도록 한다.
               throw new RuntimeException("로그인이 필요 합니다.");
            }
         }
      }

      //로그인을 했으면 아래의 코드가 수행되고 ModelAndView 객체가 Object type 으로 리턴된다. 
      Object obj=joinPoint.proceed();

      return obj;
   }

  • 설명
    - @Around("aspectj expression")
    - execute(ModelAndView auto *(..) 이라는 aspectj expression 은 리턴 Type이 ModelAndView 이고 메소드의 이름은 auth 로 시작하는 모든 메소드를 가리킨다.
    - 따라서 Spring 이 관리하는 객체중에 위의 모양을 가지고 있는 메소드는 위에 작성한 aspect가 적용된다.


📌 Spring Framework의 핵심 개념

  • DI (Dependency Injection)
    - 필요한 핵심 의존 객체를 주입 받아서 사용하는 구조

  • IOC (Inversion Of Controll) [제어의 역전]
    - 객체의 생성과 조립(DI 등), 관리를 스프링이 실행될 때 알아서 동작하는 구조

  • AOP (Aspect Oriented Programming) [관점 지향적 프로그래밍]
    - 핵심 비즈니스 로직과는 상관없는 횡단관심사따로 작성해 놓고 원하는 위치에 따로 작성된 로직을 설정을 통해서 적용 시키는 구조이다.
    - 주로 로그 기록이나, 테스트, 인증(Authentication) 등의 작업을 할 때 사용한다.
  • AOP는 왜 사용할까?
    수십 개의 혹은 수백 개의 메소드에 핵심 비즈니스 로직과는 상관없는 작업을 해야 한다면 해당 작업을 따로 만들어 놓고 원하는 메소드에 설정만으로 적용시킬 수 있다면 편리하지 않을까?

즉, 핵심 비즈니스 로직(특정 메소드에서 하기로 했던 작업) 이외의 작업이 발생할 때, 해당 작업을 처리하는 로직을 복사 붙여넣기 하면 너무 불편함
여기서 AOP는 해당 로직을 따로 작성해서 원하는 위치에 간단하게 적용시킬 수 있다.
따로 작성했기 때문에 유지 관리가 편리하다.

  • 예시
    - 수백 개의 핵심 비즈니스 로직을 작성한 메소드가 존재함
    - 모든 메소드에 로그 기록을 추출할 분리된 로직을 추가해야함
    - 수백번의 복사 붙여넣기를 통해 해당 코드를 추가? ㄴㄴ
    - 여기서 AOP를 사용해서 따로 작성한 로그 기록 추출 코드를 원하는 메소드들에 추가함


📌 Aspect의 역할

  • 역할
    - 특정 메소드 호출 시, 전달 파라미터 확인
    - 특정 메소드에서 반환 시, 반환값 확인
    - 따로 작성한 메소드를 어떤 시점에 적용할 것인지를 지정


📌 Aspect 용어 설명

  • @Around
    - 특정 메소드 주변
    - 특정 메소드 입력 값, 출력 값을 확인하기 위한 어노테이션


📌 Aspectj Expression

  • Aspectj Expression
    1. execution(* *(..)) => 접근 가능한 모든 메소드가
    point cut



    2. execution( test.service..*(..))
    => test.service 패키지의 모든 메소드 point cut



    3. execution(void insert*(..))
    =>리턴 type 은 void 이고 메소드명이
    insert 로 시작하는 모든 메소드가 point cut



    4. execution( delete(*))
    => 메소드 명이 delete 로 시작하고 인자로 1개 전달받는
    메소드가 point cut (aop 가 적용되는 위치)



    5. execution( delete(,))
    => 메소드 명이 delete 로 시작하고 인자로 2개 전달받는
    메소드가 point cut (aop 가 적용되는 위치)



    6. execution(String update(Integer,))
    => 메소드 명이 update 로 시작하고 리턴 type 은 String
    메소드의 첫번째 인자는 Integer type, 두번째 인자는 아무 type 다되는
    메소드가 point cut (aop 가 적용되는 위치)


📌 파일 업로드

  • MultipartFile
    form으로부터 전송된 파일 정보를 갖는 객체

    • 파일을 서버에 업로드하는 절차

    1. 파라미터로 MultipartFile 객체를 정의

    2. getOriginalFileName() 메소드를 사용해 원래 파일 이름을 변수에 저장

    3. System.currentTimeMillis()+orgFileName 을 통해 실제 저장될 파일 이름을 저장

    4. getSize() 메소드를 사용해 파일 크기를 변수에 저장

    5. req.getServletContext().getRealPath("/upload") 메소드를 사용해 실제 저장할 경로를 변수에 저장

    6. File upload = new File("/upload") 객체를 생성해 upload 폴더가 없다면 upload.mkdir()를 통해 폴더 생성

    7. realPath + File.seperator + saveFileName 으로 실제 저장할 경로에 실제 저장 파일 이름 문자열을 합쳐 savePath 변수에 저장

    8. MultipartFile 객체의 transferTo(new File (실제 저장될 경로+실제 저장 파일이름)) 메소드로 실제 저장할 경로+실제 저장 파일이름의 File 객체를 파라미터로 보내 파일을 저장한다.

@RequestMapping("/file/upload.do")
public ModelAndView upload(String title, MultipartFile myFile, HttpServletRequest req) {
	// 원래 파일 이름 저장
	String orgFileName = myFile.getOriginalFilename();

	// 저장할 폴더에서 중복되지 않도록 시간 문자열을 붙인 파일 이름 저장
	String saveFileName = System.currentTimeMillis()+orgFileName;

	// 파일 크기 저장
	long fileSize = myFile.getSize();

	// upload 폴더까지의 Context 실제 경로를 GET
	String realPath = req.getServletContext().getRealPath("/upload");
	// Context의 실제 경로에 있는 upload 폴더의 객체 생성
	File upload = new File(realPath);
	if(!upload.exists()) {
		upload.mkdir();
	}

	String savePath = null;
	try {
		 //파일을 저장할 전체 경로를 구성한다.  
         savePath=realPath+File.separator+saveFileName;
         //임시폴더에 업로드된 파일을 원하는 파일을 저장할 경로에 전송한다.
         myFile.transferTo(new File(savePath));
	} catch(Exception e) {
		e.printStackTrace();
	}

	ModelAndView mav = new ModelAndView();
	mav.addObject("title", title);
	mav.addObject("orgFileName", orgFileName);
	mav.addObject("saveFileName", saveFileName);
	mav.addObject("fileSize", fileSize);
	mav.addObject("savePath", savePath);
	mav.setViewName("file/upload");

	return mav;
}


profile
데이터 사이언티스트를 목표로 하는 개발자

0개의 댓글