계속 test.http로 콘솔에서 찍어보고 결과받는게 불편해서 오늘부터는 html 페이지를 만들어서 하기로 했음 흐흐
데이터를 담는 클래스의 규칙:
public class Person {
private String name; // private 필드
private int age;
public Person() {} // 기본 생성자
public String getName() { return name; } // getter
public void setName(String name) { this.name = name; } // setter
}
우리가 만든
User.java가 바로 Java Bean!
Spring이 직접 생성하고 관리하는 객체
// 개발자가 직접 관리 (Spring Bean 아님)
UserService service = new UserService();
// Spring이 관리 (Spring Bean)
@Service
public class UserService { ... }
클래스를 Spring Bean으로 등록하는 가장 기본적인 어노테이션
@Component
├── @Service → 비즈니스 로직
├── @Repository → DB 관련
├── @Controller → MVC 컨트롤러
└── @RestController → REST API 컨트롤러
전부 내부적으로
@Component를 포함하고 있어요!
기능은 같지만 역할을 명확하게 표현하기 위해 구분해서 사용
| Java Bean | Spring Bean | |
|---|---|---|
| 관리 주체 | 개발자 | Spring |
| 생성 방법 | new로 직접 생성 | 어노테이션으로 등록 |
| 목적 | 데이터 담는 클래스 | Spring이 관리하는 객체 |
| 예시 | User.java | UserService.java |
모든 메서드마다 실행시간 측정 코드를 반복해야 하는 문제:
// 이걸 모든 메서드마다 반복해야 함 😱
public User findById(Long id) {
long start = System.currentTimeMillis(); // 반복
User user = userRepository.findById(id)...;
long end = System.currentTimeMillis(); // 반복
System.out.println("실행시간: " + (end - start)); // 반복
return user;
}
메서드가 100개면 100번 반복! → AOP로 한 곳에서 해결!
implementation 'org.springframework.boot:spring-boot-starter-aop'
⚠️ Spring Boot 4.x 마일스톤 버전에서는 AOP가 없을 수 있어요.
이 경우build.gradle의 Spring Boot 버전을 3.3.0으로 다운그레이드!
버전 다운그레이드 시 변경사항:
// plugins
id 'org.springframework.boot' version '3.3.0' // 4.2.0-M1 → 3.3.0
// dependencies
implementation 'org.springframework.boot:spring-boot-starter-web' // webmvc → web
testImplementation 'org.springframework.boot:spring-boot-starter-test' // webmvc-test → test
// repositories (마일스톤 저장소 추가)
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
package com.example.demo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect // AOP 클래스 선언
@Component // Spring Bean으로 등록
public class LogAspect {
// com.example.demo 패키지의 모든 메서드 실행 전후
// 단, JwtFilter는 제외! (final 메서드 충돌 방지)
@Around("execution(* com.example.demo..*(..)) " +
"&& !within(com.example.demo.JwtFilter)")
public Object logtime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 실제 메서드 실행
long end = System.currentTimeMillis();
System.out.println(joinPoint.getSignature().getName()
+ " 실행시간: " + (end - start) + "ms");
return result;
}
}
| 용어 | 의미 | 비유 |
|---|---|---|
@Aspect | AOP 클래스 선언 | 공통 기능 모음집 |
@Around | 메서드 실행 전후에 끼어들기 | 앞뒤로 코드 삽입 |
@Before | 메서드 실행 전에만 실행 | 실행 전에만 코드 삽입 |
@After | 메서드 실행 후에만 실행 | 실행 후에만 코드 삽입 |
joinPoint.proceed() | 실제 메서드 실행 | 원래 코드 실행 |
에러: Unable to proxy interface-implementing method because it is marked as final
원인: AOP가 JwtFilter의 final 메서드를 프록시하려다 충돌 발생
해결: JwtFilter를 AOP 대상에서 제외
// !within(com.example.demo.JwtFilter) 추가로 JwtFilter 제외
@Around("execution(* com.example.demo..*(..)) " +
"&& !within(com.example.demo.JwtFilter)")
API 호출 시 IntelliJ 콘솔에 자동으로 출력됨:
findAll 실행시간: 17ms
findById 실행시간: 5ms
join 실행시간: 23ms
| AOP 없을 때 | AOP 있을 때 | |
|---|---|---|
| 실행시간 측정 위치 | 메서드마다 직접 작성 | LogAspect 한 곳 |
| 코드 중복 | 많음 | 없음 |
| 새 메서드 추가 시 | 또 작성해야 함 | 자동으로 적용됨 |