Search
Duplicate
🏑

ν…ŒμŠ€νŠΈ ν™˜κ²½ κ°œμ„ κΈ°

생성일
2022/10/23
νƒœκ·Έ
Spring
test

λ°°κ²½

μ €ν¬λŠ” ν”„λ‘œμ νŠΈ μ΄ˆκΈ°λΆ€ν„° ν…ŒμŠ€νŠΈ 속도λ₯Ό 높이기 μœ„ν•΄ 신경을 μΌμŠ΅λ‹ˆλ‹€. λŒ€ν‘œμ μœΌλ‘œ ν…ŒμŠ€νŠΈ 데이터λ₯Ό κ²©λ¦¬ν•˜κΈ° μœ„ν•΄ @DirtiesContext λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ , sql script λ₯Ό μ΄μš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€. λ˜ν•œ μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ λΆˆν•„μš”ν•œ @SpringBootTest λŒ€μ‹  @DataJpaTest 와 @Import 만으둜 μ„œλΉ„μŠ€ 계측 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•  수 μžˆλ„λ‘ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ ν”„λ‘œμ νŠΈκ°€ 점점 컀지고 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ μˆ˜κ°€ 400개λ₯Ό λ„˜μ–΄κ°€λ©΄μ„œ, ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜λŠ” 데에 κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ 점점 였래 걸리게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ†Œμš”λ˜λŠ” μ‹œκ°„μ΄ μ μ§„μ μœΌλ‘œ λŠ˜μ–΄λ‚¬κΈ°λ•Œλ¬Έμ— μΈμ§€ν•˜κΈ° νž˜λ“€μ—ˆλŠ”λ°, ν•œλ²ˆμ€ ν…ŒμŠ€νŠΈλ₯Ό 돌리고 ν™”μž₯싀을 λ‹€λ…€μ™”λŠ”λ°λ„ 전체 ν…ŒμŠ€νŠΈκ°€ 계속 μˆ˜ν–‰μ€‘μ΄μ—ˆλ˜ 것을 보고 β€˜μ΄κ±° λ„ˆλ¬΄ 였래 κ±Έλ¦¬λŠ”λ°β€™λ₯Ό κΉ¨λ‹«κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€..
ν˜„μž¬ κΈ°μ€€μœΌλ‘œ μ°νžˆλŠ” μ‹œκ°„μ€ 51초 정도 λ˜μ§€λ§Œ μ‹€μ œ μΆ”μ‚°λ˜μ§€ μ•Šμ€ μ‹œκ°„κΉŒμ§€ ν•©ν•˜λ©΄ 체감 μ‹œκ°„μ€ 2λΆ„ 정도 κ±Έλ¦¬λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. νŒ€μ›λ“€λ„ λ™μΌν•œ 문제λ₯Ό μΈμ‹ν•˜κ³  μžˆμ—ˆκΈ°λ•Œλ¬Έμ— ν…ŒμŠ€νŠΈ 속도λ₯Ό κ°œμ„ ν•˜κΈ°λ‘œ κ²°μ •ν–ˆμŠ΅λ‹ˆλ‹€.

원인 νŒŒμ•…

