일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- list remove
- github ec2 deploy
- stream groupingby
- java stream api
- equals
- AWS Codedeploy
- Java Wrapper Class
- Github action deploy
- AliasFor
- hashcode override
- image slider
- github deploy
- kotest
- 코드포스
- Java lombok
- c++ 빌드
- github CI/CD
- javascript image slider
- github action codedeploy
- java CompletableFuture
- java 비동기처리
- java hashCode
- SpringBootApplication
- equals override
- lombok Builder
- github CI
- Spring Aspect
- github action
- vanilla js image slider
- Java
- Today
- Total
기록창고
중복된 로직이 있다면 어떻게해야될까? AOP 본문
개인프로젝트 중 전적을 조회할때, 중복된 로직이 존재했다.
게임사의 API를 통하여 데이터를 가져오기때문에 데이터를 가져오기전, 해당 ID 가 존재하는지 부터 확인을 해야한다.
전적 관련 API가 여러가지 존재하는데 (캐릭터, 랭크, 일반 등) 이 모든 API 호출함수에는 아이디를 체크하는 로직이 들어간다.....
public List<OperatorDto> getOperatorList(String id) {
Profile profile = getProfile(id); // 아이디가 있는지 확인하는 부분 및 고유 ID 로 변환
/* 데이터 가져오고 파싱하는 부분 */
}
public RankStatDto getRankStat(String id, ~) {
Profile profile = getProfile(id); // 아이디가 있는지 확인하는 부분 및 고유 ID 로 변환
/* 데이터 가져오고 파싱하는 부분 */
}
public GeneralPvpDto getGeneralPvp(String id, ~) {
Profile profile = getProfile(id); // 아이디가 있는지 확인하는 부분 및 고유 ID 로 변환
/* 데이터 가져오고 파싱하는 부분 */
}
5개 정도 API 가 있는데, 중복된 코드가 5개가 존재한다! ... 그렇다고 뺄수도없다..
알고 있는 선에서 생각해본건 Template Pattern 이다.
public abstract class R6API {
public List<Operator> getOperatorList(id, ~) {
Profile profile = getProfile(id);
return getOperator(profile.~, ~);
}
protected abstract List<Opertator> getOperatorListUsingApi(String id, ~)
}
// R6APIConcrete
public class R6APIConcrete extends R6API {
@Override
protected List<Operator> getOperatorListUsingApi(String id, ~ ) {
// 데이터 가져오고 파싱하는 부분
}
}
이런식으로 Profile 을 가져오는 부분을 고정해놓고 실질적으로는 아래 Concrete 가 구현 후 호출
근데 이 방식 또한 위에 Concrete 만 깔끔해 진것이지 실제 Template 은 5번이나 반복하긴한다..
그래서 질문 후 알아낸것이 AOP. 관점지향프로그래밍이다.
음.. 횡단관심사..?!
아래 그림을 보면 파란색하나가 어떤 로직이다.
모든 로직에 Logging과 Security 가 존재한다. 이런걸 횡단 관심사라고 하나보다..
내 경우는 아이디를 확인하는 작업이 횡단 관심사일것이다.
그래서 Spring 에서는 Aspect 라는 것을 통해 할 수 있다고 한다.. 👍👍
https://docs.spring.io/spring/docs/2.5.x/reference/aop.html
여기보면 다양한 방식으로 언제 Aspect 를 실행할 것인가 에 대한 다양한 방식이 존재한다..
나는 그 중에서 Annotation 을 활용한 Aspect 를 사용한다.
이유는 적용되면 안되는 함수가 존재하기 때문에 필요한 부분만 Annotation을 통해 처리하기 위해서이다.
아주 간단한 Annotation 을 만든다. Method에만 쓸 수 있고 아무런 기능이 없고 단지 마커 역할만 한다.
@Target(ElementType.METHOD)
public @interface ConvertIdToProfileId {
}
Aspect
@annotation 을 통하여 ConvertIdToProfileId 가 있는 부분만 실행하겠다고 선언
@Around 는 언제 이 Aspect가 동작을 시작하는지에 대한 정의이다.
5가지가 있다.
@Before : Method 호출 이전 실행
@After : Method 호출 이후 관계 없이 실행
@AfterReturning : Method 실행 이후 리턴 후 실행, Method 의 리턴 값을 변경할 수 있음.
@AfterThrowing : Method 실행 이후 예외가 발생하면 실행, 로깅용이나 그 후 작업을 위한 것, 강제로 리턴 불가능
@Around : Method 실행 전 후 다 관여, Return 이 없으면 아무것도 안넘김 ..
public class PlayerSearchAspect {
private final PlayerService playerService;
@Around("@annotation(org.example.springboot.service.aspect.ConvertIdToProfileId)")
public Object convertIdToProfileIdAndProceed(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs(); // method 의 인자들을 가져옴
String platform = args[0].toString();
String id = args[1].toString(); // 변경할 부분
Player player = playerService.findPlayerIfNotExistReturnNewEntity(platform, id);
args[1] = player.getProfileId(); // 인자 변경
return pjp.proceed(args); // 변경된 인자로 method 에 넣고 실행
}
}
여기서 around 를 한 이유는 직접적으로 args 를 id 에서 profileid 로 변경을 한뒤,
해당 메소드를 실행해야하기 때문에 이렇게 했다.
실험하다가 알게된것)
같은 annotation에 around 와 afterthrowing 을 하면 afterthrowing 작동
같은 annotation에 around 와 before 을 하면 before 작동
같은 annotation에 around 와 afterReturing 을 하면 안에 있는 다른 로직은 수행(printf 는 수행)
하지만 return 값을 강제로 null 로 했음에도 불구하고, around 의 return 값으로 나옴
결과
중복된 코드를 하나의 Aspect 로 빼서 지우고 관리도 편해졌다. 👍👍
지식이 늘어난 느낌, 내가 모르는 거지 나보다 뛰어난 사람들이 분명 만들어놨다.
'Spring' 카테고리의 다른 글
@Controller, @Service 의 차이? (@Aliasfor) (0) | 2020.07.01 |
---|---|
@SpringBootApplication (0) | 2020.06.16 |