6 minute read

μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈ, λ’€λŠ¦μ€ ν…ŒμŠ€νŠΈ ν™˜κ²½ ꡬ좕기 (with. TestContainer)

Using TestContainers allows developers to create reliable test environments that mimic production.

더 λΉ λ₯΄κ²Œ 달리기 μœ„ν•΄ 잠깐 λ©ˆμΆ˜λ‹€..

μ•½κ°„ λͺ¨μˆœμ΄ λ˜λŠ” λ¬Έμž₯이기도 ν•œλ°μš”,

이번 ν¬μŠ€νŒ…μ˜ μ£Όμ œκ°€ ν…ŒμŠ€νŠΈ μ½”λ“œλΌλŠ” 것을 μ•Œκ³  λ‹€μ‹œ 제λͺ©μ„ 읽어본닀면

μ–΄λŠμ •λ„λŠ” λ‹€λ“€ 곡감이 갈 λ§Œν•œ 제λͺ©μ΄λΌκ³  μƒκ°ν•©λ‹ˆλ‹€. (πŸ˜… 크흠)

μž‘λ…„ 10μ›”μΈκ°€μš”?

κ·Έλ•ŒλΆ€ν„° μ‹œμž‘ν•œ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈμΈ λŒ€ν”Όλ‘œ λŠ” 2 κ°œμ›” μ•ˆμ— 기획/개발/λ””μžμΈμ΄ μ™„λ£Œλ˜μ–΄

데λͺ¨ μ–΄ν”Œμ„ μ‹œμ—°ν•  수 μžˆμ–΄μ•Ό ν–ˆμ–΄μš”.

쀑간에 기획이 λ³€κ²½λ˜κΈ°λ„ ν•˜κ³ 

μ‹œμ—° μ§μ „κΉŒμ§€λ„ μƒˆλ‘œμš΄ μš”κ΅¬μ‚¬ν•­μ΄ μΆ”κ°€λ˜κ±°λ‚˜ κΈ°μ‘΄ κΈ°λŠ₯ μŠ€νŽ™μ΄ μˆ˜μ •λ˜λŠ” 일이 맀우 λ§Žμ•˜κΈ° λ•Œλ¬Έμ—

사싀상 ν…ŒμŠ€νŠΈ μ½”λ“œμ— ν• μ• ν•  μ‹œκ°„κ³Ό μ—¬λ ₯이 μ—†μ—ˆλ˜ 것 κ°™μ•„μš”β€¦

해컀톀 같은 λŠλ‚ŒμœΌλ‘œ ν•˜λ£¨ ν•˜λ£¨ κ°œλ°œν–ˆλ˜ 것 κ°™μŠ΅λ‹ˆλ‹€.

μ™„λ²½ν•œ DDD (λ°λ“œλΌμΈ 주도 개발) μ΄μ˜€μ£  γ…‹γ…‹γ…‹γ…‹γ…‹γ…‹γ…‹


μž‘λ…„ ν•˜λ°˜κΈ°λ₯Ό λΆˆνƒœμ› λ˜ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈ λŒ€ν”Όλ‘œλŠ”,

올 2μ›”λΆ€ν„° λ‹€μ‹œ λ‹¬λ €λ³΄κΈ°λ‘œ ν–ˆμŠ΅λ‹ˆλ‹€.

와 πŸŽ‰ ~

μž‘λ…„ ν•˜λ°˜κΈ° 2κ°œμ›” κ°„,

μŒ“μ—¬λ²„λ¦° 기술 뢀채λ₯Ό λ¦¬μŠ€νŠΈμ—…ν•˜κ³  ν•˜λ‚˜ν•˜λ‚˜ ν•΄κ²°ν•΄λ‚˜κ°€λŠ” μ€‘μΈλ°μš”

μ΄λ²ˆμ— 이야기할 λ‚΄μš©μ€ ν…ŒμŠ€νŠΈ ν™˜κ²½ κ΅¬μΆ•μž…λ‹ˆλ‹€. (두λ‘₯)


ν…ŒμŠ€νŠΈ ν™˜κ²½..?