λ¨Όμ € μ–΄λ””μ„œ 였래 κ±Έλ¦¬λŠ”μ§€ νŒŒμ•…ν•˜λŠ” 것이 μ€‘μš”ν–ˆμŠ΅λ‹ˆλ‹€. 저희 ν…ŒμŠ€νŠΈλŠ” 크게 총 5가지 인수 ν…ŒμŠ€νŠΈ, 컨트둀러 ν…ŒμŠ€νŠΈ, μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈ, λ ˆν¬μ§€ν† λ¦¬ ν…ŒμŠ€νŠΈ, 도메인 λ‹¨μœ„ ν…ŒμŠ€νŠΈλ‘œ ꡬ뢄할 수 μžˆμŠ΅λ‹ˆλ‹€. 도메인 λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” λ‹¨μˆœνžˆ 컴파일만 되면 μˆ˜ν–‰ν•  수 μžˆμ§€λ§Œ λ‚˜λ¨Έμ§€ 4κ°€μ§€μ˜ ν…ŒμŠ€νŠΈλŠ” μ»¨ν…μŠ€νŠΈ λΉˆμ„ λ„μ›Œμ•Ό ν•©λ‹ˆλ‹€. 이 쀑 인수 ν…ŒμŠ€νŠΈ λŠ” @SpringBootTestλ₯Ό μ‚¬μš©ν•˜κ³ , 컨트둀러 ν…ŒμŠ€νŠΈλŠ” @WebMvcTest, μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈμ™€ λ ˆν¬μ§€ν† λ¦¬ ν…ŒμŠ€νŠΈλŠ” @DataJpaTest λ₯Ό μ‚¬μš©ν•˜μ—¬ μ»¨ν…μŠ€νŠΈ λΉˆλ“€μ„ λ„μ›λ‹ˆλ‹€. 그리고 이 μ»¨ν…μŠ€νŠΈ λΉˆμ„ λ„μš°λŠ” κ³Όμ •μ—μ„œ μ‹œκ°„μ΄ μ†Œμš”λ  것이라고 νŒλ‹¨ν•˜μ˜€μŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ ν…ŒμŠ€νŠΈκ°€ μˆ˜ν–‰λ˜λŠ” 창을 보고 있으면 도메인 λ‹¨μœ„ ν…ŒμŠ€νŠΈ 뢀뢄은 β€˜μ§„μ§œ ν…ŒμŠ€νŠΈλ₯Ό ν•œκ±΄κ°€β€™ 싢을 μ •λ„λ‘œ μˆœμ‹κ°„μ— μ§€λ‚˜κ°€μ§€λ§Œ, λ‚˜λ¨Έμ§€ ν…ŒμŠ€νŠΈλ“€μ€ β€˜μ•„ μ§€κΈˆ 이 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜λŠ” μ€‘μ΄κ΅¬λ‚˜β€™ λΌλŠ” 생각이 λ“€ μ •λ„λ‘œ 버퍼링이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

Application Context Caching

