ν›„κΈ°πŸ”₯/ν”„λ‘œμ νŠΈ

Spring Boot λ‘œλ”© μ‹œκ°„ 및 λΉŒλ“œ μ‹œκ°„ λ‹¨μΆ•ν•˜κΈ°

dkswnkk 2025. 4. 16. 19:00

κ°œμš”

ν”„λ‘œμ νŠΈμ˜ λͺ¨λ“ˆ μˆ˜μ™€ μ½”λ“œλŸ‰μ΄ 증가함에 따라 λ‘œμ»¬μ—μ„œ Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ™„μ „νžˆ κ΅¬λ™ν•˜λŠ” 데 μ†Œμš”λ˜λŠ” μ‹œκ°„μ΄ μ§€μ†μ μœΌλ‘œ κΈΈμ–΄μ‘Œλ‹€. 이둜 인해 λ‘œμ»¬μ—μ„œ μ½”λ“œλ₯Ό μˆ˜μ •ν•˜κ³  λ‹€μ‹œ κ΅¬λ™ν•˜μ—¬ ν…ŒμŠ€νŠΈν•˜λŠ” κ³Όμ •μ—μ„œ λΆˆν•„μš”ν•œ μ‹œκ°„μ΄ λŠ˜μ–΄λ‚¬κ³ , λΉŒλ“œ μ‹œκ°„λ„ μ¦κ°€ν•˜λ©΄μ„œ CI μˆ˜ν–‰μ‹œκ°„μ΄ κΈΈμ–΄μ Έ 개발 생산성이 계속 μ €ν•˜λ˜μ—ˆλ‹€. 이λ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‘œλ”© μ‹œκ°„κ³Ό λΉŒλ“œ μ‹œκ°„μ„ 단좕할 수 μžˆλŠ” 방법을 찾아보고 μ μš©ν•΄ λ³΄μ•˜λ‹€.(Spring Boot v3.2.7, Gradle v8.5 μ‚¬μš© 쀑)

1. λΆˆν•„μš”ν•œ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ” λ²”μœ„ μΆ•μ†Œ 및 μ§€μ—° μ΄ˆκΈ°ν™”(lazy initialization) μ„€μ •

   μ μš© 이후 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‘œλ”© μ‹œκ°„ 47% 단좕

  • Mac(M4 Pro): 53초 → 22초
  • Windows(ASUS gu603): 90초 → 48초

2. Gradle 병렬 처리(parallel execution) 및 λΉŒλ“œ μΊμ‹œ(build cache) ν™œμš©

   μ μš© 이후 λΉŒλ“œ μ‹œκ°„ 91% 단좕

  • 전체 λΉŒλ“œ κΈ°μ€€: 3λΆ„ 16초 → 17초

 

 

1. μ§€μ—° μ΄ˆκΈ°ν™”(lazy initialization) μ„€μ •

Spring은 기본적으둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ꡬ동 μ‹œμ μ— μ»¨ν…μŠ€νŠΈμ— λ“±λ‘λœ λͺ¨λ“  싱글톀 λΉˆμ„ μ¦‰μ‹œ μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ‹€μ œ 개발 μ‹œμ μ—μ„œλŠ” λͺ¨λ“  λΉˆμ„ μ‚¬μš©ν•˜λŠ” κ²½μš°λŠ” μ›¬λ§Œν•΄μ„  잘 μ—†λ‹€. λ‹Ήμž₯ λ‚΄κ°€ μ†ν•œ ν”„λ‘œμ νŠΈλ§Œ ν•˜λ”λΌλ„ μ—¬λŸ¬ μŠ€μΏΌλ“œκ°€ λ™μΌν•œ λ ˆν¬μ§€ν† λ¦¬μ—μ„œ λͺ¨λ“ˆλ³„λ‘œ λ‚˜λˆ  μ‚¬μš©ν•˜λŠ”λ° λ‹€λ₯Έ μŠ€μΏΌλ“œμ˜ λͺ¨λ“ˆ 같은 κ²½μš°λŠ” μ‚¬μš©ν•˜μ§€ μ•Šκ³ , λ‚΄κ°€ μ‚¬μš©ν•˜λŠ” λͺ¨λ“ˆμ—μ„œλ„ μ§€κΈˆ κ°œλ°œν•˜λŠ” 둜직 λ§κ³ λŠ” μ‚¬μš©λ˜μ§€ μ•ŠλŠ” λΉˆλ“€μ΄ λŒ€λ‹€μˆ˜λ‹€.

