[Spring] AOP, 직렬화, 람다식

2025. 11. 3. 02:31·백엔드 공부(BE, AWS)

발생한 오류

2025-11-02T22:11:09.024+09:00 WARN 15964 --- [nio-8080-exec-1].m.m.a.ExceptionHandlerExceptionResolver 
	:Resolved [org.springframework.http.converter.HttpMessageConversionException: 
    Type definition error:[simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]]

 

직렬화 문제

지피티가 맨날 말하는 문제. 이게무엇이냐...  

엔티티(객체) -> JSON데이터로 구조화하는 과정에서 발생하는 문제이다.

 

문제상황을 다시보자.

@ManyToOne,  @OneToOne 관계에서는 fetch = FetchType.LAZY를 명시하지 않아도 LAZY 로딩된 프록시가 생길 수 있다.(특히 Hibernate는 내부적으로 프록시 최적화를 사용한다고 한다)

 

내가 사용하려는 객체  workShift에 대해서, Store store로 선언된 이 스토어 객체는 ManyToOne이라 위와 같은 상황이발생함.

 

`LAZY`란?

Hibernate에서 연관 관계를 불러오는 방식 중 하나

 

예시상황

`@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "store_id") private Store store;`
  • LAZY는 필요할 때만 DB에서 가져온다.
  • 즉, WorkShift를 조회할 때는 store 정보는 아직 가져오지 않고, store 필드에는 프록시 객체가 들어있다.(필요한것만)

프록시 객체 예시: `Store$HibernateProxy$1234` = 실제 Store는 DB 조회 시점에 생성됨

= JSON 직렬화 시점에 Jackson이 이 프록시를 그대로 변환하려고 하면 오류 발생

 

 

  • WorkShift 안에 LAZY 필드: store와 userStore
  • Jackson이 엔티티를 직렬화하려고 접근 → LAZY 프록시(ByteBuddyInterceptor) 잡음 → Exception 발생
  • DTO로 변환 후 반환해야 안전함! -> 그냥 응답해줄 dto를 새로만들었다.
  • 단순 ID만 담아도 WorkShift 엔티티를 ResponseEntity에 그대로 쓰면 LAZY 문제가 발생한다....!

 

결론: 아무튼 엔티티랑 dto랑 분리해서쓰라는 의미인듯.

 

람다식. `::new`의 의미

문법 : `클래스명::메서드명` 

 

코 의미
x -> new WorkShiftDto(x) 람다식
WorkShiftDto::new 생성자 레퍼런스 (람다식 축약형)

 

 

이것에 관해서는 나중에 더 알아보기로하자..

 

그리고 궁금해져서 찾아본 람다식 vs for문 속도차이

https://beatmejy.tistory.com/51

 

JAVA 람다(Lambda)를 사용할 때 장단점

내가 자바를 처음 접했을 때 버전은 11이였다. 이미 람다는 자바 8부터 등장하였었기 때문에 처음부터 람다를 특별하게 생각하지 않고 자연스럽게 사용했었던 것 같다. 하지만 람다는 특별하게

beatmejy.tistory.com

이 블로그를 참고했다.

적은 횟수를 간단하게 반복할 때에는 Stream보다 for문을 사용하는 것이 성능적으로 좋겠지만, 사실 이정도 성능차이에 대해서는 실제로는 거의 미미한 수준이기 때문에 정말 0.1초의 차이도 중요한 서비스가 아니라면 상황에 맞게 사용해도 좋을 것 같다.

적은 시간에 대해서도 거의 10배차이나다보니 나는 그냥 람다식 사용을 지양하는걸로....

그래서 기존에 있던 식들도 다 람다식-> for문으로 줄여줬다.(시간복잡도가 너무 늘어나는것같아서 슬프지만 우짤수없지..)

 

AOP : (Aspect Oriented Programming)

목적 : 프로젝트에서 권한이 필요한게 있는데, 이걸 모든 서비스의 하위 메서드에 권한검증코드를 넣자니 너무 중복이 많은코드라 불편했다. 유지보수성을 올리고,, 모듈화를 위해 이를 적용해보았다.

아래는 디버깅한 결과.

owner권한 정상 처리!

 

staff가 요청했을 때

[OwnerOnlyAspect] userId=9, storeId=1
[OwnerOnlyAspect] requester position=STAFF
2025-11-03T02:12:24.350+09:00  WARN 5280 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.example.unis_rssol.global.exception.ForbiddenException: 해당 매장에 대한 권한이 없습니다.]

 

 

이제 모든 admin 권한이 필요한곳에 @OwnerOnly 어노테이션만 추가해주면 알아서 계정 유효성검사해준다!

 

 

정리: AOP는 내 임의로 접근권한을 설정해주기 위해 !!

-> 반복적 코드인 if문 `if (position != OWNER) throw Forbidden`을 공통로직으로 분리 (solid 중, srp? 라고봐도 되겠다.)

 

대충 사용한 구조는 세개이다.