κ·Έκ±° κ·Έλƒ₯ ν•˜λ©΄ λ˜λŠ”κ±° μ•„λ‹ˆμ•Ό..?

라고 생각할 수 μžˆμ§€λ§Œ, λŒ€ν”Όλ‘œ λ°±μ—”λ“œμ—μ„œλŠ” 사싀 λ§Žμ€ κ³ λ‡Œμ™€ μ‹œν–‰μ°©μ˜€κ°€ μžˆμ—ˆλŠ”λ°μš”..

μ°¨κ·Όμ°¨κ·Ό μ†Œκ°œν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.. 😭


제λͺ©μ„ 잠깐 λ‹€μ‹œ 보면…

더 λΉ λ₯΄κ²Œ 달리기 μœ„ν•΄ 잠깐 λ©ˆμΆ”κΈ°.

이 λ¬Έμž₯은,

ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ™œ μž‘μ„±ν•΄μ•Ό ν•˜λŠ” 지 κ·Έ 이유λ₯Ό ν•¨μΆ•μ μœΌλ‘œ μ•„μ£Ό 잘 λ‹΄κ³  μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.

μ•žμœΌλ‘œ 기획이 κ³ λ„ν™”λ˜λ©΄μ„œ

κΈ°μ‘΄ κΈ°λŠ₯을 ν™•μž₯μ‹œν‚€κ±°λ‚˜

μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•  일이 λΆ„λͺ… λ§Žμ•„μ§ˆ 것 κ°™μ€λ°μš”,

image

(μžλ™ν™” 된 ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” μ—†λ‹€κ³  κ°€μ •)

A μ˜μ—­μ„ μ—΄μ‹¬νžˆ μˆ˜λ™μœΌλ‘œ ν…ŒμŠ€νŠΈν•΄μ„œ 보μž₯된 κΈ°λŠ₯이 λ˜μ—ˆλ‹€κ³  ν•΄λ΄…μ‹œλ‹€.

그리고 고도화 μž‘μ—…μ„ μ§„ν–‰ν•˜λ©° A’ κΈ°λŠ₯을 ν™•μž₯ν•˜μ˜€μ–΄μš”.

λ¬Όλ‘  A와 A’ 이 κ²ΉμΉ˜μ§€ μ•ŠλŠ” 뢀뢄은 μƒˆλ‘œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•΄μ•Όκ² μ§€λ§Œ,

A 와 A’ 이 κ²ΉμΉ˜λŠ” 뢀뢄도 또 λ‹€μ‹œ μˆ˜λ™μœΌλ‘œ ν…ŒμŠ€νŠΈλ₯Ό ν•΄μ•Όκ² μ£ .

이후에 B λΌλŠ” κΈ°λŠ₯을 μΆ”κ°€ν•˜λ©΄,

μ™„μ „νžˆ μƒˆλ‘œμš΄ κΈ°λŠ₯인 B 뿐만이 μ•„λ‹ˆλΌ A 와 B κ°€ κ²ΉμΉ˜λŠ” ꡬ간도 ν…ŒμŠ€νŠΈν•΄μ•Ό ν•΄μš”.

기쑴에 ν…ŒμŠ€νŠΈλ₯Ό ν–ˆλ˜ μ˜μ—­μ„ 계속 반볡적으둜 ν…ŒμŠ€νŠΈν•˜κ³  μžˆλŠ” λΉ„νš¨μœ¨μ μΈ 상황을 확인할 수 μžˆμ–΄μš”.

μœ„μ™€ 같은 μƒν™©μ—μ„œλŠ” κ²¬κ³ ν•œ μ‹œμŠ€ν…œμ„ κ°œλ°œν•  수 없을 것 κ°™μŠ΅λ‹ˆλ‹€.

μƒˆλ‘œμš΄ μ„œλΉ„μŠ€ 피쳐λ₯Ό κ°œλ°œν•˜λ €λ©΄, ν•΄λ‹Ή 개발 건이 ν˜„μž¬ λ™μž‘ 쀑인 μ–΄λ–€ 뢀뢄에 영ν–₯이 갈 지