μ΄λŸ¬ν•œ μƒν™©μ—μ„œ spring.main.lazy-initialization=true 섀정을 μ μš©ν•˜λ©΄, λͺ¨λ“  빈의 생성을 ν•œ λ²ˆμ— μ‹€μ œ μ‚¬μš© μ‹œμ κΉŒμ§€ μ§€μ—°μ‹œν‚¬ 수 μžˆλ‹€. 

https://www.baeldung.com/spring-boot-lazy-initialization

μ¦‰μ‹œ μ΄ˆκΈ°ν™” 방식과 달리 μ§€μ—° μ΄ˆκΈ°ν™” λ°©μ‹μ—μ„œλŠ” λΆˆν•„μš”ν•œ λΉˆμ„ μƒμ„±ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‘œλ”© μ‹œ ν•„μš”ν•œ ν΄λž˜μŠ€μ™€ 빈의 μˆ˜κ°€ λŒ€ν­ κ°μ†Œν•˜μ—¬ λ‘œλ”© μ‹œκ°„μ΄ 크게 단좕될 수 μžˆλ‹€.

μ‹€μ œλ‘œ ν”„λ‘œμ νŠΈμ—μ„œ Java Flight Recorder(JFR)λ₯Ό ν™œμš©ν•˜μ—¬ μ§€μ—° μ΄ˆκΈ°ν™” 적용 μ „ν›„λ‘œ λ©”μ„œλ“œ 호좜 νšŸμˆ˜κ°€ μ–Όλ§ˆλ‚˜ μ€„μ–΄λ“€μ—ˆλŠ”μ§€ 비ꡐ해 λ³΄μ•˜λ‹€. 

-XX:StartFlightRecording=duration=60s,filename=boot.jfr,settings=profile

JFR은 VM options에 μœ„ μ˜΅μ…˜μ„ μ£Όκ³  μ‹€ν–‰ν•˜λ©΄ μˆ˜μ§‘μ΄ κ°€λŠ₯ν•˜κ³ , IntelliJλ₯Ό μ‚¬μš© 쀑이라면 λ³„λ„λ‘œ JMCλ₯Ό μ„€μΉ˜ν•˜μ§€ μ•Šκ³ λ„ JFR을 IntelliJμ—μ„œ μ—΄μ–΄λ³Ό 수 μžˆλ‹€.

 

Profiler κ²°κ³Ό

μ§€μ—° λ‘œλ”© 적용 μ „: 5,290회
μ§€μ—° λ‘œλ”© 적용 ν›„: 2,202회

초기 λ‘œλ”© μ‹œ λ©”μ„œλ“œ 호좜 νšŸμˆ˜κ°€ 절반 이상 κ°μ†Œν–ˆλ‹€.

 

적용 μ „: λ‘œλ”© μ‹œκ°„ 53초
적용 ν›„: λ‘œλ”© μ‹œκ°„ 22초

그리고 이에 따라 λ‘œλ”©μ΄ μ™„λ£Œλ˜λŠ” μ‹œκ°„λ„ 절반 이상이 μ€„μ–΄λ“€μ—ˆλ‹€.

 

μ„±λŠ₯이 쒋은데 μ™œ κΈ°λ³Έ 섀정이 μ•„λ‹κΉŒ?

μ§€μ—° μ΄ˆκΈ°ν™”λ₯Ό μ„€μ •ν•˜λ©΄ λΆ€νŠΈ λ‘œλ”© 속도가 λˆˆμ— λ„κ²Œ 빨라진닀. κ·ΈλŸ°λ°λ„ μŠ€ν”„λ§μ˜ κΈ°λ³Έ 섀정은 μ™œ μ¦‰μ‹œ μ΄ˆκΈ°ν™”μΌκΉŒ?

https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2

