Search

Spring (패스트캠퍼스)

@ExceptionHandler(Exception.class) ← 처리할 예외 적기
()안의 예외가 발생했을 때 어노테이션이 달린 메서드가 호출된다.
예외가 여러개일땐 {예외1, 예외2} 이런식으로 배열로 처리한다
@ControllerAdvice(”패키지”) ← 패키지 안 적으면 모든 패키지에 적용
모든 컨트롤러에 대한 ‘클래스'
전역 예외 처리 메서드들을 정의해놓는다 (예외처리가 중복일 경우 컨트롤러 내 메서드가 우선)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
응답의 상태 코드를 변경할 때 사용한다.
web.xml에서 errorpage 지정
상태 코드별로 매핑
jsp 페이지 맨 위 선언문에 isErrorPage=”true” 추가하면 Exception 사용가능
상태 코드가 강제로 500으로 고정됨
SimpleMappingExceptionResolver
예외 종류별로 매핑
servlet.context.xml에 등록
DispatcherServlet
서블릿들의 입력부분을 공통적으로 처리해주는 서블릿 (전처리)
중간에 나눠놓은건 느슨한 결합을 위함 → 변경에 유리하게 하려고 → 관심사의 분리
viewresolver
registerForm이라는 view 이름을 주면 prefix, suffix 접두사 접미사를 붙여서 실제 뷰 이름을 반환해준다 (/WEB-INF/views/registerForm.jsp)
Servlet-Context.xml의 InternalResourceViewResolver도 같은 역할을 한다
WebDataBinder
예외 발생하지 않고 컨트롤러에게 보낸다. (컨트롤러가 처리하게끔함)
@InitBinder 어노테이션이 붙은 메서드는 컨트롤러 내에서만 적용되는 타입변환 메서드다
기본적으로 CustomDateEditor 클래스를 제공해준다
ex) binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
@DateTimeFormat(pattern=”yyyy-MM-dd”) 어노테이션은 Date 타입 변수 하나에만 붙일 수 있다
PropertyEditor는 양방향 타입변환으로 특정 타입이나 이름의 필드에 적용 가능하다
ex) binder.registerCustomEditor(String[].class, “hobby”, new StringArrayPropertyEditor(”#”));
디폴트 PropertyEditor는 스프링이 기본적으로 제공한다
커스텀 PropertyEditor는 사용자가 직접 구현하고 PropertyEditorSupport를 상속하면 편리하다
모든 컨트롤러 내에서 변환하려면 WebBindingInitializer를 구현 후 등록한다
특정 컨트롤러 내에서 변환하려면 컨트롤러에 @InitBinder가 붙은 메서드를 작성한다
Converter는 단방향 타입 변환으로 PropertyEditor의 단점을 개선했다 (stateful → stateless)
ConversionService : 타입 변환 서비스를 제공하며 여러 컨버터를 등록 가능하다
모든 컨트롤러 내에서 변환하려면 ConfigurableWebBindingInitializer를 설정해야 사용한다
특정 컨트롤러 내에서 변환하려면 컨트롤러에 @InitBinder가 붙은 메서드를 작성한다
Formatter는 양방향 타입 변환으로 바인딩할 필드에 적용한다 (@NumberFormat, @DateTimeFormat)
Validator
객체를 검증하기 위한 인터페이스
@Valid를 사용하려면 Maven Repo에서 API를 받아서 pom.xml에 추가한 후 사용해야함
조건문을 통해 필드에 대한 에러를 적을 땐 rejecValue를 사용한다
수동 검증 : Validator 객체를 생성해서 직접 검증
자동 검증 : @InitBinder 안에 setValidator 해준 뒤 검증할 필드 앞에 @Valid만 붙여주면 된다
글로벌 Validator : servlet-context.xml에 빈 등록하고 @Valid 사용
사용 예시 : Validator 인터페이스를 구현하고 supports, validate 추상 메서드를 구현한다
——————————————————————————————————————————————————
Context의 getBean을 통해 빈을 호출할 때 객체의 scope에 대한 설정에 따른다
scope=”prototype” : 빈을 호출할 때마다 새로운 객체 생성
scope=”singleton” : 빈을 호출할 때마다 기존 객체 호출
분리!!!
관심사의 분리
변하는 것과 변하지 않는 것 분리
중복 코드 제거
Bean이란?
JavaBeans : 재사용 가능한 컴포넌트, 상태(iv), 게터&세터, 기본생성자
Servlet&JSP Bean : MVC의 Model, EL, scope에 사용하고 JSP Container가 관리
EJB : 복잡한 규칙을 갖고 EJB Container가 관리
Spring Bean : POJO 객체로 단순하고 독립적이고 Spring Container가 관리
Bean의 초기화
<property>
setter가 있어야 쓸 수 있지만 setter를 호출하지 않아도 된다는 장점
setter
<constructor-arg>
생성자를 이용한다
<list>, <set>, <map>
Spring Container란?
Bean 저장소로 저장, 관리(생성, 소멸, 연결)를 담당
BeanFactory : Bean의 생성, 연결 등의 기본 기능을 정의한 인터페이스
ApplicationContext : BeanFactory를 확장해 여러 기능을 추가 정의한 인터페이스
AC의 종류 중 XML(<bean>)과 Java Config(@Bean) 중 자바 코드 선호
ApplicationContext란?
처음 RootAC를 생성한 후 DispatcherServlet을 등록할 때 또 만들어진 AC들은 부모-자식 관계를 가진다
RootAC에는 주로 공통된 non-Web의 Bean을 넣고 자식AC는 개별적으로 Bean을 넣어두고 찾는다
ApplicationContext의 관계도
ApplicationContext : Application 전체에서 접근할 수 있는 저장소 영역
attributes : AC가 갖는 Map 저장소
RootAC(XmlWebApplicationContext) : attributes가 맨 처음 만든 AC
children : 각 서블릿들에 대한 정보(이름, 경로)를 갖는 저장소
DispatcherServlet : children에 의해 생성된 모든 요청의 전처리를 맡는 서블릿
ServletWebAC : DS의 contextClass 변수에 생성된 AC로 자신의 parent 변수에 RootAC를 갖는다
ApplicationContext의 실제 구조
제일 위에 있는 servletContext는 진짜 ApplicationContext + Cache 인 대문 역할
하위 ApplicationContext는 attributes + 서블릿들을 모은 context + 조상인 Service 등을 감싼 역할
그 하위에 context가 진짜 context이고 children + DS 등을 갖고 있다
ApplicationContext의 주요 메서드들
제어의 역전 IoC (Inversion of Control)
제어의 흐름을 전통적인 방식과 다르게 뒤바꾸는 것
기존 : 호출 → 실행 + 처리 → 결과값 반환
IoC : 호출 + 제공 → 실행 + 처리
의존성 주입 DI : IoC에서 제공의 역할로 사용할 객체를 외부에서 주입 해주는 것
수동 주입 : new SuperEngine()과 같이 직접 지정해주는 것
자동 주입 : @Autowired 와 같이 자동으로 빈을 찾아서 연결하는 것
@Autowired ← byType
인스턴스 변수, setter, 참조형 매개변수를 가진 생성자, 메서드에 적용 할 수 있다
어노테이션을 붙이면 빈으로 등록된 객체 중 맞는걸 자동으로 연결해준다
객체의 Type으로 먼저 검색하고 여러개면 똑같은 이름으로 검색한다
@Qualifier()는 같은 이름을 못 찾았을 때 대안을 정의해준다
하나하나 붙이는 것보다 생성자에 매개변수로 다 선언한 후 한 번 붙이는걸 선호한다
생성자가 하나일 땐 생략 가능, 여러 개면 꼭 붙여준다
@Value()는 변수에 붙으며 해당 변수에 값을 지정해준다
@Resource ← byName
Spring Container에서 이름으로 빈을 검색해서 참조 변수에 자동 주입하고 일치하는 이름이 없으면 예외
Autowired랑 같은 기능을 하며 객체의 이름(key)을 통해 찾는다.
@Resource(name=””) 에서 name을 생략하면 객체의 첫글자를 소문자로 바꾼 이름을 찾는다
@Component
<context:component-scan base-package=””/> 에서 지정해준 패키지의 서브 패키지까지 검색
@Component 어노테이션이 붙은 클래스를 다 찾아서 빈으로 등록해준다
빈으로 등록하는걸 제외하고 싶다면 <context:exclude-filter type=”” expression=””/> 사용
google의 guava library를 통해 사용할 수 있다
객체를 등록하기 위해 클래스 앞에 붙이는 어노테이션
1.
ClassLoader, ClassPath를 통해 패키지 내의 모든 클래스를 읽어서 Set에 저장
2.
for문을 통해 @Component가 붙은 클래스를 찾는다
3.
null이 아니라면 객체를 생성해 클래스이름을 key로 Map에 저장한다
@Value와 @PropertySource
변수에 값을 대입하는 것 말고도 SystemProperties나 SystsemEnvironment를 불러와서 대입할 수 있다
또한 .properties 파일에서 =를 구분자로 값을 찾아와 대입할 수 있다 (PropertySource 사용해야함)
스프링 어노테이션과 표준 어노테이션
DB를 이용한 TDD
JUnit 테스트에서 test 클래스의 DataSource 인스턴스 변수는 클래스 내에서는 인스턴스 메서드와 공유되지만 @Test가 붙은 테스트 메서드는 개별 객체로 실행되기 때문에 test 클래스의 DataSource 인스턴스 변수를 공유받지 못한다

DAO(Data Access Object)

데이터에 접근하기 위한 객체
DataBase에 저장된 데이터에 대한 CRUD를 수행
DB 테이블당 하나의 DAO를 작성한다
Controller에 의해서 사용되며 DB의 데이터를 사용하기 위함

계층(layer)의 분리

DAO가 없다면 로그인과 회원가입 Controller에서처럼 selectUser와 같은 중복이 발생한다
그렇기 때문에 데이터에 접근하는 계층인 영속계층(Persistence Layer, Data Access Layer)과
데이터를 보여주는 계층인 Presentation Layer를 분리한다
이걸 통해 관심사의 분리와 중복코드 제거를 할 수 있다
DB툴 변경에 유리하다 (MySQL과 Oracle 등)
추가로 Presentation Layer에서 하나 더 나눠서 Business Layer로 나눈다

Transaction

더 이상 나눌 수 없는 작업의 단위 (insert, update, select 등등)
계좌이체의 경우는 출금과 입금이 하나의 tx(transaction)으로 묶여야함
묶은 경우 둘 다 성공하든가 하나만 실패해도 취소 (all or nothing)
TDD 코드를 짜기 위해선 connection을 받아온 후 setAutoCommit(false)로 해줘야한다
또한 try-catch를 이용해 예외발생 시 rollback을 사용할 수 있다

Transaction의 속성 (ACID)

원자성(Atomicity) : 나눌 수 없는 하나의 작업으로 다뤄져야 한다
일관성(Consistency) : Tx 수행 전과 후가 일관된 상태를 유지해야 한다
고립성(Isolation) : 각 Tx는 독립적으로 수행되어야 한다
영속성(Durability) : 성공한 Tx의 결과는 유지되어야 한다

Commit 과 Rollback

커밋 : 작업 내용을 DB에 영구적으로 저장
롤백 : 최근 변경사항을 취소 (마지막 커밋으로 복귀)
자동 커밋 : 명령 실행 후 자동으로 커밋이 수행 (rollback 불가)
수동 커밋 : 명령 실행 후 명시적으로 commit 또는 rollback을 입력 (SET autocommit = 0;)

Isolation level (1 → 4)

1.
READ UNCOMMITED (Dirty READ) : 커밋되지 않은 데이터로도 읽기 가능
2.
READ COMMITED (Phantom READ) : 커밋된 데이터만 읽기 가능
3.
REPEATABLE READ : Tx이 시작된 이후 변경은 무시됨 (Default)
4.
SERIALIZABLE : 한 번에 하나의 Tx만 독립적으로 수행

AOP (Aspect Oriented Programming)

관점지향 프로그래밍으로 횡단 관심사 또는 Cross-cutting concerns이라고도 한다
부가적인 코드(advice)를 동적으로 추가해주는 기술이다
메서드의 ‘시작’ 또는 ‘끝’에 자동으로 코드를 추가한다
관련 용어
target : advice가 추가될 객체로 핵심 기능을 하는 객체
advice : target에 동적으로 추가될 부가적인 코드
join point : advice가 추가될 대상(메서드)
pointcut : join point들을 정의한 패턴 execution(.com.fastcampus..*(..))
proxy : target에 advice가 동적으로 추가되어 생성된 객체
weaving : target에 advice를 추가해서 proxy를 생성하는 동작
Advice의 종류 (설정은 XML이나 어노테이션으로 가능)
try-catch 사용 안할 시
around advice (@Around) : 메서드의 시작과 끝 부분에 추가되는 부가 기능 (before+after)
before advice (@Before) : 메서드의 시작 부분에 추가되는 부가 기능
after advice (@After) : 메서드의 끝 부분에 추가되는 부가 기능
try-catch 사용 시
after returning (@AfterReturning) : 예외가 발생하지 않았을 때 실행되는 부가 기능 (try 블럭)
after throwing (@AfterThrowing) : 예외가 발생했을 때 실행되는 부가 기능 (catch 블럭)
advice가 추가될 메서드를 지정하기 위한 패턴
execution(반환타입 패키지명.클래스명.메서드명(매개변수 목록))
@Around(”execution(* com.fastcampus.ch3.aop.*.*(..))”)
advice의 반환타입은 Object나 void를 사용하여야 한다.
Object를 사용할 때는 return result;로 결과를 반환해야한다
advice 클래스에 @Order(1) 과 같이 advice가 실행될 순서를 지정해줄 수 있다.
advice 클래스의 매개변수로 ProceedingJoinPoint를 사용하면 메서드의 정보를 이용할 수 있다
pjp.getSignature() 뒤에 getName, getArgs 등과 result

서비스 계층의 분리

Presentation Layer에서 비즈니스 로직을 분리해내서 Business Layer를 만든다
Biz Layer는 DAO들을 DI 받고 로직을 처리하는 메서드를 가지며 Controller는 호출하기만 하면 된다
registerUser() 에는 UserDao의 insertUser() + UserHistoryDao의 insertUserHistory()의 Tx가 있다
이 Tx는 하나라도 실패하면 안되고 RegisterController에서 처리하기엔 너무 복잡하므로
UserService로 비즈니스 로직을 따로 분리해 처리하는게 적합하다
각 계층에 붙은 레이어 @Controller, @Service, @Repository는 @Component를 포함한다
그렇기에 <component-scan>으로 스캔이 가능하다.

TransactionManager란

DAO의 각 메서드는 개별 Connection을 사용한다 (각 Transaction은 1개의 Connection을 사용한다)
→ 커밋과 롤백에 대한 문제를 해결하기 위해 TxManager를 사용해 Connection을 묶어줘야 한다
사용하기 위해선 DAO에서 Connection을 얻거나 반환할 때 DataSourceUtils를 사용해야 한다
TxManager를 사용해 DAO의 insert 메서드에 transaction을 적용한 예시이다
PlatformTransactionManager와 TransactionStatus를 자동으로 생성하기 위한 코드
TxManager를 빈으로 등록한 후 annotation-driven을 통해 @Transactional을 사용할 수 있게 해준다

@Transactional

AOP를 이용한 핵심 기능과 부가 기능의 분리
insert()라는 핵심 기능 위 아래로 붙은 부가 기능을 AOP를 통해 떼어낸 후 @Transactional을 사용
메서드뿐만 아니라 클래스나 인터페이스에도 붙을 수 있으며 붙은 곳의 모든 메서드에 적용된다
@Transaction 은 RuntimeException, Error만 롤백
@Transaction(rollbackFor = Exception.class) 은 Exception을 롤백

@Transactional의 속성

propagation : Tx의 경계를 설정하는 방법을 지정
같은 클래스의 메서드를 호출하는 경우 Tx를 나눌 수 없으므로 클래스를 분리한 후 경계를 지정해야함
REQUIRED : Tx이 진행 중이면 참여하고 없으면 새로운 Tx 시작 (Default)
REQUIRES_NEW : Tx이 진행중이건 아니건 새로운 Tx 시작
NESTED : Tx이 진행 중이면 Tx의 내부 Tx로 실행 (서브 Tx) ← git의 브랜치 느낌 savepoint
MANDATORY : 반드시 진행 중인 Tx 내에서만 실행 가능 벗어나면 예외 발생
SUPPORTS : Tx이 진행 중이건 아니건 상관없이 실행
NOT_SUPPORTED : Tx 없이 처리 (Tx이 진행 중이면 잠시 중단)
NEVER : Tx 없이 처리 (Tx이 진행 중이면 예외 발생)
isolation : Tx의 isolation level을 지정
readOnly : Tx이 데이터를 읽기만 하는 경우 true로 지정하면 성능이 향상
rollbackFor : 지정된 예외가 발생하면 Tx을 rollback한다 (RuntimeException, Error는 자동 롤백)
noRollbackFor : 지정된 예외가 발생해도 Tx을 롤백하지 않는다
timeout : 지정된 시간(초) 내에 Tx이 종료되지 않으면 강제 종료한다