ν…ŒμŠ€νŠΈκ°€ μˆ˜ν–‰λ˜λŠ” κ±Έ λ³΄λŠ”λ° μ΄μƒν•œ 점이 λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€. SpringBoot κ°€ μž¬μ‹€ν–‰λ˜λŠ” λΉˆλ„κ°€ λ„ˆλ¬΄ λ†’μ•˜μŠ΅λ‹ˆλ‹€.
@DirtiesContext λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ€ μ΄μœ κ°€ ν…ŒμŠ€νŠΈ ν™˜κ²½ μ΄ˆκΈ°ν™”(μ •ν™•νžˆλŠ” in-memory DB μ΄ˆκΈ°ν™”)λ₯Ό ν…ŒμŠ€νŠΈλ§ˆλ‹€ 맀번 ν•˜μ§€ μ•Šκ³  Application Context λ₯Ό μž¬ν™œμš©ν•˜κΈ° μœ„ν•΄μ„œμ˜€λŠ”λ°, Application Context λ₯Ό μž¬ν™œμš©ν•˜μ§€ μ•Šκ³  SpringBoot κ°€ 계속 μž¬μ‹€ν–‰λ˜λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€.
μ΄μœ λŠ” Application Context Caching 의 μž¬ν™œμš© 쑰건에 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. Application Context Caching 이 μž¬ν™œμš©λ˜μ§€ μ•Šκ³  μƒˆλ‘œ λ„μš°λŠ” 쑰건은
1.
λ“±λ‘λœ λΉˆλ“€ 쀑 빈의 λ‚΄λΆ€μ—μ„œ μ˜€μ—Όμ΄ λ˜μ—ˆμ„ λ•Œ(빈의 λ‚΄λΆ€ ν•„λ“œκ°€ λ³€κ²½λ˜μ—ˆμ„ λ•Œ)
2.
Context λ‚΄λΆ€ λ“±λ‘λœ 빈의 λ‚΄μš©μ΄ λ‹€λ₯Ό λ•Œ(빈 μΆ”κ°€ λ˜λŠ” 제거)
등이 μžˆλŠ”λ°, 2λ²ˆμ—μ„œ λ¬Έμ œκ°€ λ°œμƒν•˜μ˜€μŠ΅λ‹ˆλ‹€. 2λ²ˆμ€ μ‰½κ²Œ λ§ν•΄μ„œ Context Bean λ“€μ˜ μŠ€λƒ…μƒ·μ„ 뜨고, 이후 ν…ŒμŠ€νŠΈμ—μ„œ Context Bean 의 λ‚΄μš©μ΄ μŠ€λƒ…μƒ·κ³Ό λ‹€λ₯΄λ©΄ Caching 을 μ‚¬μš©ν•˜μ§€ μ•Šκ³  λ‹€μ‹œ μ΄ˆκΈ°ν™”μ‹œν‚€λŠ” κ²ƒμž…λ‹ˆλ‹€. 저희가 μž‘μ„±ν•œ @WebMvcTest 의 λ‚΄μš©μ€ κ°„λ‹¨νžˆ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.
@MockBean(RoleService.class) @WebMvcTest(RoleController.class) class RoleControllerTest { } @MockBean(AppointmentService.class) @WebMvcTest(AppointmentController.class) class AppointmentControllerTest { }
Java
볡사
RoleControllerTest λ₯Ό μˆ˜ν–‰ν•  λ•Œμ—λŠ” @WebMvcTest 와 κ΄€λ ¨λœ 빈, RoleService, RoleController κ°€ Application Context 의 빈으둜 λ“±λ‘λ˜κ³ , AppointmentControllerTest λ₯Ό μˆ˜ν–‰ν•  λ•Œμ—λŠ” @WebMvcTest 와 κ΄€λ ¨λœ 빈, AppointmentService, AppointmentController κ°€ 빈으둜 등둝이 λ©λ‹ˆλ‹€. 이에 따라 μΊμ‹±μ˜ 쑰건을 μœ„λ°°ν•˜κ²Œ 되고, μ»¨ν…μŠ€νŠΈ λΉˆμ„ μƒˆλ‘œ λ„μš°κ²Œ λ˜λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€.
μ΄λŸ¬ν•œ λ¬Έμ œλŠ” 컨트둀러 ν…ŒμŠ€νŠΈ 뿐만 μ•„λ‹ˆλΌ μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈμ—μ„œλ„ λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
@DataJpaTest @Import(PollService.class) @Sql(scripts = {"classpath:schema.sql", "classpath:data.sql"}) class PollServiceTest { private final MemberRepository memberRepository; private final TeamRepository teamRepository; private final TeamMemberRepository teamMemberRepository; private final PollService pollService; @Autowired public PollServiceTest( MemberRepository memberRepository, TeamRepository teamRepository, TeamMemberRepository teamMemberRepository, PollService pollService ) { this.memberRepository = memberRepository; this.teamRepository = teamRepository; this.teamMemberRepository = teamMemberRepository; this.pollService = pollService; } }
Java
볡사
ServiceTest λŠ” μœ„μ™€ 같이 κ΅¬μ„±λ˜μ–΄μžˆκ³ , 각각의 ServiceTest λ₯Ό μˆ˜ν–‰ν•  λ•Œλ§ˆλ‹€ @DataJpaTest 와 κ΄€λ ¨λœ 빈, @Import 둜 λ“±λ‘ν•˜λŠ” 각각의 Service κ°€ 빈으둜 등둝이 λ©λ‹ˆλ‹€. 이에 따라 맀 μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€λ§ˆλ‹€ Application Context λ₯Ό μ΄ˆκΈ°ν™”μ‹œμΌ°μŠ΅λ‹ˆλ‹€.
SpringBoot κ°€ μ–Όλ§ˆλ‚˜ 많이 μ‹€ν–‰λ˜λŠ”μ§€ μ‹€μ œλ‘œ μ„Έμ–΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
β€’
appointment, auth, poll, role, team λ“± 5개 λ„λ©”μΈμ˜ μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈ, 컨트둀러 ν…ŒμŠ€νŠΈ μ—μ„œ 10번
β€’
core 1개 λ„λ©”μΈμ—μ„œ μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈ, λ‘κ°œμ˜ 컨트둀러 ν…ŒμŠ€νŠΈ 총 3번
β€’
notification 1개 λ„λ©”μΈμ—μ„œ 두 개의 μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈ 총 2번
β€’
μΈμˆ˜ν…ŒμŠ€νŠΈ, ApplicationTest λ₯Ό μœ„ν•œ @SpringBootTest μ—μ„œ 총 1번
β€’
λ ˆν¬μ§€ν† λ¦¬ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ @DataJpaTest μ—μ„œ 총 1번
전체 ν…ŒμŠ€νŠΈλ₯Ό ν•œλ²ˆ 돌리면 총 17번 Application Context κ°€ μ΄ˆκΈ°ν™”λ˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€

ν•΄κ²° - λ™μΌν•œ ν™˜κ²½μ„ λ§Œλ“€μž!