μ§€μ—° μ΄ˆκΈ°ν™”κ°€ 초기 λ‘œλ”© μ‹œ μ„±λŠ₯적으둜 λ›°μ–΄λ‚œλ°λ„ κΈ°λ³Έ 섀정이 μ•„λ‹Œ μ΄μœ λŠ” μˆœν™˜ μ°Έμ‘°, λ©”λͺ¨λ¦¬ λΆ€μ‘± 였λ₯˜ λ“±μ˜ 문제λ₯Ό μ¦‰μ‹œ λ°œκ²¬ν•˜μ§€ λͺ»ν•˜λŠ” λ“± 잠재적 λ¬Έμ œκ°€ λ°œμƒν•  수 있기 λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ λ‘œμ»¬μ—μ„œ λ„μšΈ λ•Œλ§Œ μ„ νƒμ μœΌλ‘œ μ μš©ν•˜λŠ” 것이 μ•ˆμ „ν•˜κ²Œ μ„±λŠ₯의 이점을 λˆ„λ¦΄ 수 μžˆλ‹€κ³  μƒκ°λœλ‹€.

λ§Œμ•½ λͺ¨λ“  λΉˆμ„ μ§€μ—° μ΄ˆκΈ°ν™”ν•˜λŠ” 것이 λΆ€λ‹΄μŠ€λŸ½κ±°λ‚˜, 배포 ν™˜κ²½μ—μ„œλ„ μ μš©ν•˜κ³  μ‹Άλ‹€λ©΄ @Lazyλ₯Ό μ‚¬μš©ν•΄ μ„ νƒμ μœΌλ‘œ ν…ŒμŠ€νŠΈν•΄ 보고 ν•˜λ‚˜μ”© μ μš©ν•˜λ©΄ 될 것 κ°™λ‹€.

 

 

2. Gradle 병렬 처리(parallel execution) 및  λΉŒλ“œ μΊμ‹œ(build cache)

Gradle의 λΉŒλ“œ μ„±λŠ₯을 κ°œμ„ μ— λŒ€ν•œ μžμ„Έν•œ λ¬Έμ„œλŠ” Gradle Performance λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜λ©΄ λœλ‹€. μ„±λŠ₯κ°œμ„  외에도 λͺ¨λ“  속성에 λŒ€ν•΄ μ•Œκ³  μ‹Άλ‹€λ©΄ Gradle Propertiesλ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μž.

μ„±λŠ₯ κ°œμ„ κ³Ό κ΄€λ ¨λœ μ£Όμš” 속성은 μ•„λž˜ μ„Έ 가지이닀.

1. org.gradle.daemon=true

Gradle λΉŒλ“œλ₯Ό μœ„ν•΄ JVM을 맀번 μƒˆλ‘œ λ„μš°λŠ” λŒ€μ‹  λ°±κ·ΈλΌμš΄λ“œμ—μ„œ 항상 μ‹€ν–‰λ˜λŠ” 데λͺ¬ ν”„λ‘œμ„ΈμŠ€λ₯Ό μž¬μ‚¬μš©ν•˜λŠ” κΈ°λŠ₯이닀. JVM μ΄ˆκΈ°ν™” λΉ„μš©μ„ μ ˆκ°ν•  수 있기 λ•Œλ¬Έμ— λΉŒλ“œ μ‹œμž‘μ΄ 훨씬 빨라진닀. λ‹€λ§Œ Gradle v3.0λΆ€ν„°λŠ” defaultκ°€ true이기 λ•Œλ¬Έμ— λͺ…μ‹œμ μœΌλ‘œ μ„€μ •ν•˜μ§€ μ•Šμ•„λ„ μ•„λ§ˆ λŒ€λΆ€λΆ„μ˜ ν”„λ‘œμ νŠΈμ—μ„œ 기본적으둜 ν™œμ„±ν™”λ˜μ–΄ μžˆμ„ κ°€λŠ₯성이 크닀.

2. org.gradle.caching=true

