GDJ 24/05/31 (Lombok Annotaion, Interface)

kimuki·2024년 5월 31일
클래스 간 결합도 낮추기

interface를 구현하여 사용하고자 하는 클래스에 implements 시키고
>> 추상화 작업
다형성을 이용하여 결합도를 낮춘다 (결합도를 없앨 수는 없음, 낮출뿐)

개발자가 아니라 Spring Framework가 객체를 제어한다 IOC(제어의 역전)
DI(의존성 주입)

@Autowired
(Spring Bean에 등록되어 관리하고 있는 Data만 @Autowired로 주입 가능)

1. @Bean 사용
Spring이 Autowired 애너테이션을 사용하려면 Bean에 객체가 등록이
되어 있어야 한다
따라서 Application.java 파일에 @Bean을 사용하여
강제로 스프링빈에 객체를 추가해주면 사용할 수 있다

2. @Component 사용
Application.java 파일의 @ComponentScan 어노테이션이
프로젝트 파일 안의 @Component 어노테이션이 붙은
모든 클래스를 찾아서 검색하고 찾게되면 Bean에 등록하여 사용할 수 있게함

단, @Component 어노테이션을 사용하려면 어노테이션을 붙일
클래스 파일과 코드가 필요한데 그것이 없는 경우가 있기 때문에
1번처럼 @Bean을 사용하여 강제로 Bean에 등록해서 사용하는 방식이 필요하다

다른 패키지에 있는 클래스를 @Component를 사용하여 찾도록 하려면
Application.java 파일의 클래스에 @ComponentScan(basePackages = {패키지명}
을 사용하여 접근할 수 있도록 해야한다.

두 개 이상의 클래스에 @Component를 사용하는 경우
Bean으로 뭘 사용할지 몰라 충돌이 일어나기 때문에 이 경우에는
Bean으로 사용할 Data의 이름을 @Autowired에 따로 명시해주어야 한다.


Spring Framework - DI, AOP
+
Spring @mvc Framework - MVC
+
MyBatis Framework - JDBC(ORM Framework 아님)
+
Log4J Framework - 디버깅


# AOP (관점지향프로그래밍) - Spring API 사용
@Component 어노테이션 필요

특정 Method의 실행 순서를 가로채서 먼저실행(@Before) 또는 나중실행(@After)
AOP class에 @Aspect 어노테이션 적용후 @Before, @After 사용

# Interceptor - Spring MVC API 사용
Interceptor : @Controller의 실행 순서를 가로채기

Interceptor로 사용할 class 생성 후
HandlerInterceptor Interface를 상속받아 구현하여 사용
Default method 인 preHandle()와 postHandle() 메서드 오버라이딩 추가

Application.java 클래스가 WebMvcCOnfigurer interface를 상속받도록 하고
Default method인 addInterceptors method 오버라이딩 추가하여
어떤 Controller를 가로채어 작업할 것인지에 대한 코드 작성

# Filter - 서블릿 요청 - Servlet API 사용
Filter : 웹 요청 URL을 가로채기


Question. Filter, Interceptor, AOP가 모두 적용된다면?
Filter > Interceptor > AOP > method > AOP > Interceptor > Filter 순서

출력결과 >>
===== Before Filter =====
===== before Intercept =====
===== before AOP =====
member b
===== after AOP =====
===== after Intercept =====
===== After Filter =====


# 웹 요청 (HttpServletRequest)이 들어오면 Interceptor를 사용하면 됨
HttpServletRequest 의 부모격인 servletRequest 가 들어오면 Filter 사용

# Transaction

Service 단에서 conn을 설정하고
DAO에서 2개의 sql 실행

트랜잭션 실패시 Service 단에서 rollback

Controller 단에서 트랜잭션을 설정하게 되면 쿼리가 실패했을 때
Controller 의 요청분석과 view 처리마저 모두 취소되는 문제가 발생한다.

그래서 Service단을 만들어 Controller와 Mapper(DAO)사이에
중계역할을 하도록 만들어 Service에서 트랜잭션이 수행되도록 한다.

이 때 Service와 DAO를 class name으로 접근하는 방식은 결합도가 높기 때문에
interface를 사용하여 결합도를 줄이는것이 좋은 방법이다.
단, 프로젝트 때는 interface 하지 말것..

Interface를 이용한 다형성

Unit.java (main 함수)

package Unit;

public class Unit {
		Weapon x = new Knife();
	
		public void fight() {
			System.out.println(x.getName() + "(으)로 싸우다");
		}

	public static void main(String[] args) {
		Unit unit = new Unit();
		unit.fight();
	}
}

Weapon.java (Interface)

package Unit;

public interface Weapon {
	
	public String getName();
}

Gun.java (Weapon Interface 상속)

package Unit;

public class Gun implements Weapon{
	
	@Override
	public String getName() {
		return "총";
	}
}

Knife.java (Weapon Interface 상속)

package Unit;

public class Knife implements Weapon{
	
	@Override
	public String getName() {
		return "칼";
	}
}

Unit.java 실행결과 (Console)

칼(으)로 싸우다.

Filter & Interceptor & AOP

HelloController.java (Controller)

package com.gd.AOPTest;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController		// view 가 없는 컨트롤러
public class HelloController {
	
	@GetMapping("/a")
	public void a() {
		System.out.println("a");
	}
	
	// Filter + Interceptor + AOP
	@GetMapping("/member/b")
	public void b() {
		System.out.println("member b");
	}
	
	/*
	@GetMapping("/admin/c")
	public void c() {
		System.out.println("admin c");
	}
	*/
}

AopTestApplication.java (main 함수)

package com.gd.AOPTest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@ServletComponentScan	// Servlet 에서 사용하는 어노테이션도 Spring 이 작업하겠다
@SpringBootApplication
public class AopTestApplication implements WebMvcConfigurer{
	
	@Autowired
	MyInterceptor myInterceptor;

	public static void main(String[] args) {
		SpringApplication.run(AopTestApplication.class, args);
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		
		// Intercept 할 Controller 의 Mapping 주소(url)
		registry.addInterceptor(myInterceptor).addPathPatterns("/member/*");
		
		WebMvcConfigurer.super.addInterceptors(registry);
	}
}

MyFilter.java (Filter)

package com.gd.AOPTest;

import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

@WebFilter("/member/*")	// member 으로 시작하는 모든 요청에 대해서 Filter 를 적용하겠다	
// @WebFilter 어노테이션은 servlet(Tomcat) 것임
// Spring 이 처리할 수 있도록 Application.java 파일에 추가 어노테이션(@ServletComponentScan) 필요

public class MyFilter implements Filter{
	// 아무 내용이 없으면 클래스명 아래 에러 발생
	// interface 를 상속 받았기 때문에
	// 1. 추상클래스가 되던가 (abstract)
	// 2. 추상메서드를 작성하던가

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		request.setCharacterEncoding("UTF-8");

		System.out.println("===== Before Filter =====");
		
		// this.doFilter(request, response, chain);	--> 재귀호출 (자신이 자신을 호출 --> 무한루프)
		chain.doFilter(request, response);
		
		System.out.println("===== After Filter =====");
	}
}

