๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ฃ ์ด์Šˆ ํ•ด๊ฒฐ ๊ธฐ๋ก

[Spring Data JPA] ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๊ตฌํ˜„์ฒด๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ! (feat. ์ˆœํ™˜ ์ฐธ์กฐ)

by dev_writer 2024. 1. 19.

๋ฌธ์ œ ๋ฐœ์ƒ ์ „

ํ•™๊ต ์„ ๋ฐฐ ๋ถ„๊ป˜ ์ข‹์€ ๊ธฐํšŒ๋กœ ์Šคํ”„๋ง์— ๋Œ€ํ•ด ๋ฉ˜ํ† ๋ง์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด, ๋ฉ˜ํ† ๋ง ๊ณผ์ œ๋กœ ๊ฐ„๋‹จํ•œ ๊ฒŒ์‹œํŒ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณด๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ๋ฐœ์ƒ

๊ฒŒ์‹œ๊ธ€ (Board)์— ๋Œ€ํ•œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋˜ ๋„์ค‘, ์ˆœํ™˜ ์ฐธ์กฐ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
 
์ €๋Š” ๊ฒŒ์‹œ๊ธ€์— ๊ด€๋ จ๋œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๊ตฌํ˜„์ฒด๋“ค์„ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๋‘๊ณ  ์‹ถ์–ด, ์™„์ „ํžˆ ์ถ”์ƒํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค์ธ BoardRepository๋ฅผ ๋งŒ๋“ค๊ณ , ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ธํ„ฐํŽ˜์ด์Šค๋กœ BoardJpaRepository๋ฅผ ๋งŒ๋“ค์—ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ํ™˜๊ฒฝ์—์„œ BoardRepository๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” BoardJpaRepositoryImpl์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
 
์ฆ‰, ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
 

public interface BoardRepository {

    Long save(final Board board);
    ...
}

 

@Repository
@RequiredArgsConstructor
public class BoardJpaRepositoryImpl implements BoardRepository {

    private final BoardJpaRepository boardJpaRepository;

    @Override
    public Long save(final Board board) {
        boardJpaRepository.save(board);
        ...
    }
}

 

public interface BoardJpaRepository extends JpaRepository<Board, Long> {
}

 
์•„๋ž˜๋Š” ๋ฐœ์ƒํ•œ ์—๋Ÿฌ ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.
 

The dependencies of some of the beans in the application context form a cycle:

   boardController defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/controller/BoardController.class]
      โ†“
   boardService defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/service/BoardService.class]
โ”Œโ”€โ”€โ”€โ”€โ”€โ”
|  boardJpaRepositoryImpl defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/infrastructure/BoardJpaRepositoryImpl.class]
โ””โ”€โ”€โ”€โ”€โ”€โ”˜

 

์ฒ˜์Œ ๊ฐ€์ง„ ์˜๋ฌธ์ , ํ•ด๊ฒฐ๋ฒ•

์ฒ˜์Œ์—๋Š” ๊น€์˜ํ•œ ๋‹˜์˜ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๊ฐ•์˜์™€, ์Šคํ”„๋ง ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ดค์„ ๋•Œ ์ œ๊ฐ€ ์ž˜๋ชปํ•˜๊ฑฐ๋‚˜ ๋†“์นœ ๊ฒƒ์ด ์—†๋Š” ์ค„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.
 
๊น€์˜ํ•œ ๋‹˜์˜ ๊ฒฝ์šฐ์—์„œ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ MemberRepositoryCustom์„ ๋งŒ๋“ค๊ณ , ๊ทธ๊ฒƒ์˜ ๊ตฌํ˜„์ฒด๋กœ MemberRepositoryImpl, ๊ทธ๋ฆฌ๊ณ  ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ธํ„ฐํŽ˜์ด์Šค๋กœ MemberRepository (JpaRepository์™€ MemberRepositoryCustom์„ ๋‹ค์ค‘ ์ƒ์†๋ฐ›์Œ)๋กœ ์„ค๊ณ„ํ•ด์„œ ์ด๋ฆ„๋งŒ ๋ดค์„ ๋•Œ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๋Š” ๊ฒƒ์œผ๋กœ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ๊ทธ๋ ‡๊ณ ์š”.
 
๊ทธ๋ž˜์„œ ์—ฌ์ „ํžˆ ์˜๋ฌธ์ด ๋‚จ์•„ ์ง์ ‘ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ด์Šˆ๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.
 

์ง€๊ธˆ ๋ณด๋ฉด ์ง„์งœ ๋‹น์—ฐํžˆ ์•ˆ๋˜๋Š” ๊ฒŒ ๋งž์•˜์Šต๋‹ˆ๋‹ค.

๋ฉ”์ธํ…Œ์ด๋„ˆ ๋ถ„์˜ ๋ฆฌ๋ทฐ

