Spring

Spring 07 - AOP, Transaction

나주나주 2024. 3. 7. 10:13

AOP

: Aspect Oriented Programming이 추구하는 것은 관심사의 분리seperate concerns 로, 주변 로직은 관심사로 분리한다.

관심사와 비즈니스 로직을 분리하여 별도의 코드로 작성하고, 실행할 때 결합

Tip) AOP 기능은 일반적인 API를 이용하는 클래스(POJO)에 적용, Controller에 적용이 불가능한 것은 아니지만, Controller의 경우 인터셉터나 필터 등을 이용

 

주변 로직의 예

  • 파라미터가 올바르게 들어왔는가
  • 적절한 권한을 가진 사용자가 작업하는가
  • 발생할 수 있는 예외 처리

AOP 적용 시 기존 코드를 수정하지 않고 관심사들을 엮을 수 있다(cross-concern)

 

  • Target: 핵심 비즈니스 로직, 관심사와 관계를 맺지 않는다, 순수한 core
  • Proxy: 관심사들을 거쳐 Target을 호출, 대부분 스프링 AOP 기능을 이용한 자동 생성 (auto-proxy) 방식 사용
  • JoinPoint: Target객체가 가진 여러 메서드, 외부 호출의 경우: Proxy객체를 통해 JoinPoint() 호출

 

  • Pointcut: Advice - JoinPoint 결합 설정
  • Aspect: 관심사 자체를 의미하는 추상명사
  • Advice: 코드 Aspect를 구현한

 

Advice의 구분 설명
@Before 핵심 로직 실행 전 관심사 처리, 실행 제어 X
After Returning Advice 모든 실행이 정상적으로 이루어진 후에 동작
After Throwing Advice 예외 발생 후 동작
@After  
@Around 실행중(실행 전후) 관심사 처리, 리턴 타입이 있어야, 메서드 실행 제어 O
ProceedingJoinPoin* 파라미터나 예외 처리, @Around 필수!

*: 진행중인 JoinPoint

 

Tip) Target은 Pointcut에 의해 자신에게 없는 기능을 가지게 된다

Pointcut의 구분 설명
@execution 메서드를 기준으로 Pointcut 설정
@within 특정한 타입(클래스) 기준으로 Pointcut 설정
this 주어진 인터페이스를 Pointcut 설정
@args 특정한 파라미터를 가지는 대상들만  Pointcut 설정
@annotation 특정한 어노테이션이 적용된 대상들만 Pointcut 설정

 

Tip) 서비스 계층에 AOP 적용

 

AOP 초기 설정

 

SampleService.java (i)

package org.zerock.service;

public interface SampleService {
	//문자열을 parseInt로 변환하여 더한다
	public Integer doAdd(String str1, String2) throws Exception;
}

 

SampleServiceImpl.java

package org.zerock.service;

import org.springframework.stereotype.Service;

@Service	//스프링에서 빈으로 사용!
public class SampleServiceImpl implements SampleService {

	@Override	//추상 메서드 구현
    public Integer doAdd(String str1, String str2) throws Exception {
		return Integer.parseInt(str1) + Integer.parseInt(str2);    
	}
}

Tip) 기존에 해왔던 수많은 로그를 기록하는 일 log.info() 은 반복적이며, 핵심 로직은 아니지만 필요하기 때문에,  관심사로 보고 Advice를 설계하자

 

root-context.xml

Proxy 객체를 만들어주는 설정

<context:annotation-config/>

//service, aop 패키지 스캔: SampleServiceImpl과 LogAdvice는 스프링의 빈(객체)으로 등록
<context:component-scan base-package="org.zerock.service"/> 
<context:component-scan base-package="org.zerock.aop"/> 

//프록시를 이용해 LogAdvice에 설정한 @Before 동작
<aop:aspectj-autoproxy/>

 

Transaction

: 한 번에 이루어지는 작업

 

ACID 원칙

Atomicity 원자성 A와 B의 처리 결과는 동일해야 한다
작업이 잘못될 경우 모두 원점으로 되돌아가야 한다
Consistency 일관성 트랜잭션으로 처리된 데이터와 일반 데이터 사이에 차이가 없어야 한다
Isolation 격리 처리중 외부에서의 간섭은 없어야 한다
Durability 영속성 결과가 영속적으로 보관되야 한다

 

트랜잭션으로 관리한다 ≒ AND 연산

