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 주요 개념
- 어드바이스
종류 | 어노테이션 | 설명 |
---|---|---|
Before Advice | @Before | 대상 메서드가 실행되기 전에 적용할 어드바이스 |
After returning Advice | @AfterReturning | 대상 메서드가 성공적으로 실행되고 결과 값을 반환 후 적용할 어드바이스 |
After throwing Advice | @AfterThrowing | 대상 메서드에서 예외 발생 시 적용할 어드바이스 |
After Advice | @After | 대상 메서드의 수행 여부 상관 없이 무조건 실행되는 어드바이스 |
Around Advice | @Around | 대상 메서드 호출 전후, 예외발생 모든 시점에서 적용할 수 있는 어드바이스 |
-
포인트 컷
*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 | |
---|---|---|
장점 | *설정없이 쉽게 사용 | *공통으로 적용이므로 누락의 가능성이 없다. |
*원하는 곳에만 사용. 성능의 영향 최소화 | *외부 라이브러리에도 적용 가능 | |
단점 | *누락되거나 여러 메서드에 걸쳐 사용할 경우 적용이 안될 수도 있다. | *성능에 영향이 간다. |
*외부라이브러리에 적용 불가능 | *원하는 시점에만 할 수가 없어 원하는 시점까지는 데이터에 적용되어야 하나 모두 롤백될 수 있음 |
*두 방법을 모두 적용은 불가능. 알아서 판단하여 잘 사용하여야 함.
예외처리
- try ~catch 처리
- 컨트롤단에서 @ExceptionHandler를 이용
- @ControllerAdvice를 이용한 전역 예외처리
*2번의 경우 각 컨트롤 별로 동일한 예외처리를 추가하는 경우가 많아 코드 중복이 발생
*3번으로 진행
*XML 기반에서는 @ControllerAdvice를 설정하기 위해서는 스프링 프레임워크의 root application context와 servlet context에 대한 지식이 필요했다. 또한 XML 설정도 필요. 그렇지만 스프링부트에서는 단순 어노테이션으로 끝.
인코딩
*스프링부트 2.1.X버전에는 이미 필터가 적용되어 있음. 아닌 경우만 따로 설정하면 됨.