이전 λΉŒλ“œμ—μ„œ μƒμ„±λœ task κ²°κ³Ό(컴파일 κ²°κ³Ό, λ¦¬μ†ŒμŠ€ 처리 λ“±)λ₯Ό μ €μž₯ν•΄ λ‘μ—ˆλ‹€κ°€, λ‹€μŒ λΉŒλ“œ μ‹œ μž…λ ₯값이 동일할 경우 ν•΄λ‹Ή κ²°κ³Όλ₯Ό μž¬μ‚¬μš©ν•˜λŠ” κΈ°λŠ₯이닀. 특히 λ©€ν‹° λͺ¨λ“ˆκ³Ό 같은 ν”„λ‘œμ νŠΈμ—μ„œ 곡톡 λͺ¨λ“ˆμ΄ 자주 μž¬μ‚¬μš©λ˜λŠ” κ²½μš°μ— 큰 효과λ₯Ό λ³Ό 수 μžˆλ‹€.

참고둜 ./gradlew clean을 μ‹€ν–‰ν•˜λ”λΌλ„ μΊμ‹œλŠ” μ‚­μ œλ˜μ§€ μ•Šκ³  μœ μ§€λœλ‹€. λ”°λΌμ„œ λ³„λ„λ‘œ μΊμ‹œλ₯Ό μ§€μš°μ§€ μ•ŠλŠ” ν•œ clean ν›„ buildλ₯Ό μˆ˜ν–‰ν•˜λ”λΌλ„ μΊμ‹œλŠ” μ μš©λœλ‹€. μΊμ‹œλ₯Ό μ‚­μ œν•˜λ €λ©΄ Gradle의 μΊμ‹œ 디렉터리λ₯Ό 직접 μ‚­μ œν•˜λ©΄ λœλ‹€. (~/.gradle/)

3. org.gradle.parallel=true

μ„œλ‘œ μ˜μ‘΄ν•˜μ§€ μ•ŠλŠ” task듀을 λ™μ‹œμ— λ³‘λ ¬λ‘œ μˆ˜ν–‰ν•˜μ—¬ λΉŒλ“œ 속도λ₯Ό 높인닀. μ΄λ•Œ 'μ˜μ‘΄κ΄€κ³„μ— λ”°λ₯Έ μˆœμ„œλ₯Ό λ”°λ‘œ μ§€μ •ν•΄μ£Όμ–΄μ•Ό ν•˜λ‚˜?' 싢을 μˆ˜λ„ μžˆμ§€λ§Œ 의쑴 관계에 λ”°λ₯Έ μ‹€ν–‰ μˆœμ„œλŠ” λ”°λ‘œ μ •μ˜ν•΄μ£Όμ§€ μ•Šλ”λΌλ„ 보μž₯λœλ‹€. Gradle은 λΉŒλ“œ μ‹€ν–‰ 전에 νƒœμŠ€ν¬ κ°„ 의쑴 관계 κ·Έλž˜ν”„λ₯Ό κ΅¬μ„±ν•˜λŠ”λ° 이 κ·Έλž˜ν”„λ₯Ό 기반으둜 νƒœμŠ€ν¬μ˜ μ‹€ν–‰ μˆœμ„œλ₯Ό μžλ™μœΌλ‘œ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

https://docs.gradle.org/current/userguide/build_lifecycle.html#:~:text=As%20a%20build%20author%2C%20you,order%20dictated%20by%20these%20dependencies

λ”°λΌμ„œ μ˜μ‘΄μ„±μ΄ μžˆλŠ” νƒœμŠ€ν¬λŠ” λ°˜λ“œμ‹œ μ„ ν–‰ νƒœμŠ€ν¬κ°€ μ™„λ£Œλœ ν›„ μ‹€ν–‰λ˜λ©°, μ˜μ‘΄μ„±μ΄ μ—†λŠ” νƒœμŠ€ν¬λŠ” λ³‘λ ¬λ‘œ μ‹€ν–‰λœλ‹€.

μ μš©λ°©λ²•μ€ project root에 gradle.propertiesλ₯Ό λ§Œλ“€κ³  속성을 ν—ˆμš©ν•΄ μ£Όλ©΄ λœλ‹€.

# gradle.properties
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true

 

적용 ν›„ λ©”λͺ¨λ¦¬ λΆ€μ‘± 문제