같은 μ„±κ²©μ˜ ν…ŒμŠ€νŠΈλ§ˆλ‹€, 같은 Application Context Bean ν™˜κ²½μ„ λ§Œλ“€μ–΄μ£Όλ©΄ Caching 이 λ˜μ–΄ μ΄ˆκΈ°ν™”ν•˜κΈ° μœ„ν•œ SpringBoot μž¬μ‹€ν–‰μ„ ν•˜μ§€ μ•Šμ„ 것이라고 μƒκ°ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

1. λͺ¨λ‘ SpringBootTest 둜 ν•˜μž!

κ°€μž₯ 처음 μƒκ°ν•œ 방법은 λͺ¨λ“  λΉˆλ“€μ„ λ„μ›Œ SpringBoot λ₯Ό ν•œλ²ˆλ§Œ μ‹€ν–‰μ‹œν‚€λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이 방법은 λ‹¨μˆœνžˆ SpringBoot λ₯Ό ν•œλ²ˆλ§Œ μ‹€ν–‰μ‹œν‚€λŠ” 것 뿐이지, 각각의 ν…ŒμŠ€νŠΈ 성격에 λ§žμ§€ μ•ŠλŠ” λ°©λ²•μ΄μ—ˆμŠ΅λ‹ˆλ‹€. 맀번 전체 ν…ŒμŠ€νŠΈλ§Œ λŒλ¦¬μ§„ μ•Šμ„ 것이고, ν…ŒμŠ€νŠΈ 클래슀만 λ‹¨λ…μœΌλ‘œ λŒλ¦¬λŠ” 일도 λ§Žμ€λ° λΆˆν•„μš”ν•œ λΉˆλ“€μ΄ λ“±λ‘λ˜μ–΄ μžˆκΈ°λ•Œλ¬Έμ— 전체적인 ν…ŒμŠ€νŠΈ λ§₯락을 보면 μ–΄μšΈλ¦¬μ§€ μ•ŠλŠ” ν•΄κ²°μ±…μ΄μ—ˆμŠ΅λ‹ˆλ‹€.

2. μœ μ‚¬ν•œ μ„±κ²©μ˜ λΉˆλ“€μ„ ν•œλ²ˆμ— λ“±λ‘ν•˜μž!

