springboot 연습 01

Updated:

SpringBoot (1장 ~ 5장)

히카리CP란?

스프링 부트 2.0.0 M2 버전부터 기본적으로 사용되는 커넥션 풀이 톰캣에서 히카리 CP로 변경됨.

최근 몇 년 사이에 급격히 사용이 많아짐. 빠른 속도가 장점

커넥션 풀이란?

애플리케이션과 DB를 연결할 때 효과적으로 관리하기 위해 사용하는 라이브러리

상용 웹 애플리케이션 서버(WAS)를 사용한다면 일반적으로 제조사사에 제공되는 커넥션 풀을 사용

그 외 오픈 소스로 Tomcat JDBC, BoneCP 등이 사용됨

logback

몇년 전까지 자바 프로젝트에서 많이 사용되는 로그 라이브러리는 Log4j였다. 현재는 개발이 종료되어 이를 바탕으로 현재 더 깊은 프로젝트로 개발된 것이 Logback이다. 장점은 다음과 같다.

*오랫동안 널리 사용되고 검증된 Log4j를 기반으로 다시 만들어짐

*Log4j보다 성능이 10배 빠르고 메모리 사용량도 적다

*로그 설정이 변경되어도 서버를 재시작하지 않고 바로 반영

Logback의 로깅 구현체 하나로 slf4j(Simple Logging Facade for Java)를 함께 사용한다.

로그 레벨
로그레벨 설명
trace 모든 로그 출력
debug 개발할 때 디버그 용도
info 상태 변경 등과 같은 정보성 메시지
warn 프로그램은 문제 없지만 추후 시스템 에러의 원인이 될수 있는 경고성 메시지
error 요청을 처리하던 중 문제가 발생한 것 의미
로그back xml 설정
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration debug="true">
	<!-- Appenders -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<Pattern>%d %5p [%c] %m%n</Pattern>
			<!-- 로그 출력 형식 debug용으로 출력 -->
		</encoder>   
	</appender>
	<!-- appender는 로그를 어디에 출력할지(콜솔, 파일기록, DB저장 등) 결정하는 역할 -->
	<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<Pattern>%d %5p %m%n</Pattern>
			<!-- 로그 출력 형식 info용으로 출력 -->
		</encoder>   
	</appender>
	
	<!-- 로거 : 로그를 출력하는 요소로 level 속성을 통해서 출력할 로그의 레벨을 조절하여 appender에 전달. DEBUG level은 위에 appender의 console이라는 이름에 전달한다라는 뜻 -->
	<logger name="board" level="DEBUG" appender-ref="console"/>	
    
    
    
	<logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog"/>
	<logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog"/>
	<logger name="org.springframework" level="error"/>
	<logger name="org.springframework.jdbc" level="error"/>
	
	<!-- 루트 로거 -->
    <root level="error">
        <appender-ref ref="console"/>
    </root>
</configuration>
로그백 코드에서 실행 할 경우
private Logger log = LoggerFactory.getLogger(this.getClass()); // logback 선언
// 아님 @Slf4j 어노테이션 사용

log.debug("oepnBoardList"); // 호출되는 메소드에 로그 사용
log4jdbc로 쿼리 로그 정렬
implementation group: 'org.bgee.log4jdbc-log4j2', name: 'log4jdbc-log4j2-jdbc4.1', version: '1.16'
//build.gradle에 라이르러리 추가
//log4jdbc-remix라는 라이르러리가 있었으나 개발이 종료. 이후에 log4j2가 사용
log4jdbc가 제공하는 로거 종류
로거 설명
jdbc.sqlonly sql 보여줌. prepared statement의 경우 관련된 파라미터 자동으로 변경되어 출력
jdbc.sqltiming sql 과 해당 sql문 실행시간을 보여줌
jdbc.audit resultsets 제외한 모든 jdbc 호출 정보를 보여줌
jdbc.resultset resultsets 포함한 모든 jdbc 호출 정보를 보여줌
jdbc.resulttable sql 조회 결과를 테이블로 보여줌
jdbc.connection connection 연결과 종료를 보여줌

인터셉터

인터셉터는 어떠한 URI를 호출했을 때 해당 요청의 컨트롤러가 처리되기 전 또는 후의 작업을 하기 위해서 사용.

이러한 역할은 필터와 인터셉터로 수행할 수 있다.

스프링 MVC 요청의 라이프사이클

필터와 인터셉터의 차이점

*필터는 디스패처 서블릿 앞 단에서 동작, 인터셉터는 디스패처 서블릿에서 핸들러 컨트롤러로 가기 전에 작동

*필터는 J2EE 표준 스펙에 있는 서블릿의 기능 중 일부. 인터셉터는 스프링 프레임워크에서 제공. 따라서 인터셉터는 스프링 빈을 사용 할 수 있음