μˆ˜λ™μœΌλ‘œ 확인해야 ν•˜κΈ° λ•Œλ¬Έμ— 생산성이 ν˜„μ €νžˆ 떨어지겠죠.

λ§Œμ•½..

잘 μž‘μ„±λœ, μžλ™ν™” 된 ν…ŒμŠ€νŠΈ μ½”λ“œκ°€ μžˆμ—ˆλ”λΌλ©΄

μƒˆλ‘œμš΄ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κ±°λ‚˜, κΈ°μ‘΄ κΈ°λŠ₯을 μ•„μ˜ˆ κ°ˆμ•„μ—Žλ”λΌλ„ μ „ν˜€ 두렀움 없이 κ°œλ°œν•  수 있겠죠.

더 빨리 달리기 μœ„ν•΄, 잠깐 λ©ˆμΆ°μ•Ό ν•œλ‹€λŠ” μ˜λ―ΈλŠ” μ΄κ±°μ—μš”.

우리 μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈμΈ λŒ€ν”Όλ‘œ λŠ”,

μž‘λ…„ 2κ°œμ›” κ°„ 데λͺ¨λ°μ΄ μΆœμ‹œλΌλŠ” 단거리 거리 μ§ˆμ£ΌλŠ” ν•΄λƒˆμ§€λ§Œ

ν…ŒμŠ€νŠΈ ν™˜κ²½μ΄ μ „ν˜€ κ΅¬μΆ•λ˜μ–΄μžˆμ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ—

더 λΉ λ₯΄κ²Œ 였래 달릴 수 μžˆλŠ” μ—¬λ ₯이 λ‚¨μ•„μžˆμ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

κ·Έλž˜μ„œ μ§€κΈˆμ€ 잠깐 λ©ˆμΆ”κ³ , 더 멀리 κ°€κΈ° μœ„ν•œ μ •λΉ„λ₯Ό ν•΄μ•Όν•  λ•ŒλΌκ³  κ²°μ •ν–ˆμ£ .


μ„œλ‘ μ΄ κΈΈμ—ˆλ˜ 것 κ°™λ„€μš”.

μš”μ•½ν•˜μžλ©΄, μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈ λŒ€ν”Όλ‘œ 의 기술 뢀채 쀑 ν•˜λ‚˜μΈ ν…ŒμŠ€νŠΈ ν™˜κ²½ ꡬ좕을 μ§€κΈˆ ν•΄κ²°ν•΄μ•Όλ§Œ ν•œλ‹€.

μ΄κ±΄λ°μš”..!

λŒ€ν”Όλ‘œ λ°±μ—”λ“œμ—μ„œ ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ μ‰½κ²Œ κ΅¬ν˜„ν•˜μ§€ λͺ»ν•˜κ²Œ ν–ˆλ˜ ν—ˆλ“€μ€ 무엇이 μžˆμ—ˆμ„κΉŒμš”?

image

λŒ€ν”Όλ‘œμ—μ„œ μ œκ³΅ν•˜λŠ” 핡심 κΈ°λŠ₯ 쀑 ν•˜λ‚˜λŠ” ν˜„μž¬ μœ„μΉ˜λ₯Ό 기반으둜 κ°€κΉŒμš΄ λŒ€ν”Όμ†Œλ“€μ„ μ‘°νšŒν•˜λŠ” API μž…λ‹ˆλ‹€.

각각의 μœ„μΉ˜λŠ” μœ„κ²½λ„ 정보λ₯Ό 기반으둜 μ‘°νšŒν•˜λ©°,

λŒ€ν”Όμ†Œμ™€ μ‚¬μš©μž κ°„ 거리 λ˜ν•œ μ‹€μ‹œκ°„μœΌλ‘œ 계산이 κ°€λŠ₯ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

μœ„μ™€ 같이 λŒ€ν”Όλ‘œλŠ” 곡간 데이터λ₯Ό 적극적으둜 ν™œμš©ν•΄μ•Ό ν•˜λŠ” κ²½μš°κ°€ λ§Žμ•˜μŠ΅λ‹ˆλ‹€.