동시에 && 조건으로 T, T 나와야

 

정규화

:중복 값을 제거하여 데이터 저장의 효율을 올린다

 

정규화를 진행할수록   1) 테이블의 수는 늘어나고

                                  2) 각 테이블의 데이터 양은 줄어들면서

                                  3) 트랜잭션 처리할 일이 적어진다.                                         반면에,

  쿼리 등을 이용해서   4) 데이터를 가져오는 입장에서는 불편해 진다 (join, subquery 이용해서 처리, 매번 계산이 발생하     는 쿼리는 성능이 저하될 수 있다)

 

반정규화

: 중복 값을 DB에 보관하고, join이나 subquery의 사용을 줄인다

 

정규화 진행 시 처리되지 않는 데이터

  • 시간에 따라 변경되는 데이터 (ex) 나이
  • 계산이 가능한 데이터
  • 통용되는 데이터 (ex) 월요일

 

 

Tip) 트랜잭션은 비즈니스 계층에서 이루어지므로 org.zerock.service 계층에서 Sample1Mapper, Sample2Mapper를 사용하는 SampleTxService 인터페이스, SampleTxServiceImpl 클래스 설계

 

스프링은 간단한 트랜잭션 매니저의 설정과 @Transactional 을 이용하여 트랜잭션을 처리할 수 있습니다.

 

@Transactional 의 경우 메서드뿐만 아니라 클래스, 인터페이스에도 선언할 수 있습니다.

@Transactional 의 우선순위
메서드에서 설정 시 가장 우선
클래스에서 설정 시 메서드 다음
인터페이스에서 설정 시 가장 낮은 우선순위

 

댓글과 댓글 수에 대한 처리

 

board.sql

alter table tbl_board add (replycnt number default 0); --댓글 수 칼럼 추가

--댓글 수 수정 = rno의 개수
update tbl_board set replycnt =
(select count(rno) from tbl_reply where tbl_reply.bno = tbl_board.bno);

 

BoardVO.java

@Data
public class BoardVO {
  private Long bno;
  private String title;
  private String content;
  private String writer;
  private Date regdate;
  private Date updateDate;
  private int replyCnt; //추가!
}

 

BoardVO.java

public interface BoardMapper {
	public List<BoardVO> getList();
	public List<BoardVO> getListWithPaging(Criteria cri);
	public void insert(BoardVO board);
	public Integer insertSelectKey(BoardVO board);
	public BoardVO read(Long bno);
	public int delete(Long bno);
	public int update(BoardVO board);
	public int getTotalCount(Criteria cri);

	//bno의 증가(댓글 작성) 및 감소를 의미하는 amount변수에 파라미터를 받음
	//MyBatis의 SQL로 2개 이상의 데이터를 전달하려면 @Param 이용
	public void updateReplyCnt(@Param("bno") Long bno, @Param("amount") int amount);
}

 

BoardMapper.xml

<update id="updateReplyCnt">
	update tbl_board set replycnt = replycnt + #{amount} where bno = #{bno}
	</update> <!-- 댓글 증감값을 더해 replycnt값 수정 -->
    
    <select id="getListWithPaging"
		resultType="org.zerock.domain.BoardVO"> 
		<![CDATA[
		select
			bno, title, content, writer, regdate, updatedate, replycnt
		from ( 
			select /*+INDEX_DESC(tbl_board pk_board) */
				rownum rn, bno, title, content, writer, regdate, updatedate, replycnt
		from
			tbl_board
		where
		]]>
		
	<include refid="criteria"></include>	
		
	<![CDATA[
		rownum <= #{pageNum} * #{amount}
		)
	where rn > (#{pageNum} -1) * #{amount}
	]]>	
	</select>

 

ReplyServiceImpl.java

Tip) 기존에는 ReplyMapper만을 이용했지만 반정규화 처리 하면서 BoardMapper를 이용해야 한다. 댓글이 증감되는 상황이 오면 BoardMapper와 ReplyMapper를 같이 이용해서 트랜잭션으로 처리한다.

 

 

'Spring' 카테고리의 다른 글

Spring - 초기 설정 ver.2  (0) 2024.03.07
Spring 06 - REST방식, 댓글  (0) 2024.03.06
Spring 05 - Paging  (0) 2024.03.04
Spring 04  (0) 2024.02.29
기본적인 웹 게시물 관리  (0) 2024.02.28