MyInterceptor.java (Interceptor)

package com.gd.AOPTest;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor{
	
	@Override	// 특정 Controller 의 실행을 가로채어 preHandler method 가 먼저 실행되도록 하는 어노테이션
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		request.setCharacterEncoding("UTF-8");
		
		System.out.println("===== before Intercept =====");
		
		return HandlerInterceptor.super.preHandle(request, response, handler);
	}
	
	@Override	// 특정 Controller 의 실행이 끝나면 postHandler method 가 실행되도록 하는 어노테이션
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		
		System.out.println("===== after Intercept =====");
		
		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
	}
}

MyAOP.java (AOP)

package com.gd.AOPTest;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAOP {
	
	// 특정 method 의 실행을 가로채어 @Before method 가 먼저 실행되도록 하는 어노테이션
	// method 전체 경로 작성, a(..)는 'a 라는 method 안에 어떤 인자가 오던지 전부' 라는 뜻
	@Before("execution(* com.gd.AOPTest.HelloController.b(..))")
	public void beforePrint() {
		System.out.println("===== before AOP =====");
	}
	
	// 특정 method 의 실행이 끝나면 @After method 가 실행되도록 하는 어노테이션
	@After("execution(* com.gd.AOPTest.HelloController.b(..))")
	public void afterPrint() {
		System.out.println("===== after AOP =====");
	}
	
	/*
	
	출력결과 : 
	
	===== before AOP =====
	a
	===== after AOP =====
	  
	*/
}

하나의 메서드에

Question. Filter, Interceptor, AOP가 모두 적용된다면?

Filter(before) > Interceptor(preHandler) > AOP(before) >
method > AOP(after) > Interceptor(postHandler > Filter(after) 순서로 적용

출력결과 >>
===== Before Filter =====
===== before Intercept =====
===== before AOP =====
member b
===== after AOP =====
===== after Intercept =====
===== After Filter =====


Transaction

MemberController.java (Controller)

package com.gd.transaction.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.gd.transaction.service.IMemberService;

@RestController
public class MemberController {
	
	@Autowired
	IMemberService memberService;
	
	@GetMapping("changePw")
	public void changePw() {
		// 요청처리
		memberService.addPw();
		// 뷰 응답
	}
}

MemberService.java (Service)

package com.gd.transaction.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gd.transaction.repository.IMemberDAO;

@Service
@Transactional
public class MemberService implements IMemberService {
	
	@Autowired
	IMemberDAO memberDAO;

	public void addPw() {
		memberDAO.insertPw();
		memberDAO.updatePw();
	}
}

IMemberService.java (Service.Interface)

package com.gd.transaction.service;

public interface IMemberService {
	
	public void addPw();
}

MemberDAO.java (DAO)

package com.gd.transaction.repository;

import org.springframework.stereotype.Repository;

@Repository
public class MemberDAO implements IMemberDAO{
	
	public int insertPw() {
		System.out.println("변경된 PW 입력 메서드");
		return 0;
	}
	
	public int updatePw() {
		System.out.println("사용자 테이블 PW 변경 메서드");
		return 0;
	}
}

IMemberDAO.java (DAO.Interface)

package com.gd.transaction.repository;

public interface IMemberDAO {
	
	public int insertPw();
	
	public int updatePw();
}
profile
Road OF Developer

0개의 댓글