곡간 데이터 κ΄€λ ¨ ν’λΆ€ν•œ κΈ°λŠ₯을 μ œκ³΅ν•΄μ£ΌλŠ” mysql 을 메인 λ°μ΄ν„°λ² μ΄μŠ€ μ—”μ§„μœΌλ‘œ μ„ νƒν•œ μ΄μœ λ„ μ΄λ•Œλ¬Έμ΄μ—ˆλŠ”λ°μš”..!

( 좔후에 곡간 인덱슀 κ΄€λ ¨ν•œ 쿼리 νŠœλ‹ ν¬μŠ€νŒ…μ΄ μ˜ˆμ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€ ~ )

보톡 ν…ŒμŠ€νŠΈ ν™˜κ²½μ—μ„œμ˜ λ°μ΄ν„°λ² μ΄μŠ€λŠ” 정말 κ°„νŽΈν•˜κ²Œ ꡬ좕할 수 μžˆλŠ” h2 DB λ₯Ό 많이 μ‚¬μš©ν•˜μ£ .

λ‹€λ§Œ λŒ€ν”Όλ‘œμ—μ„œλŠ” μ§€κΈˆλ„, μ•žμœΌλ‘œλ„ mysql μ—μ„œ μ œκ³΅ν•˜λŠ” 곡간 데이터 νƒ€μž… / ν•¨μˆ˜λ₯Ό 적극적으둜 ν™œμš©ν•˜μ—¬ μ„±λŠ₯ κ°œμ„ μ„ ν•΄λ‚˜κ°ˆ κ³„νšμ΄κΈ° λ•Œλ¬Έμ—

H2 기반의 ν…ŒμŠ€νŠΈ ν™˜κ²½μœΌλ‘œλŠ” μ œμ•½μ΄ λ§Žμ•˜μŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ›ν™œν•˜κ²Œ ν•˜κΈ° μœ„ν•΄,

ν”„λ‘œλ•μ…˜ μ½”λ“œμ—μ„œ mysql 에 의쑴적인 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 방법도 μžˆμ—ˆλŠ”λ°μš”

κ·ΈλŸ¬κΈ°μ—λŠ” mysql μ—μ„œ μ œκ³΅ν•΄μ£ΌλŠ” μœ„μΉ˜ 기반 κΈ°λŠ₯이 κ°•λ ₯ν•˜κΈ° λ•Œλ¬Έμ— 쒋지 μ•Šλ‹€κ³  νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€.

μ•„λ‹ˆλ©΄ ν…ŒμŠ€νŠΈ ν™˜κ²½μ—μ„œ ν•΄λ‹Ή κΈ°λŠ₯듀을 λͺ¨λ‘ mocking 해도 될 ν…λ°μš”,

μ™ΈλΆ€ μ‹œμŠ€ν…œμ΄ μ•„λ‹Œ λŒ€ν”Όλ‘œ μ„œλΉ„μŠ€μ˜ 핡심 κΈ°λŠ₯인데 이λ₯Ό mocking ν•˜λŠ” 게 κ³Όμ—° μ˜¬λ°”λ₯Έ λ°©ν–₯μΌκΉŒμš”?

λ‹Ήμ—°νžˆ.. μ•„λ‹ˆκ² μ£ .

그러면 남은 방법은 단 ν•˜λ‚˜.

ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ h2 κ°€ μ•„λ‹Œ mysql 을 μ‚¬μš©ν•˜λ©΄ λ˜λŠ”κ±°μ£ ! (λ‹Ήμ—°)

자 그럼 문제 μ •μ˜κ°€ 쑰금 λ°”λ€Œμ—ˆλ„€μš”.

ν…ŒμŠ€νŠΈ ν™˜κ²½μ—μ„œ mysql 을 μ–΄λ–»κ²Œ κ΅¬μΆ•ν•˜λ©΄ μ’‹μ„κΉŒμš”?

각자 λ‘œμ»¬μ— mysql 을 demon 으둜 μ‹€ν–‰μ‹œν‚¨ λ’€, ν”„λ‘œλ•μ…˜ db λ₯Ό dump ν•΄μ„œ μ‚¬μš©ν•˜λ„λ‘ ν•˜λ©΄ μ–΄λ–¨κΉŒμš”?

