28일차!

닥개·2026년 9월 24일

지피티야과외해줘

목록 보기
5/10

계속 test.http로 콘솔에서 찍어보고 결과받는게 불편해서 오늘부터는 html 페이지를 만들어서 하기로 했음 흐흐

28일차: Java Bean vs Spring Bean vs @Component + AOP


Java Bean vs Spring Bean vs @Component

Java Bean

데이터를 담는 클래스의 규칙:

  • 기본 생성자 있어야 함
  • private 필드
  • getter/setter 있어야 함
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 Bean

Spring이 직접 생성하고 관리하는 객체

// 개발자가 직접 관리 (Spring Bean 아님)
UserService service = new UserService();

// Spring이 관리 (Spring Bean)
@Service
public class UserService { ... }

@Component

클래스를 Spring Bean으로 등록하는 가장 기본적인 어노테이션

@Component
    ├── @Service        → 비즈니스 로직
    ├── @Repository     → DB 관련
    ├── @Controller     → MVC 컨트롤러
    └── @RestController → REST API 컨트롤러

전부 내부적으로 @Component를 포함하고 있어요!
기능은 같지만 역할을 명확하게 표현하기 위해 구분해서 사용


Java Bean vs Spring Bean 비교

Java BeanSpring Bean
관리 주체개발자Spring
생성 방법new로 직접 생성어노테이션으로 등록
목적데이터 담는 클래스Spring이 관리하는 객체
예시User.javaUserService.java

Spring AOP (관점 지향 프로그래밍)

왜 필요한가?

모든 메서드마다 실행시간 측정 코드를 반복해야 하는 문제:

// 이걸 모든 메서드마다 반복해야 함 😱
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로 한 곳에서 해결!


build.gradle 추가

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' }

LogAspect.java

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;
    }
}

AOP 핵심 용어

용어의미비유
@AspectAOP 클래스 선언공통 기능 모음집
@Around메서드 실행 전후에 끼어들기앞뒤로 코드 삽입
@Before메서드 실행 전에만 실행실행 전에만 코드 삽입
@After메서드 실행 후에만 실행실행 후에만 코드 삽입
joinPoint.proceed()실제 메서드 실행원래 코드 실행

트러블슈팅: JwtFilter AOP 충돌

에러: 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 한 곳
코드 중복많음없음
새 메서드 추가 시또 작성해야 함자동으로 적용됨

profile
발바닥부터 시작하는 코딩공부

0개의 댓글