์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฉ”์ธํ…Œ์ด๋„ˆ ๋ถ„๊ป˜์„œ ๋ฆฌ๋ทฐํ•ด ์ฃผ์…จ์Šต๋‹ˆ๋‹ค.

You cannot inject a repository facade into its own repository fragment as this creates a circular dependency. You could make the repository @Lazy or inject an ObjectProvider<BoardJpaRepository> to defer object resolution to a time after the objects are constructed.
์ž์‹ ์˜ ์ €์žฅ์†Œ ์กฐ๊ฐ์— ์ €์žฅ์†Œ ํŒŒ์‚ฌ๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๋ฉด ์›ํ˜• ์ข…์†์„ฑ์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์ €์žฅ์†Œ ํŒŒ์‚ฌ๋“œ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ €์žฅ์†Œ @Lazy๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ObjectProvider<BoardJpaRepository>๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ๊ฐœ์ฒด๋ฅผ ๊ตฌ์„ฑํ•œ ํ›„ ์‹œ๊ฐ„์œผ๋กœ ๊ฐœ์ฒด ํ•ด๊ฒฐ์„ ์—ฐ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 
@Lazy, ObjectProvider ๋“ฑ ์•„์ง์€ ์ €์—๊ฒŒ ์ƒ์†Œํ•œ ๊ฐœ๋…์ด๊ธฐ๋„ ํ•˜๊ณ , ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ๊ทธ์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์„ ๋” ์ฐพ์„ ์ˆ˜ ์—†์–ด ์ถ”๊ฐ€์ ์ธ ์งˆ๋ฌธ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. (๊ทธ๋Ÿฐ๋ฐ ๋ฐ”๋กœ closed ๋œ ์ด์Šˆ..)
 

2์ฐจ (์ตœ์ข…) ํ•ด๊ฒฐ๋ฒ•

EnableJpaRepositories์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๋‹ค

๊ทธ๋Ÿฌ๋˜ ๋„์ค‘, ์ด์™€ ๊ด€๋ จ์ด ์žˆ๋Š” EnableJpaRepositories ์–ด๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊น€์˜ํ•œ ๋‹˜์˜ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์—์„œ๋Š” ์ด ์–ด๋…ธํ…Œ์ด์…˜์—์„œ์˜ ์„ค์ •์„ ๋ฐ”๊ฟˆ์œผ๋กœ์จ ๊ตฌํ˜„์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜๋Š” ๊ฒƒ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
 

๋งˆ์ง€๋ง‰ ๋ฉ”์„œ๋“œ๋ฅผ ๋ด์ฃผ์„ธ์š”!

 
์—ฌ๊ธฐ์—์„œ repositoryImplementationPostfix์— ๋Œ€ํ•ด ํ™•์ธํ•ด ๋ณด์‹œ๋ฉด, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ์ปค์Šคํ…€ ๊ตฌํ˜„์ฒด๊ฐ€ ์ž‘์„ฑ๋  ์‹œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ด๋ฆ„ ๋’ค์— ์ ‘๋ฏธ์‚ฌ๋กœ Impl์ด ๋ถ™์€ ๊ฒƒ์„ ๊ตฌํ˜„์ฒด๋กœ ์ธ์‹ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
์ด๋Š” ๊ณต์‹ ๋ฌธ์„œ์—๋„ ๋‚˜์™€์žˆ๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

 

์ฝ”๋“œ ์žฌํ™•์ธ, ๊นจ๋‹ฌ์Œ ๋ฐ ํ•ด๊ฒฐ๋ฒ•

๊ทธ๋Ÿผ ๋‹ค์‹œ ํ•œ๋ฒˆ ๋ฌธ์ œ ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
 

@Repository
@RequiredArgsConstructor
public class BoardJpaRepositoryImpl implements BoardRepository {

    private final BoardJpaRepository boardJpaRepository;

    @Override
    public Long save(final Board board) {
        boardJpaRepository.save(board);
        ...
    }
}

 
๋ฌธ์ œ์ ์„ ์•„์‹œ๊ฒ ๋‚˜์š”?
 
BoardJpaRepository๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์ถ”๊ฐ€์ ์œผ๋กœ ๊ฑด๋“ค์ง€ ์•Š์„ ๊ฒฝ์šฐ ๊ตฌํ˜„์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด ์ž์‹ ์˜ ์ด๋ฆ„ ๋’ค์— Impl์ด ๋ถ™์€ ๊ฒƒ์„ ๊ตฌํ˜„์ฒด๋กœ ์‚ผ์Šต๋‹ˆ๋‹ค.
 