흠…

λ„ˆλ¬΄ 둜컬 ν™˜κ²½μ— 의쑴적이죠.

β€œμŸ€ 컴에선 ν…ŒμŠ€νŠΈκ°€ λ‹€ μ„±κ³΅ν•˜λŠ”λ° μ™œ μ „ μ•ˆλ˜λŠ”κ±°μ£ ?” 

ν”„λ‘œλ•μ…˜ db 와 μŠ€ν‚€λ§ˆλ₯Ό 항상 동기화해주어야 ν•œλ‹€λŠ” κ΄€λ¦¬ν¬μΈνŠΈλ„ ν•˜λ‚˜ λŠ˜μ–΄λ‚©λ‹ˆλ‹€..

ν…ŒμŠ€νŠΈ ν™˜κ²½ ꡬ좕이 맀우 κ°„νŽΈν•˜κ³  νŽΈλ¦¬ν•΄μ•Ό,

더 λ§Žμ€ μ‹œκ°„μ„ ν…ŒμŠ€νŠΈ μ½”λ“œμ— ν• μ• ν•˜λ„λ‘ μœ λ„ν•  수 μžˆλ‹€κ³  μƒκ°ν•˜κΈ° λ•Œλ¬Έμ—

이 방법은 κΈ°κ°ν–ˆμŠ΅λ‹ˆλ‹€.

음..

λ‹€λ₯Έ 쒋은 방법이 μ—†μ„κΉŒμš”?

둜컬 ν™˜κ²½μ—.. 쒅속적이지 μ•Šμ€ ν…ŒμŠ€νŠΈ ν™˜κ²½μ΄λΌ..

μ•„ 이거 μ™„μ „ μ»¨ν…Œμ΄λ„ˆ κ°œλ… μ•„λ‹Œκ°€μš”? (두λ‘₯)

ν…ŒμŠ€νŠΈλ₯Ό run ν•˜λ©΄

ν…ŒμŠ€νŠΈ μš©λ„μ˜ 도컀 μ»¨ν…Œμ΄λ„ˆκ°€ λ„μ›Œμ§€κ³ 

ν…ŒμŠ€νŠΈκ°€ λλ‚˜λ©΄ μ»¨ν…Œμ΄λ„ˆλ₯Ό λ‚΄λ €μ£Όλ©΄ λ˜κ² λ„€μš”!

라고 μƒκ°ν•˜κ³  λ°”λ‘œ κ΅¬κΈ€λ§ν•΄λ³΄μ•˜λŠ”λ°,

μ •ν™•νžˆ μΌμΉ˜ν•˜λŠ” κ°œλ…μ„ κ΅¬ν˜„ν•΄λ†“μ€ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μžˆλ”λΌκ΅¬μš”.. ( μ—†λŠ”κ²Œμ—†λ„€μš”.. )


ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ(TestContainers)

image

ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ΄μš©ν•˜λ©΄, μžλ°” μ½”λ“œλ§ŒμœΌλ‘œ ν…ŒμŠ€νŠΈ 용 도컀 이미지λ₯Ό μ•„μ£Ό μ•„μ£Ό νŽΈλ¦¬ν•˜κ²Œ μ œμ–΄ν•  수 μžˆμ–΄μš”.

이번 ν¬μŠ€νŠΈμ—μ„œ λ‹€λ£¨λŠ” mysql 뿐만이 μ•„λ‹ˆλΌ, mongoDB, Kafka 와 같은 관리 μ‹œμŠ€ν…œλ“€λ„ λͺ¨λ‘ λ‹€λ£° 수 μžˆμœΌλ―€λ‘œ μ‹€μ œ μ„œλΉ„μŠ€ ν™˜κ²½κ³Ό 맀우 μœ μ‚¬ν•˜μ§€λ§Œ 격리된 ν™˜κ²½μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό 진행할 수 있죠.

단점은 둜컬 ν™˜κ²½μ—μ„œ 도컀가 λ™μž‘ν•˜κ³  μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 점과,