μœ μ‹¬νžˆ 보면 ν•΄λ‹Ή λ¬Έμ œλŠ” 컨트둀러 ν…ŒμŠ€νŠΈμ™€ μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈμ—μ„œ λ°œμƒν•˜λŠ” λ¬Έμ œμž…λ‹ˆλ‹€. 컨트둀러 ν…ŒμŠ€νŠΈμ—μ„œλŠ” 각각 λ„λ©”μΈμ˜ μ»¨νŠΈλ‘€λŸ¬μ™€ λͺ¨ν‚Ήμ„ μœ„ν•œ μ„œλΉ„μŠ€ 클래슀만 빈으둜 등둝이 되면 ν…ŒμŠ€νŠΈ μˆ˜ν–‰μ΄ κ°€λŠ₯ν•˜κ³ , μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈλŠ” μ„œλΉ„μŠ€ 클래슀만 빈으둜 등둝이 되면 ν…ŒμŠ€νŠΈ μˆ˜ν–‰μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.
이에 따라 좔상화λ₯Ό ν•œλ‹¨κ³„ 높이고 μΆ”κ°€λ˜λŠ” 클래슀만 ν•œλ²ˆμ— 빈으둜 λ“±λ‘ν•˜λŠ” 방법을 μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.
@WebMvcTest({ GlobalController.class, OAuthController.class, TeamController.class, PollController.class, AppointmentController.class, RoleController.class, NotificationController.class, }) @MockBean({ OAuthService.class, TeamService.class, AppointmentService.class, PollService.class, RoleService.class, WebhookService.class }) public abstract class ControllerTest { }
Java
볡사
λ‹€μŒκ³Ό 같이 상속할 좔상 클래슀λ₯Ό ν•˜λ‚˜ λ§Œλ“€κ³ , ν•΄λ‹Ή ν΄λž˜μŠ€μ—μ„œ μ»¨νŠΈλ‘€λŸ¬μ™€ μ„œλΉ„μŠ€ ν΄λž˜μŠ€λ“€μ„ λͺ¨λ‘ ν•œλ²ˆμ— λ“±λ‘μ‹œμΌ°μŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ μ„Έ κ°€μ§€μ˜ μž₯점을 얻을 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
1.
각각의 λ„λ©”μΈμ—μ„œ 컨트둀러 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•  λ•Œ κ΄€λ ¨λœ λͺ¨λ“  λΉˆλ“€μ΄ λ“±λ‘λ˜μ–΄ μΆ”κ°€, λ³€κ²½λ˜λŠ” λΉˆλ“€μ΄ μ—†κΈ°λ•Œλ¬Έμ— λ™μΌν•œ μ»¨ν…μŠ€νŠΈ 빈 ν™˜κ²½μ„ λ§Œλ“€ 수 μžˆλ‹€.
2.
컨트둀러 ν…ŒμŠ€νŠΈμ—μ„œ κ³΅ν†΅μœΌλ‘œ μˆ˜ν–‰λ˜λ˜ 인증 κ΄€λ ¨ 둜직(JwtProvider)을 λ§Œλ“€μ–΄ μ€‘λ³΅λ˜λŠ” λ©”μ„œλ“œλ„ μ œκ±°ν•  수 μžˆλ‹€.
3.
μƒˆλ‘œμš΄ 도메인에 λŒ€ν•œ 컨트둀러 ν…ŒμŠ€νŠΈλ₯Ό μΆ”κ°€ν•  λ•Œμ—λ„, ν•΄λ‹Ή 컨트둀러λ₯Ό μ–΄λ…Έν…Œμ΄μ…˜μ— μΆ”κ°€λ§Œ ν•˜λ©΄ λ˜μ–΄ ν™•μž₯ μΈ‘λ©΄μ—μ„œ 관리가 μš©μ΄ν•˜λ‹€.
그리고 각각의 컨트둀러 ν…ŒμŠ€νŠΈμ—μ„œ λΆ™μ–΄μžˆλ˜ μ–΄λ…Έν…Œμ΄μ…˜μ„ μ§€μš°κ³  ControllerTest λ₯Ό μƒμ†λ°›μ•˜μŠ΅λ‹ˆλ‹€.
class RoleControllerTest extends ControllerTest { }
Java
볡사
μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈλ„ μœ μ‚¬ν•˜μ§€λ§Œ λ‹€λ₯Έ λ°©μ‹μœΌλ‘œ λ³€κ²½ν•˜μ˜€μŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 각각의 μ„œλΉ„μŠ€μ—μ„œ κ³΅ν†΅λ˜λŠ” 둜직이 μ—†μ—ˆκΈ°λ•Œλ¬Έμ— ꡳ이 클래슀둜 μƒμ†ν•˜μ§€ μ•Šκ³  μ»€μŠ€ν…€ μ–΄λ…Έν…Œμ΄μ…˜μ„ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @DataJpaTest(includeFilters = @Filter(classes = {Service.class, FakeBean.class})) @Sql(scripts = {"classpath:schema.sql", "classpath:data.sql"}) public @interface ServiceTest { }
Java
볡사
includeFilters μ˜΅μ…˜μ„ 톡해 빈으둜 등둝할 클래슀λ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ—μ„œλŠ” μ„œλΉ„μŠ€ μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 클래슀λ₯Ό 빈으둜 λ“±λ‘μ‹œμΌ°μŠ΅λ‹ˆλ‹€. FakeBean 은 Slack λ“±κ³Ό 같은 μ™ΈλΆ€ API λ‘œμ§μ„ μœ„ν•΄ μ‚¬μš©ν•œ μ»€μŠ€ν…€ μ–΄λ…Έν…Œμ΄μ…˜μ΄κ³ , μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈμ‹œ μ‚¬μš©λ˜μ–΄ ν•¨κ»˜ μΆ”κ°€ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
이와 같은 μ–΄λ…Έν…Œμ΄μ…˜ 방식은 μƒˆλ‘œμš΄ 도메인에 λŒ€ν•œ μ„œλΉ„μŠ€κ°€ μΆ”κ°€λ˜μ–΄λ„, ν•΄λ‹Ή μ„œλΉ„μŠ€ 클래슀λ₯Ό λ”°λ‘œ ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€μ—μ„œ μΆ”κ°€ν•  ν•„μš”μ—†μ΄ includeFilters λ₯Ό 톡해 λ“±λ‘λ˜κΈ°λ•Œλ¬Έμ— 컨트둀러 ν…ŒμŠ€νŠΈ 방식보닀 ν™•μž₯에 λ”μš± μš©μ΄ν•˜λ‹€λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.
Application Context ν™˜κ²½μ„ μœ„μ™€ 같이 λ³€κ²½ν•˜κ³  λ‹€μ‹œ SpringBoot μž¬μ‹€ν–‰ 횟수λ₯Ό μ„Έμ–΄λ³΄λ‹ˆ 5번으둜 μ€„μ—ˆμŠ΅λ‹ˆλ‹€. @SpringBootTest, @DataJpaTest, 컨트둀러 ν…ŒμŠ€νŠΈ, μ„œλΉ„μŠ€ ν…ŒμŠ€νŠΈμ—μ„œ ν•œ λ²ˆμ”© 총 4번 μž¬μ‹€ν–‰λ˜μ—ˆκ³ , @ActiveProfiles(”master”) μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 PollSchedulerTest μ—μ„œ μž¬μ‹€ν–‰μ„ ν•œλ²ˆ 더 ν•˜μ˜€μŠ΅λ‹ˆλ‹€. κ΄€λ ¨ 레퍼런슀λ₯Ό 보면 active profile 이 λ‹€λ₯Έ κ²½μš°μ—λ„ μž¬ν™œμš©ν•˜μ§€ μ•Šκ³  μƒˆλ‘œ μ»¨ν…μŠ€νŠΈ λΉˆμ„ λ„μš΄λ‹€κ³  ν•©λ‹ˆλ‹€.
μˆ˜ν–‰ μ‹œκ°„μ΄ 크게 μ€„μ§€λŠ” μ•Šμ•˜μ§€λ§Œ Application Context λ₯Ό μ΄ˆκΈ°ν™”ν•˜κΈ° μœ„ν•΄ 뚝뚝 끊기던 μž‘μ—…μ΄ 17νšŒμ—μ„œ 5회둜 μ€„μ—ˆκ³ , μΆ”μ‚°λ˜μ§€ μ•Šμ€ 체감상 μ‹œκ°„λ„ 많이 μ€„μ—ˆμŠ΅λ‹ˆλ‹€.