๊ทธ๋Ÿฐ๋ฐ ๊ทธ๋ ‡๊ฒŒ ๋  ๊ตฌํ˜„์ฒด์˜ ์ด๋ฆ„์ด BoardJpaRepositoryImpl์ธ๋ฐ, ์ด ํด๋ž˜์Šค๋Š” BoardJpaRepository๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๊ตฌํ˜„์ฒด๊ฐ€ ์ž์‹ ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋œ ๊ผด์ž…๋‹ˆ๋‹ค. ์…€ํ”„ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒ๋˜๊ฒŒ ๋œ ๊ฑฐ์ฃ .
 
๊ทธ๋ž˜์„œ ๋กœ๊ทธ ๋˜ํ•œ ์ด๋ ‡๊ฒŒ BoardJpaRepositoryImpl ์•ˆ์—์„œ๋งŒ ๊ณ„์† ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒ๋œ๋‹ค๊ณ  ํ–ˆ๋˜ ๊ฒƒ์œผ๋กœ ์ถ”์ธก๋ฉ๋‹ˆ๋‹ค.
 

โ”Œโ”€โ”€โ”€โ”€โ”€โ”
|  boardJpaRepositoryImpl defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/infrastructure/BoardJpaRepositoryImpl.class]
โ””โ”€โ”€โ”€โ”€โ”€โ”˜

 
๋”ฐ๋ผ์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด (์ €์ฒ˜๋Ÿผ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฃผ์ž…๋ฐ›๋„๋ก ํ•˜์‹ค ๋ถ„๋“ค) ์ด๋ฆ„์„ ๋‹ค๋ฅด๊ฒŒ ํ•˜์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค!


์ •๋ฆฌ

๊ฒฐ๊ตญ์—๋Š” ์ œ๊ฐ€ ์ž˜๋ชป๋œ ๋„ค์ด๋ฐ ์ „๋žต์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ ์ผ์ด์—ˆ์Œ์„ ์•Œ๊ฒŒ ๋˜์–ด, ๋ฉ”์ธํ…Œ์ด๋„ˆ ๋ถ„๊ป˜ ์ฃ„์†กํ•œ ๋งˆ์Œ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
 
ํ•˜์ง€๋งŒ ํ•œํŽธ์œผ๋กœ๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์— ๋Œ€ํ•ด ๋” ์•Œ๊ฒŒ ๋œ ๊ท€์ค‘ํ•œ ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
 
์ปค์Šคํ…€ JPA ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค๋‹ค๊ฐ€ ์ถฉ๋ถ„ํžˆ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ด์Šˆ๋ผ๊ณ  ์ƒ๊ฐ๋˜๊ธฐ๋„ ํ•˜๊ณ , "๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ์•Œ๋ ค์ฃผ๊ณ  ์žˆ์„๊นŒ?", "๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์€ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ์„๊นŒ?"๋ฅผ ์ƒ๊ฐํ•ด ๋ณด๋ฉฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ณด๋ ค๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ํ™œ์šฉํ•˜๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
 
๊ทธ๋ฆฌ๊ณ  ๋œปํ•˜์ง€ ์•Š๊ฒŒ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์ง์ ‘ ์ด์Šˆ๋ฅผ ๋‚จ๊ธฐ๊ฒŒ ๋˜์—ˆ๊ธฐ๋„ ํ•˜์˜€์ฃ .

๊ท€์ค‘ํ•œ ๊ฒฝํ—˜..

 
๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ฑฐ๋‚˜ ๊ณต๋ถ€๋ฅผ ํ•˜๋‹ค๊ฐ€ ๊ถ๊ธˆํ•œ ๊ฒŒ ์žˆ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐํ•˜๊ธฐ ์ „๊นŒ์ง€ ์ž์‹ ์ด ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ฐฉ๋ฒ•์„ ๋™์›ํ•ด์„œ ์‹œ๋„ํ•ด ๋ณด๋Š” ๊ฒŒ ๋น ๋ฅด๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๋Š” ๊ฒƒ์„ ๋‹ค์‹œ๊ธˆ ๊นจ๋‹ฌ์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
 
(๋งˆ์ง€๋ง‰์œผ๋กœ ์˜์–ด์— ๋Œ€ํ•ด ์ง„์งœ ๋„ˆ๋ฌด ์•ฝํ•œ ์ƒํƒœ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณต๋ถ€๋ฅผ ๋œ ํ•˜๊ธด ํ–ˆ์ง€๋งŒ ์˜์–ด๊ถŒ ๋ถ„๋“ค๊ณผ ์˜์‚ฌ์†Œํ†ตํ•  ๋•Œ ์ด ์ •๋„๋Š” ์•„๋‹ˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™์€๋ฐ, ์ด ์ ์— ๋Œ€ํ•ด์„œ๋„ ๋ฐ˜์„ฑํ•˜๊ฒŒ ๋๋„ค์š”.)
 

Reference