ν…ŒμŠ€νŠΈ μ‹œμž‘ν•  λ•Œ μ»¨ν…Œμ΄λ„ˆλ₯Ό λ„μš°λŠ” μ •λ„μ˜ μ†Œμš” μ‹œκ°„μ΄ λŠ˜μ–΄λ‚œλ‹€λŠ” 점이 μžˆμ–΄μš”.

ν•˜μ§€λ§Œ,

λͺ¨λ“  κ°œλ°œμžλ“€μ˜ 둜컬 ν™˜κ²½μ— μ „ν˜€ 쒅속적이지 μ•Šκ³ , 맀우 κ°„λ‹¨ν•˜κ³  νŽΈλ¦¬ν•˜κ²Œ μ‹€μ œ μ„œλΉ„μŠ€μ™€ μœ μ‚¬ν•œ ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ ꡬ좕할 수 μžˆλ‹€λŠ” μ•„μ£Ό 큰 μž₯점이 있기 λ•Œλ¬Έμ— μœ„ 단점듀은 μ–΄λŠμ •λ„ κ°μ•ˆμ΄ κ°€λŠ₯ν•˜λ‹€κ³  μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

λ™μž‘ν•˜λŠ” λͺ¨μŠ΅μ„ λ¨Όμ € λ³ΌκΉŒμš”?

ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ‹€ν–‰μ‹œν‚€λ©΄,

image

미리 μ„€μ •ν•΄λ‘” μ»¨ν…Œμ΄λ„ˆκ°€ μ‹€ν–‰λ˜λŠ” 것을 확인할 수 μžˆμ–΄μš”.

image

그리고 ν•΄λ‹Ή μ»¨ν…Œμ΄λ„ˆ ν™˜κ²½μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό 진행할 수 있죠.

image

mysql μ»¨ν…Œμ΄λ„ˆλ₯Ό 띄웠기 λ•Œλ¬Έμ—, mysql μ—μ„œ μ œκ³΅ν•˜λŠ” μœ„μΉ˜ 데이터 νƒ€μž…μΈ Point 와 μœ„μΉ˜ ν•¨μˆ˜μΈ ST_DISTANCE_SPHERE 을 μ‚¬μš©ν•œ μ„œλΉ„μŠ€ λ‘œμ§μ„ ν…ŒμŠ€νŠΈ ν•  수 있게 λ˜μ—ˆλ„€μš”!

( ST_DISTANCE_SPHERE 을 μ‚¬μš©ν•˜λ©΄ 쿼리가 곡간 인덱슀 R-tree λ₯Ό 타지 λͺ»ν•œλ‹€λŠ” μ΄μŠˆκ°€ μžˆλŠ”λ°, μ΄λŠ” μΆ”ν›„ ν¬μŠ€νŒ…μ—μ„œ 곡간 쿼리 κ°œμ„  주제둜 λ‹€μ‹œ 닀뀄볼 μ˜ˆμ •μ΄μ—μš”!! 사싀 μ „λ°˜μ μœΌλ‘œ λΉ„νš¨μœ¨μ μΈ 쿼리가 λŒ€λΆ€λΆ„μ΄ κΈ° λ•Œλ¬Έμ—, μ „μ²΄μ μœΌλ‘œ 쿼리λ₯Ό κ°ˆμ•„μ—Žμ–΄λ³΄λ €κ³  ν•©λ‹ˆλ‹€.. λŒ€λž΅ 4~5μ›” μ•ˆμ— λͺ¨λ‘ ꡐ체해버리고 μ‹Άλ„€μš”..γ…Žγ…Ž)

ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•„λž˜μ˜ μ˜μ‘΄μ„±μ„ λ°›μ•„μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€.

testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
testImplementation 'org.testcontainers:mysql'

그리고 μ•„λž˜μ™€ 같이 ν•΄μ£Όλ©΄

@Container  
MySQLContainer mySQLContainer = new MySQLContainer("mysql:8");

mysql:8 μ΄λ―Έμ§€λ‘œ 도컀 μ»¨ν…Œμ΄λ„ˆλ₯Ό λ„μ›Œμ£Όκ³ , ν…ŒμŠ€νŠΈκ°€ λλ‚˜λ©΄ μ’…λ£Œμ‹œμΌœμ€λ‹ˆλ‹€.