*정답은 아니나 일반적으로 문자열 인코딩 같이 웹 전반에서 사용되는 기능은 필터로 구현. 클라이언트 요청에 관련된 것은(로그인, 인증 등) 인터셉터로 처리.

구현하기

*HandlerInterceptorAdatoer 클래스를 상속받아 구현

메서드 역할
preHandle 컨트롤러 실행 전에 수행
postHandle 컨트롤러 수행 후 결과를 뷰로 보내기 전에 수행
afterCompletion 뷰의 작업까지 완료된 후 수행
인터셉터 클래스 생성
package board.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Slf4j //로그 어노테이션
public class LoggerInterceptor extends HandlerInterceptorAdapter{
    //HandlerInterceptorAdapter 상속
    
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		log.debug("======         START         ======\n");
		log.debug(" Request URI \t:  " + request.getRequestURI());
		return super.preHandle(request, response, handler);
	} // 컨트롤러 시작되면 수행
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		log.debug("======           END         ======\n");
	} // 컨트롤러 실행 이후 수행
}
인터셉터 클래스를 스프링 빈으로 등록하기
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer{
	
	@Override
	public void addInterceptors(InterceptorRegistry registry){
		registry.addInterceptor(new LoggerInterceptor());
	}
}

*인터셉터를 등록할 때에는 addInterceptor()와 excludePathPatterns() 메서드를 이용하여 요청 주소의 패턴과 제외할 요청 주소의 패턴을 지정하여 선택적으로 적용할 수 있다.

AOP

*객체지향이란 관심사가 같은 기능과 데이터를 모아서 재사용이 가능한 객체로 캡슐화 하는 것을 의미함.

*객체지향적으로 개발하다가도 여러가지 기능을 넣다보면 핵심 기능과 관계 없지만 중요한 것들이 생겨남.

이러다보니 객체의 모듈화가 어려워짐. 반복하지 않기 위해 만든 클래스도 반복적으로 사용하면서 재사용성이 떨어지고 생산성이 낮아짐.

*예를 들어 로그, 권한체크, 인증, 예외 처리 등이 핵심 기능은 아니지만 중요한 기능

*이들을 하나의 클래스로 만들어 놓아도 핵심 기능에서 매번 객체를 생성하고 메서드를 호출했어야 함.

*AOP는 이를 여러 코드에 쉽게 적용할 수 있도록 돕는 역할. 즉, 관점(관심, Aspect)이라는 개념을 통해서 해결

*해당 기능이 뭔지 몰라도 어떤 기능이든 각 시점(관점)에 부가 기능이 적용되기만 하면 됨.

*AOP는 OOP를 더욱 OOP 답게 만들어주는 역할

AOP에서 사용하는 용어

용어 의미
관점(Aspect) 공통적으로 적용될 기능을 의미. 횡단 관심사의 기능. 한 개 이상의 어드바이스와 포인트컷의 조합으로 이루어짐
어드바이스(Advice) 관점의 구현체. 조인포인트에 삽입되어 동작하는 것을 의미. 동작 시점에 따라 다섯 가지로 나눔
조인포인트(Joinpoint) 어드바이스를 적용하는 지점을 의미. 메서드 실행 단계만 가능
포인트컷(Pointcut) 어드바이스를 적용할 조인포인트를 선별하는 과정이나 기능을 정의한 모듈을 의미. 정규표현식이나 AspectJ의 문법을 이용해서 어떤 조인포인트를 사용할 것인지 결정
타깃(Target) 어드바이스를 받을 대상
위빙(Weaving) 어드바이스를 적용하는 것을 의미. 공통 코드를 원하는 대상에 삽입하는 것을 뜻함.

*이건 내가 이해한 내용인데 조인포인트는 어드바이스(AOP 메소드)를 적용할 지점이라하면 어드바이스는 조인포인트에 적용될 어드바이스(AOP 메소드)라고 생각하면 될듯.

*포인트 컷은 어드바이스(AOP 메소드)가 적용할 조인포인트를 정해주는 역할이라고 보면 될듯. 그것을 정규표현식이나 AspectJ 문법을 통해 표현하는 것이고.

AOP class 파일
package board.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class LoggerAspect {
	
	@Around("execution(* board..controller.*Controller.*(..)) or "
			+ "execution(* board..service.*Impl.*(..)) or "
			+ "execution(* board..mapper.*Mapper.*(..))")
    //해당 기능이 실행될 시점. 즉, 어드바이스를 의미
    //@Around는 아래 표에 설명
    //execution은 포인트컷 표현식으로 적용할 메서드를 명시할 때 사용
	public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
		String type = "";
		String name = joinPoint.getSignature().getDeclaringTypeName();
		if (name.indexOf("Controller") > -1) {
			type = "Controller  \t:  ";
		}
		else if (name.indexOf("Service") > -1) {
			type = "ServiceImpl  \t:  ";
		}
		else if (name.indexOf("Mapper") > -1) {
			type = "Mapper  \t\t:  ";
		}
		
		log.debug(type + name + "." + joinPoint.getSignature().getName() + "()");
		
		return joinPoint.proceed();
	}
}