μ΄ˆκΈ°ν™”λ˜λŠ” 카운트 μ˜΅μ…˜ μΆ”κ°€

직접 μ„ΈλŠ” 방법 이외에도 springframework 의 μ˜΅μ…˜μ„ μ΄μš©ν•˜μ—¬ μ»¨ν…μŠ€νŠΈ 빈이 μž¬ν™œμš©λ˜λŠ” 수λ₯Ό μΉ΄μš΄νŒ…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
logging: level: org: springframework: test.context.cache: DEBUG
YAML
볡사
springframework 의 ContextCache λŠ” μ•žμ„œ μ΄μš©ν•œ Application Context Caching 을 ν…ŒμŠ€νŠΈμ—μ„œ μ§€μ›ν•΄μ£ΌλŠ” μΈν„°νŽ˜μ΄μŠ€μž…λ‹ˆλ‹€. ν•΄λ‹Ή 클래슀의 λ‘œκΉ… μ˜΅μ…˜μ„ DEBUG둜 μ„€μ •ν•˜λ©΄ logStatistics λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ 톡계λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
[2022-10-22 15:26:51:34733][main] DEBUG o.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@436867c3 size = 5, maxSize = 32, parentContextCount = 0, hitCount = 1384, missCount = 5]
Plain Text
볡사
missCount λ₯Ό ν™•μΈν•˜μ—¬ Context caching 을 ν•˜μ§€ λͺ»ν•˜κ³  μ΄ˆκΈ°ν™”ν•œ 횟수λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

build 방식 λ³€κ²½