λ‹€λ§Œ, 맀번 λͺ¨λ“  ν…ŒμŠ€νŠΈμ— λŒ€ν•΄μ„œ μ»¨ν…Œμ΄λ„ˆλ₯Ό λ„μ›Œμ£Όκ³  λ‚΄λ €μ£ΌλŠ” μž‘μ—…μ„ λ°˜λ³΅ν•˜λ‹€ λ³΄λ‹ˆ 전체적인 ν…ŒμŠ€νŠΈ μ½”λ“œ μ‹€ν–‰μ‹œκ°„μ΄ 많이 μ§€μ²΄λ˜κ² μ£ .

이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ»¨ν…Œμ΄λ„ˆλ₯Ό static ν•„λ“œλ‘œ μ„ μ–Έν•˜κ³  μ΄ˆκΈ°ν™”ν•΄μ£Όλ„λ‘ ν•©μ‹œλ‹€.

각 μ»¨ν…Œμ΄λ„ˆλ§ˆλ‹€ ν•„μš”ν•œ ν™˜κ²½ λ³€μˆ˜λŠ” @DynamicPropertySource 을 톡해 λ™μ μœΌλ‘œ μ£Όμž…λ°›μ„ 수 μžˆλŠ”λ°μš”,

λŒ€ν”Όλ‘œμ—μ„œλŠ” μ•„λž˜μ™€ 같이 μ‚¬μš©ν•˜κ³  μžˆμ–΄μš”!

@Slf4j
@SpringBootTest(classes = ServiceIntegrationTestConfiguration.class)
@Import({CoreTestConfiguration.class})
public abstract class ServiceIntegrationTestBase {

    private static final String REDIS_DOCKER_IMAGE = "redis:5.0.3-alpine";
    private static final String MYSQL_DOCKER_IMAGE = "mysql:8.0";
    private static final String MYSQL_ROOT = "root";
    private static final String MYSQL_PASSWORD = "1234";

    private static final int REDIS_PORT = 6379;

    @Container
    protected static MySQLContainer mySQLContainer;

    @Container
    protected static GenericContainer redisContainer;

    @DynamicPropertySource
    static void configureProperties(final DynamicPropertyRegistry registry) {
        // mysql env init
        log.info("""
                    \n
                    βœ… mysql property λ₯Ό μ£Όμž…ν•©λ‹ˆλ‹€.
                """);
        registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl);
        registry.add("spring.datasource.username", () -> MYSQL_ROOT);
        registry.add("spring.datasource.password", () -> MYSQL_PASSWORD);

        // redis env init
        log.info("""
                    \n
                    βœ… redis property λ₯Ό μ£Όμž…ν•©λ‹ˆλ‹€.
                """);
        registry.add("spring.redis.host", redisContainer::getHost);
        registry.add("spring.redis.port", () -> "" + redisContainer.getMappedPort(REDIS_PORT));
    }

    static {
        // mysql test container init
        mySQLContainer = (MySQLContainer) new MySQLContainer(MYSQL_DOCKER_IMAGE)
                .withUsername(MYSQL_ROOT)
                .withPassword(MYSQL_PASSWORD)
                .withDatabaseName("test")
                .withEnv("MYSQL_ROOT_PASSWORD", MYSQL_PASSWORD);

        log.info("""
                \n
                🐳 MYSQL ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
                - base image: {}
                """, MYSQL_DOCKER_IMAGE);
        mySQLContainer.start();

        // redis test container init
        redisContainer = new GenericContainer<>(REDIS_DOCKER_IMAGE)
                .withExposedPorts(REDIS_PORT)
                .withReuse(true);
        log.info("""
                \n
                🐳 Redis ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
                - base image: {}
                """, REDIS_DOCKER_IMAGE);
        redisContainer.start();
    }

}

μ„œλΉ„μŠ€ λ ˆμ΄μ–΄ 톡합 ν…ŒμŠ€νŠΈλŠ” λͺ¨λ‘ μœ„ 클래슀λ₯Ό 상속받도둝 ν•΄μ„œ,

