곡λΆνκ² λ κ³κΈ°
ν νλ‘μ νΈλ₯Ό μ§ννλ λμ, νμμ΄ AOP(Aspect-Oriented Programming)λ₯Ό νμ©ν΄ κΆν κ²μ¬λ₯Ό ꡬννλ λͺ¨μ΅μ λ³΄κ² λμλ€. μ²μμλ AOPλ₯Ό μ΄μ©ν΄ 볡μ‘ν λ‘μ§μ κ°κ²°νκ² μ²λ¦¬νλ λͺ¨μ΅μ΄ λλΌμ κ³ , λμμ λ΄κ° μ§μ ꡬννκΈ°μ μ΄λ ΅κ² λ€λ μκ°μ΄ λ€μλ€. νμ§λ§ νμμ μ½λλ₯Ό ν¨κ» λΆμνλ©° μλ λ°©μμ μ΄ν΄νλ κ³Όμ μμ, "λλ λ
Έλ ₯νλ©΄ μΆ©λΆν AOPλ₯Ό νμ©ν μ μκ² λ€"λ μμ κ°μ΄ μκ²Όλ€.
μ΄ν, AOPμ κ°λ
κ³Ό νμ© μ¬λ‘λ₯Ό λ κΉμ΄ μ΄ν΄νκ³ μΆλ€λ μ΄μ μ΄ μκ²Όκ³ , κ΄λ ¨ μλ£λ₯Ό μ°Ύμ보며 νμ΅μ μμνλ€. μ΄λ² κ²½νμ λ¨μν μλ‘μ΄ κΈ°μ μ λ°°μ°λ κ²μ λμ΄, λ΄κ° κΈ°μ μ μΌλ‘ μ±μ₯ν μ μλ€λ κ°λ₯μ±μ νμΈν κ³κΈ°κ° λμλ€.
μ½λλ₯Ό μμ±ν λ κ°μ₯ μ€μν μμ
λ°λ‘ κ°λ μ±μ΄λΌκ³ μκ°νλ€.
νλ‘μ νΈκ° 컀μ§λ©΄ 컀μ§μλ‘, κ³ λνλ μλ‘ μ½λμ 볡μ‘μ±μΌλ‘ μΈν΄ μμ°μ±μ΄ λ¨μ΄μ§λ€. κ·Έλμ μ²μ νλ‘μ νΈλ₯Ό μμν λ μ΄λ° μμλ₯Ό κ³ λ €νλ©΄μ μμ°μ±μ΄ λ¨μ΄μ§λ μν©μ κ²½κ³ν΄μΌ νλ€.
κ·Έλ° κ°λ
μ±μ μ¦κ°μν€κ³ μ½λμ 볡μ‘μ±μ μ½κ² μ€μ¬μ£Όλ λ
μμ΄ λ°λ‘ AOPμ΄λ€.
μμ
μ΄λ€ μ½λκ° κ°λ μ±μ΄ λ μ’μκ°μ?!
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void viewProfile(String username, String role) {
if (!"ADMIN".equals(role)) {
System.out.println("Access Denied: Only ADMIN can view profiles.");
return;
}
System.out.println("Profile of " + username + " viewed successfully.");
}
public void updateProfile(String username, String role) {
if (!"ADMIN".equals(role)) {
System.out.println("Access Denied: Only ADMIN can update profiles.");
return;
}
System.out.println("Profile of " + username + " updated successfully.");
}
public void deleteProfile(String username, String role) {
if (!"ADMIN".equals(role)) {
System.out.println("Access Denied: Only ADMIN can delete profiles.");
return;
}
System.out.println("Profile of " + username + " deleted successfully.");
}
public void listUsers(String role) {
if (!"USER".equals(role) && !"ADMIN".equals(role)) {
System.out.println("Access Denied: Only USER or ADMIN can view the user list.");
return;
}
System.out.println("User list viewed successfully.");
}
public void viewDashboard(String role) {
if (!"USER".equals(role) && !"ADMIN".equals(role)) {
System.out.println("Access Denied: Only USER or ADMIN can view the dashboard.");
return;
}
System.out.println("Dashboard viewed successfully.");
}
}
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void viewProfile(String username, String role) {
System.out.println("Profile of " + username + " viewed successfully.");
}
public void updateProfile(String username, String role) {
System.out.println("Profile of " + username + " updated successfully.");
}
public void deleteProfile(String username, String role) {
System.out.println("Profile of " + username + " deleted successfully.");
}
public void listUsers(String role) {
System.out.println("User list viewed successfully.");
}
public void viewDashboard(String role) {
System.out.println("Dashboard viewed successfully.");
}
}
κ·Έλ λ€λ©΄ AOPλ 무μμΌκΉ??
AOP(Aspect-Oriental Programming)μ κ΄μ (Aspect) μ§ν₯ νλ‘κ·Έλλ°μΌλ‘, κ΄μ μ κΈ°μ€μΌλ‘ λ€μν κΈ°λ₯μ λΆλ¦¬νμ¬ λ³΄λ νλ‘κ·Έλλ°μ΄λ€.
- Aspect: λΆκ° κΈ°λ₯κ³Ό κ·Έκ²μ μ μ©νλ κ³³μ μ μνκ³ ν©μ³μ λͺ¨λλ‘ λ§λ κ²μ΄λ€.
μ ν리μΌμ΄μ μ ν΅μ¬ λΉμ¦λμ€ λ‘μ§κ³Ό κ΄λ ¨ μλ λΆκ°μ μΈ κΈ°λ₯λ€μ λͺ¨λν νμ¬ μ½λμ μ€λ³΅μ μ€μ΄κ³ μ μ§λ³΄μμ±μ ν₯μνλ λ°μ μ£Όλ‘ νμ©λλ€.
AOPλ₯Ό μ¬μ©νλ μ΄μ
κ°μ²΄ μ§ν₯ νλ‘κ·Έλλ°μ 보μνκΈ° μν΄μ μ¬μ©λλ€.
λΆκ°μ μΈ κ΄μ¬μ¬μ λΆλ¦¬
- μ ν리μΌμ΄μ μμ μμ£Ό λ°λ³΅λλ μμ (λ‘κΉ , 보μ, νΈλμμ κ΄λ¦¬ λ±)μ λΉμ¦λμ€ λ‘μ§κ³Ό λΆλ¦¬νμ¬ μ½λ μ€λ³΅μ μ΅μννκ³ μ¬μ¬μ©μ±μ λμΈλ€.
μ μ§λ³΄μμ± ν₯μ
- κ΄μ¬μ¬λ₯Ό λΆλ¦¬ν¨μΌλ‘μ¨ λΉμ¦λμ€ λ‘μ§μ΄ λ¨μν΄μ§κ³ , λΆκ°μ μΈ κ΄μ¬μ¬λ₯Ό λ 립μ μΌλ‘ κ΄λ¦¬ν μ μμ΄ μ μ§ λ³΄μκ° νΈλ¦¬ν΄μ§λ€.
μ½λ μ¬μ¬μ©μ± μ¦κ°
- 곡ν΅μ μΌλ‘ μ¬μ©λλ κΈ°λ₯μ νλμ Aspectλ‘ μ μνκ³ μ¬λ¬ κ³³μμ μ¬μ¬μ©ν μ μλ€.
λ€μν AOP μ μ© λ°©μ
μ»΄νμΌ μμ μ μ©
- Aspect μ»΄νμΌλ¬κ° μΌλ° .java νμΌμ μ»΄νμΌν λ λΆκ° κΈ°λ₯μ λ£μ΄μ .class νμΌλ‘ μ»΄νμΌν΄ μ£Όλ κ²μ μλ―Ένλ€.
ν΄λμ€ λ‘λ© μμ μ μ©
- JVM λ΄ ν΄λμ€ λ‘λμ .class νμΌμ μ¬λ¦¬λ μμ μ λ°μ΄νΈ μ½λλ₯Ό μ‘°μν΄μ λΆκ° κΈ°λ₯ λ‘μ§μ μΆκ°νλ λ°©μμ΄λ€.
λ°νμ μμ μ μ©
- μ»΄νμΌ, ν΄λμ€λ‘λ©, main( ) λ©μλμ μ€ν μ΄νμ μλ°κ° μ 곡νλ λ²μ λ΄μ λΆκ° κΈ°λ₯μ μ μ©νλ λ°©μμ΄λ€.
- μ΄λ―Έ λ°νμ μ€μ΄λΌ μ½λλ₯Ό μ‘°μνκΈ° μ΄λ €μ μ€νλ§ μ»¨ν
μ΄λ, DI, λΉ λ± μ¬λ¬ κ°λ
κ³Ό κΈ°λ₯μ μ΄λμνμ¬ Proxyλ₯Ό ν΅ν΄ λΆκ° κΈ°λ₯μ μ μ©νλ λ°©μμ΄λ€.
- Proxyλ λ©μλ μ€ν μμ μμλ§ λ€μ νκΉμ νΈμΆν μ μκΈ° λλ¬Έμ, λ°νμ μμ μ λΆκ° κΈ°λ₯μ μ μ©νλ λ°©μμ λ©μλμ μ€ν μ§μ μΌλ‘ μ νλλ€.
Spring AOP
μ£Όλ‘ λ°νμ μμ μ μ μ©νλ λ°©μμ μ¬μ©νλ€.
μ»΄νμΌ μμ κ³Ό ν΄λμ€ λ‘λ© μμ μ μ μ©νλ €λ©΄ λ³λμ μ»΄νμΌλ¬μ ν΄λμ€λ‘λ μ‘°μκΈ°λ₯Ό μ¨μΌ νλλ°, μ΄κ²μ μ νκ³ μ¬μ© λ° μ μ§νλ κ³Όμ μ΄ λ§€μ° μ΄λ ΅κ³ 볡μ‘νκΈ° λλ¬Έμ΄λ€.
Spring AOPλ₯Ό μ μ©νκΈ° μν΄μλ build.gradleμ μμ‘΄μ±μ μΆκ°ν΄μ€μΌ νλ€.
implementation 'org.springframework.boot:spring-boot-starter-aop' // μ€νλ§ aop μΆκ°
AOPκ° νμ©λλ λνμ μΈ μμ
- νΈλμμ κ΄λ¦¬
- λ‘κΉ
- 보μ / κΆν νμΈ
- μ±λ₯ λͺ¨λν°
AOP μ©μ΄
Aspect (Advice + Pointcut)
- AOPλ₯Ό μ μν ν΄λμ€ μμ λΆμ¬μ€μΌ νλ€. ex) @Service, @RestController
- @Aspectμ @Componentλ‘ μ μΈν΄μ€λ€.
@Aspect
@Component
public class AOP μ μν ν΄λμ€ {
}
Pointcut
- Join Point μ€μμ Adviceλ₯Ό μ΄λμ μ μ©ν μ§, μ μ©νμ§ μμμ§ μμΉλ₯Ό νλ¨νλ νν°λ§νλ κΈ°λ₯μ΄λ€. μ€μ Adviceκ° μ μ©λλ λΆλΆμ΄λ€.
- AOPλ₯Ό μ μ©ν λ©μλλ₯Ό μ μνλ ννμμ μ¬μ©νλ€.
- μ: νΉμ ν¨ν€μ§μ λͺ¨λ λ©μλ, νΉμ μ΄λ Έν μ΄μ μ΄ λΆμ λ©μλ λ±.
- μ΄λ€ λμμ AOPλ₯Ό μ μ©ν κ²μΈμ§ μ§μ ν΄ μ£Όλ κ²μ΄λ€.
@Aspect
@Component
public class AOP μ μν ν΄λμ€ {
// Pointcut: com.example.service ν¨ν€μ§ λ΄μ λͺ¨λ λ©μλμ AOP μ μ©
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
}
Pointcut ννμ → λͺ°λΌλ λλ€! μ°λ¦¬κ° μ κ·μμ μΈμΈ νμκ° μμλ κ²μ²λΌ μΈμΈ νμκ° μλ€!
Advice
- Join Pointμμ μ μ©ν λΆκ° λ‘μ§μ μ’
λ₯λ₯Ό μ μνλ€. μ€ν μμ μ λ°λΌ μ¬λ¬ μ νμ΄ μλ€.
- @Before: λ©μλκ° μ€νλκΈ° μ μ μ€ννλ€.
- @After: λ©μλκ° μ€νλ νμ μ€ννλ€.
- @AfterReturning: λ©μλκ° μ μμ μΌλ‘ μ€νλ νμ μ€ννλ€.
- @AfterThrowing: λ©μλκ° μ€ν μ€ μμΈκ° λ°μνμ λ μ€ννλ€.
- @Around: λ©μλ μ€ν μ νμ μ€ννλ€.
@Aspect
@Component
public class AOP μ μν ν΄λμ€ {
// Pointcut: com.example.service ν¨ν€μ§ λ΄μ λͺ¨λ λ©μλ(*)μ AOP μ μ©
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// Before Advice: λ©μλ μ€ν μ μ λ‘κ·Έ μΆλ ₯
@Before("serviceMethods()")
public void logBefore() {
System.out.println("[Before] Method execution started.");
}
// After Advice: λ©μλ μ€ν ν λ‘κ·Έ μΆλ ₯
@After("serviceMethods()")
public void logAfter() {
System.out.println("[After] Method execution finished.");
}
}
Join Point
- AOPκ° μ μ©λλ μ€ν μμ μ μ 보λ₯Ό λ΄κ³ μλ€. μ ν리μΌμ΄μ μ€ν νλ¦μμμ νΉμ ν¬μΈνΈμ΄λ€.
- Spring AOPλ Proxy λ°©μμ μ¬μ©νκΈ° λλ¬Έμ Join Pointλ νμ λ©μλ μ€ν μ§μ μΌλ‘ μ νλλ€.
- λ©μλ μ΄λ¦, 맀κ°λ³μ λ± μ€ν μ 보λ₯Ό μ‘°ν κ°λ₯νλ€.
// Around Advice: μ€ν μ νλ₯Ό κ°μΈμ λ‘μ§ μν
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[Around] Before method: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed(); // λ©μλ μ€ν
System.out.println("[Around] After method: " + joinPoint.getSignature().getName());
return result;
}
컀μ€ν μ΄λ Έν μ΄μ μ νμ©ν AOP
컀μ€ν μ΄λ Έν μ΄μ @AuthCheck
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
String[] value()
}
컀μ€ν μ΄λ Έν μ΄μ μ μ΄μ©ν΄ κΆν κ²μ¬νλ AOP
@Slf4j
@Component
@RequiredArgsConstructor
@Aspect
public class CheckRoleStoreAspect {
private final HttpServletRequest request;
@Around("@annotation(team11.team11project.common.aspect.AuthCheck)")
public Object checkRole(ProceedingJoinPoint joinPoint) throws Throwable{
//κΆν κ°μ Έμ€κΈ°
String role = (String) request.getAttribute("role");
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
AuthCheck authCheck = method.getAnnotation(AuthCheck.class);
//κΆν μ λ³΄κ° μλ κ²½μ° μμΈ μ²λ¦¬
if (role == null) {
throw new UserRoleNotFoundException("μ¬μ©μ κΆν μ λ³΄κ° μμ΅λλ€.");
}
//μ λ
Έν
μ΄μ
κΆν κ°κ³Ό μμ² λ°μ κΆν κ°μ νμΈ. λ§μ½ λ€λ₯΄λ©΄ μμΈμ²λ¦¬.
if (Arrays.stream(authCheck.value()).noneMatch(role::equals)) {
throw new UnauthorizedAccessException(String.format(" %s κΆνλ§ μ κ·Όν μ μμ΅λλ€.",
Arrays.toString(authCheck.value())));
}
log.info("κΆν νμΈ μλ£. μμ² κΆν : {}, νμ κΆν : {}", role, authCheck.value() );
return joinPoint.proceed();
}
}
컀μ€ν μ΄λ Έν μ΄μ μ μ¬μ©ν κ²½μ° μ₯μ
- μ μ§λ³΄μ μ©μ΄
- μ½λ κ°κ²°μ± → κ°λ μ± κ°μ
- νμ₯μ± μ¦κ°
AOPμ κ΄λ ¨λ Proxy
Proxyλ μλ° λ° μ€νλ§μμ μ±λ₯ ν₯μμ μν΄μ μ¬μ©νλ μλ§μ λμμΈ ν¨ν΄ μ€ νλμ΄λ€.
κ°μ²΄ μ§ν₯ νλ‘κ·Έλλ°μμ "λ리μΈ" μν μ νλ λμμΈ ν¨ν΄μ΄λ€.
μ€μ κ°μ²΄μ λν μ κ·Όμ μ μ΄νκ±°λ λΆκ°μ μΈ μμ
μ μννκΈ° μν΄ μ¬μ©λλ€.
Proxyμ μν
- μ κ·Ό μ μ΄: μ€μ κ°μ²΄μ λν μ κ·Όμ μ ννκ±°λ 쑰건μ κ²μ¦νλ€.
- λΆκ° μμ μν: μμ² μ /νμ μΆκ°μ μΈ μμ μν (λ‘κΉ , νΈλμμ κ΄λ¦¬ λ±)
- μ§μ° μ΄κΈ°ν: μ€μ κ°μ²΄λ₯Ό νμν λκΉμ§ μμ±νμ§ μλλ€. (μ§μ° λ‘λ©)
Proxyλ₯Ό μ¬μ©νλ μ΄μ
- μ κ·Ό μ μ΄: Proxyλ κ°μ²΄μ λν 보μ κ²μ¬λ κΆν νμΈ κ°μ μμ μ μ½κ² μ²λ¦¬ν μ μλ€.
- λΆκ° μμ μν: ν΄λΌμ΄μΈνΈ μ½λμ μν₯μ μ£Όμ§ μκ³ λΆκ°μ μΈ κΈ°λ₯(λ‘κΉ , νΈλμμ )μ μΆκ°ν μ μλ€.
- μ±λ₯ μ΅μ ν: μ€μ κ°μ²΄μ μμ±μ΄λ μ΄κΈ°νκ° λΉμ©μ΄ λ§μ΄ λ€ κ²½μ°, Proxyλ₯Ό μ¬μ©ν΄ μ§μ° λ‘λ©μ ꡬνν μ μλ€.
References
μ€νλ₯΄ν λ΄μΌλ°°μμΊ ν μμ€λ³ νμ΅λ° μ€ν λ€λλ° μΈμ
https://sharonprogress.tistory.com/195
https://velog.io/@kai6666/Spring-Spring-AOP-κ°λ
https://hstory0208.tistory.com/entry/Spring-μ€νλ§-AOPAspect-Oriented-Programmingλ-Aspect
'π Java & Spring & Kotlin > π± Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[ Spring ] CascadeType.REMOVEμ orphanRemoval=true, μλͺ» μ°λ©΄ λ°μ΄ν°κ° λ μκ°λλ€!! (0) | 2025.01.17 |
---|---|
[ Spring ] νΈλμμ .. λ€κ°κ°μλ‘ λ©κ² λκ»΄μ§λ μΉκ΅¬ (0) | 2025.01.05 |