23.06.27
미로 탐색 알고리즘
- 게임맵최단거리
public class Maze {
private int[] dx = new int[]{-1, 1, 0, 0};
private int[] dy = new int[]{0, 0, 1, -1};
public int solution(int[][] maze){
// BFS 로직을 활용하는데
// 다음에 접근할 수 있는 칸을 maze 의 가로 세로 크기 이내의
// 칸을 기준으로 판단
// int[]를 답는 Queue {x, y, 여태까지 이동거리}
Queue<int[]> visitNext = new LinkedList<>();
// 미로에서 이미 도달한 적 있는 칸임을 나타내기 위한 visited 이차원 배열
boolean[][] visited = new boolean[6][6];
visitNext.offer(new int[]{5, 0, 0});
// BFS 시작
int answer = -1;
// Queue 가 비어있지 않은 동안
while(!visitNext.isEmpty()) {
// 다음에 갈 곳을 Queue 에서 꺼낸다.
int[] next = visitNext.poll();
int nowX = next[0];
int nowY = next[1];
int steps = next[2];
if(maze[nowX][nowY] == 3) {
answer = steps;
break;
}
visited[nowX][nowY] = true;
// 4방향을 확인한다.
for (int dir = 0; dir < 4; dir++) {
int nextX = nowX + dx[dir];
int nextY = nowY + dy[dir];
if(
// 1. 미로의 범위를 벗어나진 않는지
checkBounds(nextX, nextY) &&
// 2. 미로에서 진행 가능한 칸인지 ( 0 or 3)
(maze[nextX][nextY] == 0 || maze[nextX][nextY] == 3) &&
// 3. 아직 방문한적 없는지
!visited[nextX][nextY]
) {
visitNext.offer(new int[]{nextX, nextY, steps + 1});
}
}
}
return answer;
}
// 미로의 범위 내에 있는지 검사하는 메소드
private boolean checkBounds(int x, int y) {
return x > -1 && x < 6 && y > -1 && y < 6;
}
public static void main(String[] args) {
System.out.println(new Maze().solution(new int[][]{
{0, 0, 0, 0, 0, 3},
{1, 0, 1, 1, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 1, 0, 1, 0},
{0, 0, 1, 0, 0, 0},
{2, 1, 1, 0, 1, 1}
}));
}
}
AOP 관점 지향 프로그래밍 Aspect oriented programming
우리가 여태 개발한 것은 직접적인 비즈니스 로직에 해당한다.
여러 비즈니스 로직에 동일하게 작동하는 기능을 만들고 싶다면?
- 메소드 실행의 걸린 시간
- 메소드의 반환값 등
💡 횡단 관심 : 서로 다른 비즈니스 로직이 공통적으로 가졌으면 하는 기능
관점 지향 프로그래밍은 핵심 기능을 관리하는 코드는 건드리지 않으면서 여러 핵심 기능들이 활용할 수 있는 기능을 만드는데 초점을 맞추고 있다.
로깅과 같은 단순한 목적을 위해 소스 코드의 여러 부분에 흩어진 중복된 코드를 작성할 필요를 줄여준다.
@Transactional이란?
- 한가지 일을 하는 일련의 코드들을 한 단위로 묶어서 작업을 처리하는 방법
- 여러 작업을 진행하다가 문제가 생겼을 때 이전상태로 롤백하기 위해 사용되는 트랜잭션이다.
✅ AOP 자주 사용하는 용어
- Aspect: 횡단 관점에 적용할 기능을 모듈화한 단위. 즉, 어떤 핵심 기능에 추가할 횡단 기능에 대한 정의를 담는다.
- Join Point : 실행 중이 프로그램에서 메소드 호출, 예외 발생 등의 시점을 지칭. 이러한 join point에 저희가 정의한 aspect를 적용할 수 있다.
- Advice : 특정 join point에서 실제로 실행할 기능을 나타낸다. 이때 advice는 join point의 전before, 후after, 전후around 등 다양한 시점을 지칭할 수 있다.
- Pointcut : 어느 특정한 join point를 지칭하기 위한 기준. advice가 실제로 적용될 join point를 정의하는 역할 ( 어떤 클래스의 메소드인지 등)
- Weaving : 횡단 관점을 실제 프로그램에 적용시키는 과정
Aspect 정의하기
✅ 실습
- 프로젝트 생성
- aop는 자동 의존성이 없기 때문에 수동으로 의존성주입을 해주어야한다.
- 아래 사이트에서 최신버전으로 선택해 가져와도 되지만, 뒤에 버전을 안붙이면 최신버전으로 설정이 자동적으로 된다.
Maven Repository: org.springframework.boot » spring-boot-starter-aop » 3.1.1
implementation 'org.springframework.boot:spring-boot-starter-aop:3.1.1'
implementation 'org.springframework.boot:spring-boot-starter-aop'
aop 실습
- dto→ UserDto생성
@Data
public class UserDto {
private Long id;
private String username;
private String password;
private String passwordCheck;
private String email;
private String phone;
}
- AopController 생성
@Slf4j
@RestController
@RequiredArgsConstructor
public class AopController {
@PostMapping("/users")
public ResponseDto addUser(@RequestBody UserDto user){
log.info(user.toString());
ResponseDto response = new ResponseDto();
response.setMessage("addUser");
return response;
}
}
- LoggingAspect 클래스 추가
@Slf4j //log 추가
@Aspect //이클래스가 관점(Aspect)임을 드러내는 어노테이션
@Component //Bean 객체로 등록
public class LoggingAspect {
//컨트롤러 클래스의 패키지를 포함한 full 명
// @Before: Adivice: 실제로 실행될 코드를 나타냄
// @Before.value: Pointcut Designator, 어느 joinpoint에서 실행될것인지
@Before("this(com.example.aop.controller.AopController)")
public void logParameters(JoinPoint joinPoint){
log.info("hello aop!");
}
}
postman
- LoggingAspect수정
@Slf4j //log 추가
@Aspect //이클래스가 관점(Aspect)임을 드러내는 어노테이션
@Component //Bean 객체로 등록
public class LoggingAspect {
//컨트롤러 클래스의 패키지를 포함한 full 명
// @Before: Adivice: 실제로 실행될 코드를 나타냄
// @Before.value: Pointcut Designator, 어느 joinpoint에서 실행될것인지
@Before("this(com.example.aop.controller.AopController)")
//JoinPoint: 이 advice 가 실행된 joinpoint(adduser)
public void logParameters(JoinPoint joinPoint){
//signature : joinpoint의 정보를 담은 객체
Signature signature = joinPoint.getSignature();
//메소드 이름 로그
log.info(signature.getName());
//메소들 인자들 확인
Object[] arguments = joinPoint.getArgs();
//인자가 없을 때
if (arguments.length == 0 ){
log.info("no args");
}
for (Object argument : arguments) {
log.info("argument: [{}]", argument);
}
}
}
signature 코드 추가
적용해볼만한 Pointcut Designator
1. execution : 메소드 지정
execution : 메소드를 지정합니다. 이때, 클래스 내부의 메소드를 직접 지정할 수도 있지만, 클래스 내부의 메소드 전부를 지정하도록 할 수도 있습니다.
@Before("execution(public " +
"com.example.aop.dto.ResponseDto " +
"com.example.aop.AopController.addUser(" +
"com.example.aop.dto.UserDto))")
// 또는
@Before("execution(public " +
"com.example.aop.dto.ResponseDto " +
"com.example.aop.AopController.*(..))")
2. withtin : 특정 클래스를 지정합니다. 클래스가 아니라 패키지 단위로 지정할수도 있습니다.
@Before("this(com.example.aop.AopController)")
// 또는
@Before("within(com.example.aop..*)")
3. @annotation : 어떤 특정 어노테이션이 붙은 Join Point를 지정합니다. 이 어노테이션은 저희가 직접 제작할 수 있습니다.
@Before("@annotation(com.example.aop.aspects.logging.LogArguments)")
AOP에서 Pointcut을 지정하는 방법은 다양하게 존재합니다. 상황에 맞는 Pointcut을 선택하도록 합시다.