Spring Context λ₯Ό μ€‘λ³΅μœΌλ‘œ λ„μ›Œμ„œ λ°œμƒν•˜λŠ” μ‹œκ°„ 지연을 μ΅œμ†Œν™”ν•˜λ €κ³  ν•©λ‹ˆλ‹€.

μ˜ˆμ‹œλ‘œ μž‘μ„±ν•΄λ‘” ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” μ•„λž˜μ™€ κ°™μ•„μš”!

public class ShelterServiceTest extends ServiceIntegrationTestBase {

    @Autowired
    private ShelterService shelterService;

    @Autowired
    private ShelterRepository shelterRepository;

    @DisplayName("1000 m 이내에 μœ„μΉ˜ν•œ μ£Όλ³€ λŒ€ν”Όμ†Œ 리슀트λ₯Ό μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.")
    @Test
    void getNearestShelterTest() {
        // given
        shelterRepository.save(Shelter.of(ShelterType.CIVIL_DEFENCE, "1000m μ΄λ‚΄μ˜ λŒ€ν”Όμ†Œ λ―Όλ°©μœ„λŒ€ν”Όμ†Œ", 35.504, 35.5));
        shelterRepository.save(Shelter.of(ShelterType.CIVIL_DEFENCE, "1000m 보닀 λ¨Ό λŒ€ν”Όμ†Œ", 36.504, 36.5));

        // when
        NearbyShelterListResponse response = shelterService.getNearbyShelterList(NearbyShelterRequest.of(35.5, 35.5, "λ―Όλ°©μœ„"));

        // then
        Assertions.assertThat(response.getCount()).isEqualTo(1);
    }

}

사싀 μœ„μ— μž‘μ„±λœ κ°„λ‹¨ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œλ§Œ 보고도, ν”„λ‘œλ•μ…˜ μ½”λ“œμ—μ„œ κ°œμ„ ν•΄μ•Ό ν•  점을 ν•˜λ‚˜ μ°Ύμ•„λ‚Ό 수 μžˆμ–΄μš”.

λ¬΄μ—‡μΌκΉŒμš”?

λ°”λ‘œ, 1000m μ΄λ‚΄λΌλŠ” μ‘°κ±΄μΈλ°μš”..!!

μœ„ ν…ŒμŠ€νŠΈμ½”λ“œλ₯Ό 보면 μ§μž‘ν•  수 μžˆκ² μ§€λ§Œ Query λ ˆλ²¨μ—μ„œ where μ ˆμ— 1000 m μ΄ν•˜μΈ λ ˆμ½”λ“œλ§Œ μ‘°νšŒν•˜λ„λ‘ ν•˜κ³  μžˆμ–΄μš”.

μ΄λ³΄λ‹€λŠ”, 1000 μ΄λΌλŠ” 값을 μ™ΈλΆ€μ˜ νŒŒλΌλ―Έν„°λ‘œλΆ€ν„° μ£Όμž…λ˜λ„λ‘ ν•˜λ©΄

쑰금 더 ν…ŒμŠ€νŠΈκ°€ μ‰¬μ›Œμ§€λŠ” μ½”λ“œκ°€ 됨과 λ™μ‹œμ— ν”„λ‘œλ•μ…˜ μ½”λ“œλ„ 더 μœ μ—°ν•΄μ§€μ§€ μ•Šμ„κΉŒμš”?

이와 같이 ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ‹€ 보면,

미처 λ°œκ²¬ν•˜μ§€ λͺ»ν–ˆλ˜ ν”„λ‘œλ•μ…˜ μ½”λ“œ μƒμ˜ 문제점이 λˆˆμ— 보이게 λœλ‹€λŠ” μ•„μ£Ό 쒋은 μž₯점이 μžˆμ–΄μš”..!

ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλŠ” ν™˜κ²½μ„ κ΅¬μ„±ν–ˆμœΌλ‹ˆ,

당뢄간은 ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©°

ν”„λ‘œλ•μ…˜ μ½”λ“œμ˜ λ¬Έμ œμ μ„ μ œλŒ€λ‘œ 진단해보렀고 ν•©λ‹ˆλ‹€.

Leave a comment