AOP 주요 개념

  1. 어드바이스
종류 어노테이션 설명
Before Advice @Before 대상 메서드가 실행되기 전에 적용할 어드바이스
After returning Advice @AfterReturning 대상 메서드가 성공적으로 실행되고 결과 값을 반환 후 적용할 어드바이스
After throwing Advice @AfterThrowing 대상 메서드에서 예외 발생 시 적용할 어드바이스
After Advice @After 대상 메서드의 수행 여부 상관 없이 무조건 실행되는 어드바이스
Around Advice @Around 대상 메서드 호출 전후, 예외발생 모든 시점에서 적용할 수 있는 어드바이스
  1. 포인트 컷

    *excution : 가장 대표적이고 강력한 지시자. *은 앞 뒤로 붙어서 모두라는 뜻 ..0은 0개 이상.

exection(void select*(..))
// 리턴 타입이 void이고 메서드 이름이 select로 시작하며 파라미터가 0개 이상인 모든 메서드 호출
exection(* board.controller.*())
//board.controller 패키지 밑에 파라미터가 없는 모든 메서드가 호출 될때
exection(* board.controller.*(..))
//board.controller 패미키 밑에 파라미터가 0개 이상인 모든 메서드가 호출 될때
exection(* board..select*(*))
//board 패키지의 모든 하위 패키지에 select로 시작하고 파라미터가 한 개인 모든 메서드가 호출 될때
exection(* board..select*(*.*))
//board 패키지의 모든 하위 패키지에 select로 시작하고 파라미터가 두 개인 모든 메서드가 호출 될때
    
execution(* board..controller.*Controller.*(..))
//board 패키지의 모든 하위 패키지중 cotroller 패키지의 Controller로 이름이 끝나는 클래스의 파라미터가 0이상인 모든 메서드를 의미
*포인트컷 표현식은 and와 or을 조합해서 표현. &&,   로도 가능

*within : 특정 타입에 속하는 메서드를 포인트컷으로 설정

within(board.service.boardServiceImpl)
//board.service 패키지 밑에 있는 boardServiceImpl 클래스의 메서드가 호출 될 때
within(board.service.*ServiceImpl)
//board.service 패키지 밑에 있는 ServiceImple이름으로 끝나는 메서드가 호출 될 때

*bean : bean 이름의 패턴으로 포인트컷 설정

bean(boardServiceImple)
bean(*ServiceImple)

트랜잭션

*데이터의 일관성, 정합성을 유지를 위해 사용

*XML, 어노테이션, AOP 방식으로 처리

트랜잭션이란 데이터베이스의 상태를 변화시킬때 더 이상 분리할 수 없는 작업의 단위를 의미

ACID 설명
원자성(Atomicitiy) 트랜잭션은 하나 이상의 관련된 동작을 하나의 작업 단위로 처리. 하나라도 실패한다면 이전 처리한 것도 모두 처음 상태로 돌아감
일관성(Consistency) 성공적으로 처리되면 모든 데이터는 일관성을 유지해야함.
고립성(Isolation) 독립적으로 처리되며 외부에서 간섭이 없어야함. 서로 다른 트랜잭션이 같은 데이터를 접근할 경우 동시 접근 제어 해야 함.
지속성(Durability) 성공적으로 처리되면 모든 데이터는 지속적으로 유지해야함.

어노테이션과 AOP 이용한 장단점

  어노테이션 AOP
장점 *설정없이 쉽게 사용 *공통으로 적용이므로 누락의 가능성이 없다.
  *원하는 곳에만 사용. 성능의 영향 최소화 *외부 라이브러리에도 적용 가능
단점 *누락되거나 여러 메서드에 걸쳐 사용할 경우 적용이 안될 수도 있다. *성능에 영향이 간다.
  *외부라이브러리에 적용 불가능 *원하는 시점에만 할 수가 없어 원하는 시점까지는 데이터에 적용되어야 하나 모두 롤백될 수 있음

*두 방법을 모두 적용은 불가능. 알아서 판단하여 잘 사용하여야 함.

예외처리

  1. try ~catch 처리
  2. 컨트롤단에서 @ExceptionHandler를 이용
  3. @ControllerAdvice를 이용한 전역 예외처리

*2번의 경우 각 컨트롤 별로 동일한 예외처리를 추가하는 경우가 많아 코드 중복이 발생

*3번으로 진행

*XML 기반에서는 @ControllerAdvice를 설정하기 위해서는 스프링 프레임워크의 root application context와 servlet context에 대한 지식이 필요했다. 또한 XML 설정도 필요. 그렇지만 스프링부트에서는 단순 어노테이션으로 끝.

인코딩

*스프링부트 2.1.X버전에는 이미 필터가 적용되어 있음. 아닌 경우만 따로 설정하면 됨.