AOP
: Aspect Oriented Programming이 추구하는 것은 관심사의 분리seperate concerns 로, 주변 로직은 관심사로 분리한다.
관심사와 비즈니스 로직을 분리하여 별도의 코드로 작성하고, 실행할 때 결합
Tip) AOP 기능은 일반적인 API를 이용하는 클래스(POJO)에 적용, Controller에 적용이 불가능한 것은 아니지만, Controller의 경우 인터셉터나 필터 등을 이용
주변 로직의 예
- 파라미터가 올바르게 들어왔는가
- 적절한 권한을 가진 사용자가 작업하는가
- 발생할 수 있는 예외 처리
- 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
<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 | 영속성 | 결과가 영속적으로 보관되야 한다 |
동시에 && 조건으로 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 |