[SPRING] 순환참조 오류로 파사드 패턴을 공부해보자 (1)

wannabeing·2025년 4월 11일
2

SPRING

목록 보기
6/12
post-thumbnail
The dependencies of some of the beans in the application context form a cycle:

 commentController defined in file [/Users/choihyuk/spring/sparta-schedule-plus/build/classes/java/main/org/example/spartascheduleplus/controller/comment/CommentController.class]
┌─────┐
|  commentService defined in file [/Users/choihyuk/spring/sparta-schedule-plus/build/classes/java/main/org/example/spartascheduleplus/service/comment/CommentService.class]
↑     ↓
|  scheduleService defined in file [/Users/choihyuk/spring/sparta-schedule-plus/build/classes/java/main/org/example/spartascheduleplus/service/schedule/ScheduleService.class]
└─────┘

위와 같은 에러를 보신 적이 있으신가요..?
ShceduleService → ← CommentService 이런 느낌이랄까??
구현하다 보면 Service Layer 간의 의존성이 너무 강해질 때가 있다.

가장 간단한 방법은 Service에서 각 Repository를 필드에 주입하여 사용하면 되지만...
그 코드가 길어진다면..? 어떡하죠??

두가지 방법을 통해 순환참조 오류를 해결해보자.
1. 파사드 패턴
2. @OneToMany


🔫 파사드(Facade) 패턴

첫번째 방법은 파사드 패턴으로 순환참조 오류를 해결하는 것이다.

파사드(Facade) 패턴은 복잡하게 얽혀있는 서브로직들을 하나로 묶고,
하나의 클래스로 재구축을하여 사용자가 접근하기 편하게 만드는 디자인 패턴이다.

예를들어 우리가 main()을 실행할 때, 우리는 내부로직에 대해서 자세히는 모르지만,
해당 메서드를 통해 프로그램이 실행되는 것을 알 수 있다.

내 프로젝트로 예를 들자면,
1. 댓글을 수정할 때, 일정정보도 조회해야 하고 댓글엔티티 뿐만아니라 일정엔티티에도 업데이트를 해야한다.
2. 일정정보를 조회할 때, 일정에 달린 댓글목록을 조회해서 반환해줘야 한다.

위의 상황에서 서로의 Service Layer를 참조하게되었고, 순환참조 오류가 발생하였다.

나의 경우, 파사드패턴이 추구하는 하위 계층에 복잡도에 비하면 약한 수준이다..ㅎㅎ


Controller → Facade → Service → Repository 구조로 해결해보자!

Controller와 Service 사이에 Facade 패턴을 적용하여
순환참조 오류를 해결해보자.

Facade Layer는 여러 Service를 주입받고,
인증/인가 부분에 대한 예외처리만을 담당한다.

Service Layer는 오로지 Repository와의 상호작용을 통해
DB에서 데이터를 가져오고, 데이터에 대한 예외처리만을 담당한다.

// ✅ 컨트롤러 레이어
@GetMapping
	public ResponseEntity<SuccessResponseDto<PagedScheduleResponseDto>> findAllSchedules(...) {
        // ...
        // ✅ 스케줄 파사드를 호출
		scheduleFacade.findAll(pageable);
	}
// ✅ 파사드 레이어
public PagedScheduleResponseDto findAll(Pageable pageable) {
		// ... ✅ 인증/인가 예외처리
        
		// ✅ 스케줄 레이어 호출
		Page<Schedule> pagedSchedule = scheduleService.findAllSchedules(pageable);

		List<ScheduleResponseDto> schedules = pagedSchedule
			.map(schedule -> {
            	// ✅ 댓글 레이어 호출
				PagedCommentResponseDto comments = commentService.findAllComments(schedule, pageable);
				return new ScheduleResponseDto(schedule, comments);
			}).getContent();

		// ...
	}
// ✅ 서비스 레이어 (스케줄)
public Page<Schedule> findAllSchedules(Pageable pageable) {
	// ✅ 데이터 예외 처리
    
	return scheduleRepository.findAll(pageable);
}

// ✅ 서비스 레이어 (댓글)
public PagedCommentResponseDto findAllComments(Schedule schedule, Pageable pageable) {
		// ... 	✅ 데이터 예외 처리
		return new PagedCommentResponseDto(
			new PageInfo(pagedComment),
			scheduleInfoDto,
			comments
		);
	}

코드가 길어서 부분생략했다.
이처럼 단방향 설정과 Facade 클래스를 중간에 추가해서, 순환참조 오류를 해결할 수 있었다.

📢 피드백

튜터님께서 내가 분리한 방법은 파사드 패턴이 추구하는 기능별로 분리한 것보다,
비즈니스 로직을 분리한거라고 말씀주셨다.
파사드 패턴은 더욱더 복잡하며, 기능을 하나로 묶을 때 쓰는 디자인 패턴이므로,
나의 경우에는 Controller → Business → Service → Repository
비즈니스 계층을 하나 추가하는 방향을 추천해주셨다.

따라서

파사드 패턴은 추후 크고 복잡한 프로젝트에는 사용될만한 디자인 패턴이면서,
가장 단순한 디자인 패턴이라고 한다.

나의 경우에는 @OneToMany 어노테이션을 통해 쉽게 해결할 수 있는 정도의
프로젝트 규모이기에 해당 방법을 추천해주셨다.

다음번엔 순환참조 오류를 @OneToMany어노테이션으로 해결해보는 방법을
작성해보려고한다!


출처

스프링 파사드 패턴
얄코 파사드패턴
스프링 웹계층
파사드패턴으로 서비스 의존성 문제 해결
가볍게 알아보는 디자인 패턴 - 파사드 패턴
야생의 순환참조 오류
내배캠 튜터님들

profile
wannabe---ing

0개의 댓글