μ½”λ“œλ₯Ό μˆ˜μ •ν•˜λŠ” 방법 이외에 build λ₯Ό ν•˜λŠ” λ°©μ‹μ—μ„œλ„ μ†λ„μ˜ 차이가 μžˆλ‹€λŠ” 말을 λ“£κ³  이에 κ΄€ν•΄ μ°Ύμ•„λ³΄μ•˜μŠ΅λ‹ˆλ‹€. build μžλ™ν™” 도ꡬ μ€‘μ—λŠ” gradle 이외에도 IntelliJ 도 μžˆμŠ΅λ‹ˆλ‹€. gradle 은 μ˜€ν”ˆ μ†ŒμŠ€ λΉŒλ“œ μžλ™ν™” 도ꡬ이고 IntelliJ 의 build λŠ” IntelliJ μžμ²΄μ—μ„œ μ œκ³΅ν•˜λŠ” λΉŒλ“œ μžλ™ν™” λ„κ΅¬μž…λ‹ˆλ‹€.
두 λ„κ΅¬λŠ” build λ°©μ‹μ—μ„œ 차이가 μžˆλŠ”λ°, κ°€μž₯ 큰 차이점은 IntelliJ build μ—μ„œ μ œκ³΅ν•˜λŠ” Incremental Build μž…λ‹ˆλ‹€. 이 방식은 μ½”λ“œκ°€ μ΅œμ‹ μ΄λΌκ³  νŒλ‹¨ν•˜λ©΄ κ±΄λ„ˆλ›°κ³ , μΆ”κ°€/λ³€κ²½λœ λΆ€λΆ„ λ“± 좔가적인 뢀뢄에 λŒ€ν•΄μ„œλ§Œ λ‹€μ‹œ build λ₯Ό μ§„ν–‰ν•©λ‹ˆλ‹€. 이둜 인해 build 속도가 λΉ λ₯΄λ‹€λŠ” μž₯점이 μžˆμ§€λ§Œ μ§€μ›ν•˜μ§€ μ•ŠλŠ” μ˜μ‘΄μ„±μ΄ μžˆμ„ μˆ˜λ„ μžˆλ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.
Run tests using 을 Gradle (Default) μ—μ„œ IntelliJ IDEA 둜 λ³€κ²½ν•œ λ’€ 속도λ₯Ό μΈ‘μ •ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
λΉŒλ“œ 방식을 λ³€κ²½ν•˜μ—¬, 42μ΄ˆμ—μ„œ 24초둜 λˆˆμ— λ„λŠ” 속도 λ³€ν™”λ₯Ό 체감할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

주의점

Build and run using κΉŒμ§€ 바꾸지 μ•Šμ€ μ΄μœ λŠ” μ•žμ„œ μ–ΈκΈ‰ν•œ λ‹¨μ λ•Œλ¬Έμž…λ‹ˆλ‹€. 좔가적인 μ½”λ“œ λ³€κ²½ 뢀뢄에 λŒ€ν•΄μ„œλ§Œ λΉŒλ“œλ₯Ό μˆ˜ν–‰ν•˜κ³ , μ§€μ›ν•˜μ§€ μ•ŠλŠ” μ˜μ‘΄μ„±μ΄ μžˆμ„ 수 μžˆλŠ” 단점이 μžˆλŠ”λ°, 저희 ν”„λ‘œμ νŠΈμ—μ„œ 이 상황을 λ§ˆμ£Όν•˜μ˜€μŠ΅λ‹ˆλ‹€.
μ €ν¬λŠ” 쿼리 μ„±λŠ₯ 츑정을 μœ„ν•΄ ν…ŒμŠ€νŠΈμ—μ„œ dummy 데이터λ₯Ό λ„£μ–΄μ£ΌλŠ” μž‘μ—…μ„ ν•˜κ³  있고, 이 μž‘μ—…μ„ μœ„ν•΄ lombok 을 μ‚¬μš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 그리고 test μ—μ„œλ„ lombok 을 μ“Έ 수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•΄ build.gradle νŒŒμΌμ— λ‹€μŒκ³Ό 같이 μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
testCompileOnly 'org.projectlombok:lombok:1.18.12' testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
Groovy
볡사
그리고 Build and run using 을 IntelliJ IDEA 둜 μ„€μ •ν•˜κ³  test λ₯Ό 돌리면 λ‹€μŒκ³Ό 같은 μ—λŸ¬κ°€ λ°œμƒν•©λ‹ˆλ‹€.
lombok μ—μ„œ μ§€μ›ν•˜λŠ” μ»΄νŒŒμΌλŸ¬κ°€ μ•„λ‹ˆλΌλŠ” μ—λŸ¬ λ‚΄μš©μž…λ‹ˆλ‹€. 이에 따라 μ˜μ‘΄μ„±μ— κ΄€ν•œ compile 은 기쑴의 Gradle 에 맑겨두고 test λ₯Ό ν•  λ•Œλ§Œ IntelliJ build 방식을 μ΄μš©ν•  수 μžˆλ„λ‘ μœ„μ™€ 같이 μ„€μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μ°Έκ³