global.auth
│
├── annotation/
│   └── OwnerOnly.java           // 메서드에 붙이는 커스텀 어노테이션
│
├── aspect/
│   └── OwnerOnlyAspect.java     // AOP 로직: Owner 권한 체크
│
└── AuthorizationService.java    // 활성 매장, 권한 조회

 

기술 설명

AOP = Aspect-Oriented Programming

핵심 로직(Core Concern)과 공통 관심사(Cross-Cutting Concern)를 분리 하기 위해서! ... 권한/로깅/트랜잭션 관리 등에 사용한다고 한다.

`@Aspect`, `@Before`, `@Around` 등의 어노테이션을 사용

 

나는 특정 메서드를(경로) 관리자만 접근 가능하도록 처리했다.

 

지피티가 알려준 핵심개념들

용어 설명 예시
Aspect 공통 관심사를 정의한 모듈 OwnerOnlyAspect
Advice Aspect 안에서 실제 동작하는 코드 checkOwner() 메서드
Join Point Advice가 개입할 수 있는 시점 컨트롤러 메서드 실행 전
Pointcut Advice가 적용될 Join Point를 지정 @annotation(OwnerOnly)
Weaving Advice를 실제 코드에 적용하는 과정 Spring이 런타임에 Aspect 연결

구성요소

1. 어노테이션

    Authrization이랑 묶기위해서 global.auth 패키지 경로를 설정해 만들어줬다.

이런식으로, 내가 사용하고싶은 어노테이션 명 앞에 `@interface` 달아주고,  위에 두가지 어노테이션 달아준다.

나중에  메서드 위에 붙여서 “이 메서드는 OWNER만 접근 가능”을 표시할 것이다.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OwnerOnly {
}

2. Aspect(관점)

 

https://juns-life.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-AOP-%EA%B5%AC%ED%98%84-%EB%B0%8F-%EC%98%88%EC%A0%9C

 

[Spring] 스프링부트 AOP 구현 및 예제

스프링부트에서 AOP 구현은 프록시를 활용하며 @Aspect 어노테이션을 제공하여 구현 편의성을 제공해준다. 스프링부트에서 AOP 를 구현하기 위해선 @Aspect 에 대해 알아야 하며 이와 관련된 용어에

juns-life.tistory.com

해당 블로그를 참고했다.

 

코드흐름

  1. 사용자가 블록 생성 요청
  2. 컨트롤러 메서드 위에 붙은 @OwnerOnly 확인
  3. Spring이 OwnerOnlyAspect의 @Before Advice 실행
  4. AuthService 통해 활성 매장 ID 조회
  5. 해당 스토에서 사용자의 Position 확인
  6. OWNER가 아니면 ForbiddenException 발생 → 컨트롤러 실행 X
  7. OWNER면 컨트롤러 메서드 정상 실행
 

'백엔드 공부(BE, AWS)' 카테고리의 다른 글

[백엔드] API 명세서 배포하기 Swagger 활용  (0) 2026.02.03
[SpringBoot3]JPA - fetch 타입  (0) 2025.09.24
25.07.02 생성시각 삽입  (0) 2025.07.02
게시판 CRUD 인기순 정렬(조회수기반)  (0) 2025.07.02
Article 파일 수정(오류수정)  (0) 2025.06.30
'백엔드 공부(BE, AWS)' 카테고리의 다른 글
  • [백엔드] API 명세서 배포하기 Swagger 활용
  • [SpringBoot3]JPA - fetch 타입
  • 25.07.02 생성시각 삽입
  • 게시판 CRUD 인기순 정렬(조회수기반)
sihyes
sihyes
24학번 컴퓨터공학과
  • sihyes
    시혜적으로개발
    sihyes
  • 글쓰기 관리
  • 전체
    오늘
    어제
    • 분류 전체보기 (104) N
      • 단순 설정 (9)
      • 백엔드 공부(BE, AWS) (8)
        • 로그인&회원가입 (3)
        • 파일업로드&GPT (2)
      • 개인 프로젝트 (2)
        • 알바솔로몬 (1)
        • PLACO 프로젝트 (0)
      • 도서 공부(정리) (20)
        • 알고리즘 코딩 테스트 자바 편 (1)
        • SQL첫걸음 (8)
        • 코딩 자율학습 스프링 부트 3 자바 백엔드 개발 .. (6)
        • Do it! 지옥에서 온 문서 관리자 깃&깃허브 .. (5)
      • 컴퓨터공학과 (51)
        • Python - 문해프 (1)
        • Java 1 & 2 (23)
        • 컴퓨터네트워크 (3)
        • 모앱JavaScript (0)
        • Data structures (9)
        • 소프트웨어공학 (5)
        • 오픈SW플랫폼 제출용 (5)
      • 개인공부정리페이지 (8)
        • 백준 (2)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ㅇ
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.4
sihyes
[Spring] AOP, 직렬화, 람다식
상단으로

티스토리툴바