1. AOP (Aspect Oriented Programming)
- AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다. 따라서 AOP를 통해 부가 기능을 모듈화할 수 있다. AOP를 사용하면 핵심 기능과 분리하여 부가 기능 중심으로 설계, 구현이 가능해진다.
- 스프링에서 부가기능은 "어드바이스", 부가기능을 적용할 위치를 "포인트컷" 이라고 한다.
다음은 AOP를 통해 부가기능을 모듈화한 코드이다. 여기서 부가 기능은 사용자의 API 수행 시간을 측정하는 것이다. 부가 기능은 excute 메서드에 구현되어있다. @Around 로 포인트컷을 지정하면 된다.
@Component // 스프링 IoC 에 빈으로 등록
@Aspect
public class UserTimeAop {
private final UserTimeRepository userTimeRepository;
public UserTimeAop(UserTimeRepository userTimeRepository) {
this.userTimeRepository = userTimeRepository;
}
@Around("execution(public * com.sparta.springcore.controller..*(..))") // 포인트컷(부가기능이 수행될 위치)
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
// 측정 시작 시간
long startTime = System.currentTimeMillis();
try {
// 핵심기능 수행
Object output = joinPoint.proceed();
return output;
} finally {
// 측정 종료 시간
long endTime = System.currentTimeMillis();
// 수행시간 = 종료 시간 - 시작 시간
long runTime = endTime - startTime;
// 로그인 회원이 없는 경우, 수행시간 기록하지 않음
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal().getClass() == UserDetailsImpl.class) {
// 로그인 회원 -> loginUser 변수
UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
User loginUser = userDetails.getUser();
// 수행시간 및 DB 에 기록
UserTime userTime = userTimeRepository.findByUser(loginUser);
if (userTime != null) {
// 로그인 회원의 기록이 있으면
long totalTime = userTime.getTotalTime();
totalTime = totalTime + runTime;
userTime.updateTotalTime(totalTime);
} else {
// 로그인 회원의 기록이 없으면
userTime = new UserTime(loginUser, runTime);
}
System.out.println("[User Time] User: " + userTime.getUser().getUsername() + ", Total Time: " + userTime.getTotalTime() + " ms");
userTimeRepository.save(userTime);
}
}
}
}
2. 트랜잭션 (Transaction)
- 트랜잭션은 데이터베이스에서 데이터에 대한 하나의 논리적 실행단계이다. ACID(원자성, 일관성, 고립성, 지속성)는 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다.
- 트랜잭션은 더 이상 쪼갤 수 없는 최소 단위의 작업이다. 따라서 작업이 모두 저장되거나 아무것도 저장되지 않는 것을 보장한다. 모든 작업이 성공적으로 완료되면 모든 작업이 저장되어 트랜잭션 Commit 이 일어난다. 작업 도중 에러가 발생하거나 중간에 작업에 실패할 경우 모든 작업이 저장되지 않아 트랜잭션 Rollback 이 일어난다.
다음은 트랜잭션을 적용한 코드이다. 스프링에서 @Transactional 어노테이션을 붙이면 간단하게 트랜잭션을 적용할 수 있다. 여기에서는 폴더 생성시에 트랜잭션을 적용하도록 되어있다. 폴더 생성시에 중복 폴더가 없고 모든 폴더가 성공적으로 생성된다면 트랜잭션 Commit이 일어난다. 폴더 생성시에 중복 폴더가 있다면 Exception이 발생되고 트랜잭션 Rollback이 일어난다.
@Transactional
public List<Folder> createFolders(List<String> folderNameList, User user) {
List<Folder> folderList = new ArrayList<>();
for (String folderName : folderNameList) {
// 1) DB 에 폴더명이 folderName 인 폴더가 존재하는지?
Folder folderInDB = folderRepository.findByName(folderName);
if (folderInDB != null) {
// DB 에 중복 폴더명 존재한다면 Exception 발생시킴
throw new IllegalArgumentException("중복된 폴더명 (" + folderName +") 을 삭제하고 재시도해 주세요!");
}
// 2) 폴더를 DB 에 저장
Folder folder = new Folder(folderName, user);
folder = folderRepository.save(folder);
// 3) folderList 에 folder Entity 객체를 추가
folderList.add(folder);
}
return folderList;
}
'Back-end > Spring' 카테고리의 다른 글
SOLID 적용하여 리팩토링하기 (0) | 2022.01.10 |
---|---|
객체 지향 프로그래밍과 SOLID (0) | 2022.01.08 |
[SpringBoot] 9주차 스터디 (페이징, JPA 연관관계) (0) | 2021.10.17 |
[SpringBoot] 8주차 스터디 (단위테스트, 통합테스트) (0) | 2021.10.07 |
[SpringBoot] 7주차 스터디 ② (OAuth, 카카오 로그인) (0) | 2021.10.02 |