ν•΄λ‹Ή 섀정듀을 μ μš©ν•œ 이후 λΉŒλ“œ 쀑 λ©”λͺ¨λ¦¬ λΆ€μ‘± λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€. 이 경우 μ•„λž˜ μ˜ˆμ‹œμ²˜λŸΌ 본인 ν™˜κ²½μ— 맞게 JVM νž™ λ©”λͺ¨λ¦¬μ™€ λ©”νƒ€μŠ€νŽ˜μ΄μŠ€ 크기λ₯Ό μ‘°μ •ν•΄μ£Όλ©΄ λœλ‹€.

org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m

 

Profiler κ²°κ³Ό

./gradlew build --scan λͺ…λ Ήμ–΄λ₯Ό 톡해 λΉŒλ“œ 메타데이터λ₯Ό λΆ„μ„ν–ˆλ‹€.

아무 섀정도 ν•˜μ§€ μ•Šμ•˜μ„ λ•Œ

κΈ°λ³Έ μ„€μ •μœΌλ‘œ λΉŒλ“œλ₯Ό μˆ˜ν–‰ν•œ κ²°κ³Ό 총 26개의 ν”„λ‘œμ νŠΈμ—μ„œ 363개의 νƒœμŠ€ν¬κ°€ μ‹€ν–‰λ˜μ—ˆκ³ , 전체 λΉŒλ“œ μ‹œκ°„μ€ 3λΆ„ 16μ΄ˆκ°€ μ†Œμš”λ˜μ—ˆλ‹€.

 

caching만 μ μš©ν–ˆμ„ λ•Œ

org.gradle.caching=true μ˜΅μ…˜μ„ μ μš©ν•œ 경우, λ™μΌν•˜κ²Œ 363개의 νƒœμŠ€ν¬κ°€ μ‹€ν–‰λ˜μ—ˆμ§€λ§Œ 이 쀑 70개의 νƒœμŠ€ν¬κ°€ μΊμ‹œλ‘œ μ²˜λ¦¬λ˜μ—ˆλ‹€. 덕뢄에 μ•½ 1λΆ„ 37초λ₯Ό μ ˆμ•½ν•˜μ—¬ 전체 λΉŒλ“œ μ‹œκ°„μ€ 1λΆ„ 35초둜 λ‹¨μΆ•λ˜μ—ˆλ‹€.

 

caching + parallel μ μš©ν–ˆμ„ λ•Œ

org.gradle.parallel=true μ˜΅μ…˜κΉŒμ§€ μΆ”κ°€ μ μš©ν•œ κ²°κ³Ό 병렬 μ²˜λ¦¬κ°€ 효과적으둜 이뀄진 것을 νƒ€μž„λΌμΈμ—μ„œ 확인할 수 μžˆλ‹€. 전체 λΉŒλ“œ μ‹œκ°„μ€ 단 17초둜 μ„±λŠ₯이 획기적으둜 κ°œμ„ λ˜μ—ˆλ‹€.

μœ„ 속성듀을 μΆ”κ°€ν•¨μœΌλ‘œμ¨ 둜컬뿐만 μ•„λ‹ˆλΌ CIλ‹¨κ³„μ—μ„œλ„ λΉŒλ“œ μ‹œκ°„μ„ λŒ€ν­ 쀄일 수 μžˆμ—ˆλ‹€.

 

 

정리

이번 μ΅œμ ν™” μž‘μ—…μ„ 톡해 Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ‘œλ”© μ‹œκ°„μ€ μ΅œλŒ€ 47%, λΉŒλ“œ μ‹œκ°„μ€ μ΅œλŒ€ 91%κΉŒμ§€ 단좕할 수 μžˆμ—ˆλ‹€. 특히 μ§€μ—° μ΄ˆκΈ°ν™”μ™€ Gradle의 μΊμ‹œ 및 병렬 처리 섀정은 적용 방법이 κ°„λ‹¨ν•˜λ©΄μ„œλ„ λˆˆμ— λ„λŠ” μ„±λŠ₯ ν–₯상을 μ œκ³΅ν•˜κΈ°μ— λ‚˜μ²˜λŸΌ λ‚­λΉ„λ˜λŠ” μ‹œκ°„ μ˜€λ²„ν—€λ“œμ— λŒ€ν•΄ κ°œμ„  고민을 ν•œλ‹€λ©΄ ν•œ 번쯀 μ μš©ν•΄ 보면 쒋을 것 κ°™λ‹€.