str에서 숫자를 찾아 다 더하고 문자열의 (숫자와 공백을 제외한 나머지) 길이로 나누고 반올림하여 리턴하라.
public int numberSearch(String str) {
double sum =0;
for(int i=0; i<str.length(); i++){
for(int j=1; j<=10; j++){
if(str.charAt(i)==j+'0'){
sum += str.charAt(i)-48;
}
}
}
return (int) Math.round(sum/str.length());
}
숫자와 공백을 제외한 나머지 << 조건을 뺴먹음
public class NumberSearch { // str에서 숫자를 찾아 다 더하고 문자열의 (숫자와 공백을 제외한 나머지) 길이로 나누고 반올림하여 리턴하라.
public int numberSearch(String str) {
String str2 = "";
double sum =0;
for(int i=0; i<str.length(); i++){
boolean isNum = false;
for(int j=1; j<=10; j++){
if(str.charAt(i)==j+'0'){
sum += str.charAt(i)-48;
isNum =true;
break;
}
}
if(!isNum && !(str.charAt(i)==' ')) str2 += str.charAt(i);
}
int result = (int) Math.round(sum/str2.length());
return result;
}
}
모든 테스트 통과
아래는 indexOf()
를 활용한 방식
public int numberSearch2(String str) {
String digits = "0123456789";
if(str.length() == 0) return 0;
double sum = 0;
String pureStr = "";
for(int i = 0; i < str.length(); i++) {
if(digits.indexOf(str.charAt(i)) != -1) { //digits의 값에서 str[i]의 값이 포함되어 있다면 (cf. indefxOf는 값이 존재하면 해당 인덱스를, 없다면 -1을 리턴)
sum = sum + Character.getNumericValue(str.charAt(i)); // Character.getNumericValue() >> 문자 -> int로 바꾸는 메서드
} else if(str.charAt(i) != ' ') {
pureStr = pureStr + str.charAt(i);
}
}
double result = sum / pureStr.length();
return (int) Math.round((result * 10) / 10);
}
indefxOf
는 값이 존재하면 해당 인덱스를, 없다면 -1을 리턴
Character.getNumericValue()
는 문자를 int로 바꾸는 메서드
카이사르 암호(Caesar cipher)를 복호화 하라.
public String decryptCaesarCipher(String str, int secret) {
String result = "";
char A = 'A'+1;
result += A;
return result;
}
//출력
B
문자에 숫자를 더하면 그 숫자만큼 평행이동 된 문자가 출력되는 것을 확인했다.
public String decryptCaesarCipher(String str, int secret) {
String result = "";
for(int i =0; i<str.length(); i++){
------------------------------------------------오류
char add = str.charAt(i)-secret; // 우항 (char)해줘야 함
------------------------------------------------오류
result += add;
}
return result;
}
똑같이 char 연산자 int인데 강제 타입변환을 안하면 오류가 뜬다.
오류 이유 : char형은 유니코드로 관리하고 있어서, 음수가 나올 경우가 존재해서 아예 연산 자체를 배제하고 있다.
강제로 형변환되는걸 활용하면 되긴하는데 예측하지 못한 상황이 발생할 수 있어서, 다른 방식을 사용해야 할 것 같다.
public String decryptCaesarCipher(String str, int secret) {
String result = "";
String alp = "abcdefghijklmnopqrstuvwxyz";
for(int i =0; i<str.length(); i++){
if(str.charAt(i)==' '){ // 공백일 경우 그대로 공백 추가
result += ' ';
}else{
char add = str.charAt(i);
if(alp.indexOf(add)-secret>=0){ //문자열의 index를 벗어날 경우를 고려해서 범위를 나눠 줌
result += alp.charAt(alp.indexOf(add)-secret);
}else{
result += alp.charAt(alp.length()-Math.abs(alp.indexOf(add)-secret));
}
}
}
return result;
}
모든 테스트 통과
public String decryptCaesarCipher2(String str, int secret) {
String result = "";
String alpha = "abcdefghijklmnopqrstuvwxyz";
for(int i = 0; i < str.length(); i++) {
if(str.charAt(i) == ' ') {
result = result + str.charAt(i);
} else {
int asis = alpha.indexOf(str.charAt(i));
int tobe = (asis - secret + alpha.length()) % alpha.length(); // 범위를 나누지 않고 이렇게도 가능
result = result + alpha.charAt(tobe);
}
}
return result;
}
범위를 굳이 나누지 않고도 위와 같이 가능
관점 지향 프로그래밍 (Aspect-Oriented Programming)
cf. OOP(객체지향 프로그래밍)을 대체하는 것이 아닌 부족한 부분을 보조하는 목적
애스펙트(Aspect)
- 부가 기능을 정의한 코드인 어드바이스(Advice)와 어드바이스를 어디에 적용할지 결정하는 포인트컷(PointCut)을 합친 개념
Advice + PointCut ⇒ Aspect
핵심 기능(Core Concerns)
- 고유의 기능(업무 로직을 포함)
부가 기능(CROSS-CUTTING CONCERNS)
- 핵심 기능을 도와주는 부가적인 기능
- ex. 트랜잭션, 보안, 로깅(로그 추적) 코드
- 단독 사용 X, 핵심 기능과 함께 사용
애스팩트(Aspect)
- 여러 객체에 공통으로 적용되는 기능
- 어드바이스 + 포인트컷을 모듈화하여 애플리케이션에 포함되는 횡단 기능
- 여러
어드바이스
와포인트컷
이 함께 존재
어드바이스(Advice)
조인포인트
에서 수행되는 코드를 의미- Aspect를 언제 핵심 코드에 적용할 지를 정의한다.
- 시스템 전체 Aspect에 API 호출을 제공
- 부가 기능에 해당 됨.
조인 포인트(join point)
- 애플리케이션 실행 흐름에서의 특정 포인트를 의미
- 조인포인트에 관심 코드(aspect code)를 추가할 수 있다.
(=> 애플리케이션에 새로운 동작을 추가할 수 있다.)- 횡단 관심은 조인포인트 전/후에 AOP에 의해 자동으로 추가된다.
- 추성적인 개념이고 AOP를 적용할 수 있는 모든 지점이라 생각하면 됨
- 조인 포인트는 항상 메소드 실행 지점으로 제한된다.
- 스프링 AOP는 프록시 방식을 사용하기 때문
- 어드바이스 적용이 필요한 곳은 애플리케이션 내에 메서드를 갖는다.
포인트컷(Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능
위빙(Weaving)
- 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용하는 것
- Advice를 핵심 코드에 적용하는 것을 의미
- 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가 할 수 있다.
AOP 프록시(proxy)
- 타겟을 감싸서 타겟의 요청을 대신 받아주는 랩핑(Wrapping) 오브젝트
- AOP 기능을 구현하기 위해 만든 프록시 객체
- 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시다.
타겟 (Target)
- 부가기능을 부여할 대상
- 핵심 기능을 담고 있는 모듈
- Adivce를 받는 객체이고 포인트컷으로 결정된다.
어드바이저(Advisor)
- 하나의
어드바이스
와 하나의포인트 컷
으로 구성된 것
어드바이스는 기본적으로 순서를 보장하지 않는다.
@Aspect
적용 단위로 org.springframework.core.annotation.@Order
애너테이션을 적용해야 함@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}
- 작업 흐름을 변경할 수 없다.
- 메서드 종료 시 자동으로 다음 타겟이 호출된다.
@AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
log.info("[return] {} return={}", joinPoint.getSignature(), result);
}
- returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
- returning 절에 지정된 타입의 값을 반환하는 메서드만 대상을 실행된다.
@AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
public void doThrowing(JoinPoint joinPoint, Exception ex) {
log.info("[ex] {} message={}", joinPoint.getSignature(), ex.getMessage());
}
- throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
- throwing 절에 지정된 타입과 맞는 예외를 대상으로 실행
@Around만 있어도 모든 기능 수행이 가능하다. (매우 강력)
(타겟 등 고려해야할 사항이 있을 때 정상적으로 작동이 되지 않는 경우 있음)
cf.
- @Before, @After와 같은 어드바이스는 기능은 적지만 원하는대로 작동되고 코드가 단순하다.
- 각 애너테이션만 봐도 타겟 실행 전에 어떤 일을 하는지 명확하게 알 수 있다.
- @Around만 사용해서 모두 해결하는 것보다 제약을 가지더라도 실수를 미연에 방지하는게 더 좋다.
- 제약을 두면 문제 자체가 발생하지 않게 하며, 역할이 명확해진다.
포인트컷은 관심 조인 포인트를 결정하므로 어드바이스가 실행되는 시기를 제어할 수 있다.
@Pointcut("execution(* transfer(..))") // 포인트컷 표현식
private void anyOldTransfer() {} // 포인트컷 서명
포인트컷 표현식은 execution 같은 포인트컷 지시자(Pointcut Designator, PCD)로 시작한다.
종류 | 설명 |
---|---|
execution | 메서드 실행 조인트 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다. |
within | 특정 타입 내의 조인 포인트를 매칭한다. |
args | 인자가 주어진 타입의 인스턴스인 조인 포인트 |
this | 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트 |
target | Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트 |
@target | 실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트 |
@within | 주어진 애너테이션이 있는 타입 내 조인 포인트 |
@annotation | 메서드가 주어니 애너테이션을 가지고 있는 조인 포인트를 매칭 |
@args | 전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트 |
bean | 스프링 전용 포인트컷 지시자이고 빈의 이름으로 포인트컷을 지정한다. |
execution
을 주로 사용하고 나머지는 거의 사용 안함.
포인트컷 표현식은 &&, ||, ! 를 사용하여 결합할 수 있다.
이름으로 pointcut 표현식을 참조할 수도 있다.
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} // 메서드 실행 조인 포인트가 공용 메서드의 실행을 나타내는 경우 일치
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} // 메서드 실행이 거래 모듈에 있는 경우에 일치
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} // 메서드 실행이 거래 모듈의 공개 메서드를 나타내는 경우 일치
// 모든 공개 메서드 실행
execution(public * *(..))
// set 다음 이름으로 시작하는 모든 메서드 실행
execution(* set*(..))
// AccountService 인터페이스에 의해 정의된 모든 메서드의 실행
execution(* com.xyz.service.AccountService.*(..))
// service 패키지에 정의된 메서드 실행
execution(* com.xyz.service.*.*(..))
// 서비스 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.xyz.service..*.*(..))
//서비스 패키지 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
within(com.xyz.service.*)
//서비스 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
within(com.xyz.service..*)
//AccountService 프록시가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
this(com.xyz.service.AccountService)
//AccountService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
target(com.xyz.service.AccountService)
//단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable과 같은 모든 조인 포인트 (Spring AOP에서만 메소드 실행)
args(java.io.Serializable)
//대상 객체에 @Transactional 애너테이션이 있는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
@target(org.springframework.transaction.annotation.Transactional)
//실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트 (Spring AOP에서만 메서드 실행)
@annotation(org.springframework.transaction.annotation.Transactional)
//단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트(Spring AOP에서만 메서드 실행)
@args(com.xyz.security.Classified)
//tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
bean(tradeService)
//와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(*Service)
조인 포인트는 추상적인 개념이고, AOP를 적용할 수 있는 지점을 의미
프록시를 사용하는 스프링 AOP
AspectJ를 사용하는 AOP는 모든 지점 적용 가능
JoinPoint.getArgs()
JoinPoint.getThis()
JoinPoint.getTarget()
JoinPoint.getSignature()
JoinPoint.toString()
proceed()
@AspectJ
애너테이션이 있는 일반 Java 클래스로 관점을 선언하는 스타일
@Configuration
으로 @AspectJ
지원을 활성화하려면 @EnableAspectJAutoProxy
애너테이션을 추가하면 된다.
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
@AspectJ 지원이 활성화되면 @AspectJ 관점(@Aspect 애너테이션)이 있는 클래스로 애플리케이션 컨텍스트에 정의된 모든 빈이 Spring에서 자동으로 감지되고 Spring AOP를 구성하는 데 사용된다.
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
pointcut 표현식은 @Pointcut
어노테이션을 사용하여 표시
test파일의 경로를 잘못 설정해서
Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
오류가 났었다.
아래과 같이 경로를 일치시켜 해결
실습내용은 깃허브에서 확인