์ „์ฒด ๊ธ€ (145)

๋ฐ˜์‘ํ˜•
09
29

 

์ œ๊ฐ€ ๋ณด๋ ค๊ณ  ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณ„์† ๋‚ด์šฉ์ด ์ถ”๊ฐ€๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
์ž˜๋ชป๋œ ๋‚ด์šฉ์žˆ์œผ๋ฉด ์–ธ์ œ๋“ ์ง€ ๋งํ•ด์ฃผ์„ธ์š”!!
์•ˆ๋“œ๋กœ์ด๋“œ 4๋Œ€ ๊ตฌ์„ฑ ์š”์†Œ

์•กํ‹ฐ๋น„ํ‹ฐ, ์„œ๋น„์Šค, ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„(BR), ์ฝ˜ํ…์ธ  ํ”„๋กœ๋ฐ”์ด๋”(CP)

  • ์•กํ‹ฐ๋น„ํ‹ฐ - UIํ™”๋ฉด์„ ๋‹ด๋‹นํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
  • ์„œ๋น„์Šค - ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ• ์ˆ˜์žˆ์Œ
  • ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„ - ์‹œ์Šคํ…œ ๋‹จ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ  ์•ฑ์—์„œ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•จ, ์ •์ (๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋“ฑ๋ก), ๋™์ ์œผ๋กœ ๋‚˜๋‰œ๋‹ค.
  • ์ฝ˜ํ…์ธ  ํ”„๋กœ๋ฐ”์ด๋” - ์•ฑ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ๊ณต์œ ๋ฅผ ๊ด€๋ฆฌ - URI๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ, AAC ViewModel, ์„œ๋น„์Šค์˜ ์ƒ๋ช…์ฃผ๊ธฐ

 

๋‹ค๋ฅธ๊ฑด ๋‹ค ์ž˜ ์•Œ๊ณ ์žˆ๊ณ  ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ๊ฐ€ ์กฐ๊ธˆ ์–ด๋ ต๋‹ค.

  • `Unbound` ์„œ๋น„์Šค: ์„œ๋น„์Šค๊ฐ€ ์‹œ์ž‘๋˜๋ฉด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ”์ธ๋”ฉ๋˜์ง€ ์•Š์•„๋„ ์ž‘์—…์„ ๋งˆ์น˜๊ฑฐ๋‚˜ ์‹œ์Šคํ…œ ๋˜๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์ค‘์ง€ํ•  ๋•Œ๊นŒ์ง€ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค. 
  • `Bound` ์„œ๋น„์Šค: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋น„์Šค์— ๋ฐ”์ธ๋”ฉ๋˜์–ด์•ผ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•˜๋‚˜ ์ด์ƒ์˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ๋Š” ๋™์•ˆ ์„œ๋น„์Šค๊ฐ€ ๊ณ„์† ์‹คํ–‰๋˜๊ณ  ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ”์ธ๋”ฉ์„ ํ•ด์ œํ•˜๋ฉด ์„œ๋น„์Šค๋Š” ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ๋‹ค.

๋‘ ์ƒํƒœ์— ๋”ฐ๋ผ์„œ ์ƒ์„ฑ ์ดํ›„ ๋ฉ”์„œ๋“œ๊ฐ€ `onStartCommand`์ผ์ง€, `onBind(ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—†์œผ๋ฉด onUnbind)`์ผ์ง€ ๊ฐˆ๋ฆฐ๋‹ค.

ViewModel๊ณผ Jetpack์˜ AAC ViewModel์€ ์–ด๋–ค ์ฐจ์ด?

ViewModel

  • ViewModel์€ MVC, MVP, MVVM ๋“ฑ ๋‹ค์–‘ํ•œ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค.
  • ์ฃผ๋กœ UI ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ํ™”๋ฉด์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ํ™”๋ฉด ํšŒ์ „ ๋“ฑ์œผ๋กœ ์ธํ•ด ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋‹ค์‹œ ์ƒ์„ฑ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • ํŠน์ง•์€ ํŠน์ • ํ”Œ๋žซํผ์— ์ข…์†๋˜์ง€ ์•Š๊ณ , UI์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•œ๋‹ค.

Jetpack AAC ViewModel

  • Jetpack์˜ AAC(ViewModel)๋Š” Android Jetpack์˜ Architecture Component์˜ ์ผ๋ถ€๋กœ, Android ์•ฑ์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ์™€ ๊ธด๋ฐ€ํ•˜๊ฒŒ ํ†ตํ•ฉ๋œ ViewModel์ด๋‹ค.
  • Android์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ™”๋ฉด ํšŒ์ „์ด๋‚˜ ๊ตฌ์„ฑ ๋ณ€๊ฒฝ ์‹œ์—๋„ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ์†Œ๋ฉธ๋˜์ง€ ์•Š๋Š” ๋™์•ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํŠน์ง•์€ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ์— ๋งž์ถฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋ฉด์„œ๋„ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.
  • AAC ViewModel์€ ViewModelProviders๋‚˜ Hilt์™€ ๊ฐ™์€ DI ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

๋”ฐ๋ผ์„œ, ViewModel์€ ๋‹ค์–‘ํ•œ ์•„ํ‚คํ…์ฒ˜์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์ด๋ผ๋ฉด, Jetpack AAC ViewModel์€ Android์˜ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ๊ตฌ์„ฑ ๋ณ€๊ฒฝ ์‹œ์—๋„ UI ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ฐ ํŠนํ™”๋œ ๊ตฌํ˜„์ด๋‹ค.

์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ์ฐจ์ด?

๊ฐ ์•กํ‹ฐ๋น„ํ‹ฐ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ณ , ๋‹ค๋ฅธ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ์ „ํ™˜ํ•  ๋•Œ์—๋Š” ์ธํ…ํŠธ๋ฅผ ํ†ตํ•ด์•ผ ํ•œ๋‹ค.

ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ํ•˜๋‚˜์˜ ์•กํ‹ฐ๋น„ํ‹ฐ ์•ˆ์—์„œ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด์„ ์˜๋ฏธํ•œ๋‹ค. ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์—†๊ณ  ํ•ญ์ƒ ์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด์— ํ˜ธ์ŠคํŒ…๋˜์–ด์•ผ ํ•œ๋‹ค.

ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š”, ํ”„๋ž˜๊ทธ๋จผํŠธ ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๊ณ„์ธต ๊ตฌ์กฐ๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • Host Activity (ํ˜ธ์ŠคํŠธ ์•กํ‹ฐ๋น„ํ‹ฐ)
    •  FragmentActivity: ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋‹ค. ์•กํ‹ฐ๋น„ํ‹ฐ๋Š” ์ž์‹ ์˜ supportFragmentManager๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.
    • supportFragmentManager: ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋“ค์„ ๊ด€๋ฆฌํ•œ๋‹ค. ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ดˆ๊ธฐํ™”๋˜๋ฉฐ, ์•กํ‹ฐ๋น„ํ‹ฐ(ํ˜ธ์ŠคํŠธ) ๋‚ด์—์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ์ถ”๊ฐ€, ์ œ๊ฑฐ, ๊ต์ฒด ๋“ฑ ํŠธ๋žœ์žญ์…˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Host Fragment (ํ˜ธ์ŠคํŠธ ํ”„๋ž˜๊ทธ๋จผํŠธ)
    • Fragment: ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ๋˜ ๋‹ค๋ฅธ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ํ˜ธ์ŠคํŠธ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋ฉฐ, ์ž์‹ ์˜parentFragmentManager(ํ˜ธ์ŠคํŠธ์˜ ๋งค๋‹ˆ์ €)์™€ childFragmentManager(ํ˜ธ์ŠคํŠธํ•ด์ค„ ๋งค๋‹ˆ์ €)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
    • parentFragmentManager: ํ˜ธ์ŠคํŠธ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ํฌํ•จ๋œ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ์ƒ์œ„ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ FragmentManager๋‹ค.
    • childFragmentManager: ํ”„๋ž˜๊ทธ๋จผํŠธ ์•ˆ์— ํฌํ•จ๋œ ์ž์‹ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ํ”„๋ž˜๊ทธ๋จผํŠธ ๋‚ด์—์„œ ๋‹ค๋ฅธ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Child Fragment (์ž์‹ ํ”„๋ž˜๊ทธ๋จผํŠธ)
    ์ž์‹ ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ํ˜ธ์ŠคํŠธ ํ”„๋ž˜๊ทธ๋จผํŠธ์— ์†ํ•œ ํ”„๋ž˜๊ทธ๋จผํŠธ๋‹ค. parentFragmentManager๋ฅผ ํ†ตํ•ด ์ƒ์œ„ ํ˜ธ์ŠคํŠธ ํ”„๋ž˜๊ทธ๋จผํŠธ์™€ ์—ฐ๊ฒฐ๋œ๋‹ค.
๊ทธ๋Ÿผ Intent๋Š”?

์ปดํฌ๋„ŒํŠธ(4๋Œ€ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ ํฌํ•จ) ๊ฐ„์— ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์š”์ฒญํ•˜๋Š” ๋ฉ”์‹œ์ง€ ๊ฐ์ฒด์ด ๋ช…์‹œ์ , ์•”์‹œ์  ์ธํ…ํŠธ๋กœ ๋‚˜๋‰œ๋‹ค.
๋ช…์‹œ์  Intent๋Š” ๋ช…ํ™•ํ•˜๊ฒŒ ์‹œ์ž‘ํ•  ์ปดํฌ๋„ŒํŠธ๋‚˜ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ,
์•”์‹œ์  Intent๋Š” ํŠน์ • ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์•„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.(urlScheme๊ณผ ๊ฐ™์€)

์•”์‹œ์  Intent๋Š” Intent filter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

 

> `PendingIntent`๋„ ์žˆ๋‹ค.
Intent๋ฅผ ๊ฐ–๋Š” ํด๋ž˜์Šค๋กœ, ๊ธฐ๋ณธ ๋ชฉ์ ์€ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜)์˜ ๊ถŒํ•œ์„ ํ—ˆ๊ฐ€ํ•˜์—ฌ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Intent๋ฅผ ๋งˆ์น˜ ๋ณธ์ธ ์•ฑ์˜ ํ”„๋กœ์„ธ์Šค์—์„œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. Notification ์„ค์ •ํ•  ๋•Œ ๋ดค์„ ๊ฒƒ์ด๋‹ค.
Notification์„ ํด๋ฆญํ•ด์„œ ๊ทธ ์•ฑ์œผ๋กœ ์ด๋™ํ•˜๋Š” ๊ฑด, ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์—์„œ PendingIntent๋ฅผ ์‚ฌ์šฉํ•ด์„œ NotificationManager๋กœ ํ•˜์—ฌ๊ธˆ ์•ฑ์„ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค.

์„œ๋น„์Šค๋Š”?
  • ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค - ์•Œ๋ฆผ์„ ํ‘œ์‹œํ•ด ๋†“๊ณ  ์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜์ง€ ์•Š์•„๋„ ๊ณ„์† ์‹คํ–‰๋˜๋Š” ๊ฑธ ๋งํ•จ(์Œ์•… ํ”Œ๋ ˆ์ด์–ด ๊ฐ™์€)
  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค - ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์•Œ์ง€ ๋ชปํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ
  • ๋ฐ”์šด๋“œ ์„œ๋น„์Šค - ์•ฑ ๋‚ด์—์„œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ํด๋ผ์ด์–ธํŠธ - ์„œ๋ฒ„ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋งํ•จ(ํŠน์ • ์ปดํฌ๋„ŒํŠธ์™€ ์„œ๋น„์Šค๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ) -> onBind, onUnbind๊ฐ€ ํ˜ธ์ถœ๋˜๋Š”

์˜ต์…˜ 3๊ฐœ๊ฐ€ ์žˆ๋‹ค.

  • `START_STICKY` - ์•ˆ๋“œ๋กœ์ด๋“œ๊ฐ€ ์„œ๋น„์Šค๋ฅผ ๊ฐ•์ œ ์ค‘์ง€ํ•œ ๊ฒฝ์šฐ, null intent๋ฅผ ๋ณด๋‚ด์„œ ์žฌ์‹œ์ž‘ํ•œ๋‹ค.
  • `START_NOT_STICKY` - ์•ˆ๋“œ๋กœ์ด๋“œ๊ฐ€ ์„œ๋น„์Šค๋ฅผ ๊ฐ•์ œ ์ค‘์ง€ํ•œ ๊ฒฝ์šฐ, ์žฌ์‹œ์ž‘ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • `START_REDELIVER_INTENT` - ์•ˆ๋“œ๋กœ์ด๋“œ๊ฐ€ ์„œ๋น„์Šค๋ฅผ ๊ฐ•์ œ ์ค‘์ง€ํ•œ ๊ฒฝ์šฐ, ์ด์ „๊ณผ ๋™์ผํ•œ intent๋ฅผ ๋ณด๋‚ด์„œ ์žฌ์‹œ์ž‘ํ•œ๋‹ค.
๋””์ž์ธ ํŒจํ„ด? - MVC, MVP, MVVM

๋””์ž์ธ ํŒจํ„ด์€ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์„ ํšจ์œจ์ ์œผ๋กœ ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…๋œ, ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์„ค๊ณ„๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

  • MVC - ๋ชจ๋ธ - ๋ทฐ - ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋ชจ๋ธ, UI๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋ทฐ, ์ด๊ฑธ ์—ฐ๊ฒฐํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
  • MVP - ๋ทฐ์™€ ๋ชจ๋ธ ์‚ฌ์ด์— ํ”„๋ ˆ์  ํ„ฐ๋ฅผ ๋‘ฌ์„œ M-V ๊ฐ„์˜ ์ง์ ‘ ์—ฐ๊ฒฐ์„ ๋Š๊ณ , ๋ทฐ์˜ ์ด๋ฒคํŠธ๋ฅผ ํ•ธ๋“ค๋งํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. MVC์˜ ์ง„ํ™”
  • MVVM - ๋ทฐ์™€ ๋ชจ๋ธ ์‚ฌ์ด์— ๋ทฐ ๋ชจ๋ธ์ด ์žˆ๋Š” ๊ตฌ์กฐ๋กœ, ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ๋ทฐ์™€ ๋ทฐ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๋ฐฉ์‹. ๋ทฐ ๋ชจ๋ธ์ด ์ค‘๊ฐ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋‹ด๋‹นํ•˜๊ณ  ๋ทฐ๋Š” ์ด๋ฒคํŠธ๋งŒ ๋ฐ›๋Š” ๋ฐฉ์‹์„ ์ ์šฉํ•˜์—ฌ, ๋ทฐ์™€ ๋ชจ๋ธ ์‚ฌ์ด์˜ ์˜์กด์„ฑ์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ๋‹ค.
๋ ˆ๊ฑฐ์‹œ ๋Œ€๋น„ - AsyncTask๊ฐ€ ๋ฌด์—‡์ธ์ง€? ์‚ฌ์šฉ์‹œ ๋‹จ์ ์€?

AsyncTask๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ UI ์Šค๋ ˆ๋“œ์—์„œ ์‰ฝ๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ํด๋ž˜์Šค์ด๋‹ค.
Android 11(API ๋ ˆ๋ฒจ 30)์—์„œ Deprecated๋˜์–ด, ์ฝ”๋ฃจํ‹ด, WorkManager, Handler๊ณผ ๊ฐ™์€ ๋Œ€์•ˆ์„ ์‚ฌ์šฉํ•œ๋‹ค.

๋‹จ์ ์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ (Memory Leaks):
    AsyncTask๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜์—ˆ๋Š”๋ฐ, ๋งŒ์•ฝ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์ „ํ™˜๋˜๊ฑฐ๋‚˜ ์†Œ๋ฉธ๋˜๋Š” ๋™์•ˆ AsyncTask๊ฐ€ ๊ณ„์† ์‹คํ–‰ ์ค‘์ผ ๊ฒฝ์šฐ, ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(GC)์— ์˜ํ•ด ์ˆ˜์ง‘๋˜์ง€ ์•Š์•„ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ทจ์†Œ ๋ฐ ์ค‘๋‹จ์˜ ์–ด๋ ค์›€:
    AsyncTask๋Š” cancel() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ž‘์—…์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ž‘์—…์ด ์ด๋ฏธ ์ง„ํ–‰ ์ค‘์ธ ๊ฒฝ์šฐ, ์ž‘์—…์ด ์ค‘๋‹จ๋˜์ง€ ์•Š๊ณ  ๊ณ„์† ์ง„ํ–‰๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค.
  • ์ž‘์—… ๊ด€๋ฆฌ์˜ ์–ด๋ ค์›€:
    ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์„ ์ ์ ˆํžˆ ์ทจ์†Œํ•˜๋Š” ๊ฒƒ์€ ์œ„์—์„œ ๋งํ–ˆ๋“ฏ AsyncTask์—์„œ ์ง์ ‘์ ์œผ๋กœ ์ง€์›๋˜์ง€ ์•Š์•„์„œ, ์—ฌ๋Ÿฌ AsyncTask๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๊ฑฐ๋‚˜ ์—ฐ์†์ ์œผ๋กœ ์‹คํ–‰๋  ๋•Œ๋Š” ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ต๋‹ค.
  • ThreadPool ์ œํ•œ:
    AsyncTask๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์Šค๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘๊ธฐ ๋•Œ๋ฌธ์—, ๋งŽ์€ AsyncTask๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•  ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œ ํ’€์˜ ๋Œ€๊ธฐ์—ด์— ์Œ“์—ฌ ์ž‘์—…์ด ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
  • ์ž‘์—… ์ˆœ์„œ ๋ถˆํ™•์‹ค์„ฑ:
    AsyncTask๋Š” Android 3.0(Honeycomb) ์ดํ›„๋กœ execute()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง๋ ฌ ์‹คํ–‰๋˜์—ˆ์ง€๋งŒ, ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” ๋ณ‘๋ ฌ ์‹คํ–‰์ด ๊ธฐ๋ณธ๊ฐ’์ด์—ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋™์ž‘์ด ๋ฒ„์ „์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์กŒ๊ณ , ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ executeOnExecutor()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ณ‘๋ ฌ ์‹คํ–‰์„ ๋ช…์‹œํ•ด์•ผ ํ–ˆ๋‹ค. ์•ˆ์ •์„ฑ์ด ๋–จ์–ด์ง„๋‹ค๋Š” ๋ง.

๋Œ€์•ˆ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” WorkManager๋Š” JetPack์— ํฌํ•จ๋œ Android์˜ ์ƒˆ๋กœ์šด ์ž‘์—… ์Šค์ผ€์ค„๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ํŠนํžˆ, ๊ธด ์‹œ๊ฐ„ ์‹คํ–‰๋˜๊ฑฐ๋‚˜ ๋„คํŠธ์›Œํฌ์— ์˜์กดํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค. ์ž‘์—…์ด ์ทจ์†Œ๋˜์—ˆ๊ฑฐ๋‚˜ ์žฅ์น˜๊ฐ€ ์žฌ๋ถ€ํŒ…๋œ ๊ฒฝ์šฐ์—๋„ ์ž‘์—…์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•ด๋ดค๋Š”์ง€? ์™œ ์ผ๋Š”์ง€?

์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection, DI)์€ ๊ฐ์ฒด ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹์œผ๋กœ, ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ, ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ, ๊ฐ€๋…์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ค.

์ €๋Š” Hilt๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ,  Hilt๋Š” Google์ด ๊ด€๋ฆฌํ•˜๋Š” Android ํŠนํ™” DI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋ณต์žกํ•œ ์„ค์ • ์—†์ด DI๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.Dagger์— ๋น„ํ•ด ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋ฅผ ๋Œ€ํญ ์ค„์—ฌ์ฃผ๊ณ , ๋ช…์‹œ์ ์ธ ์ปดํฌ๋„ŒํŠธ ์„ค์ • ์—†์ด๋„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

  Hilt Dagger
์„ค์ • ๋ณต์žก๋„ ๊ฐ„๋‹จํ•จ, ์ž๋™ ์ปดํฌ๋„ŒํŠธ ์„ค์ • ๋ณต์žกํ•จ, ์ˆ˜๋™ ์ปดํฌ๋„ŒํŠธ ์„ค์ •
๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ์ ์Œ ๋งŽ์Œ
Android ํŠนํ™” Android์— ์ตœ์ ํ™”๋จ Android ํŠนํ™” ์•„๋‹˜, Dagger-Android ํ•„์š”
์ƒ๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ Android ์ƒ๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ ์ž๋™ํ™” ์ƒ๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ ์ˆ˜๋™ ์„ค์ • ํ•„์š”
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ Hilt ํ…Œ์ŠคํŠธ ๋ชจ๋“ˆ ์ œ๊ณต, ๊ฐ„ํŽธํ•œ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ํ…Œ์ŠคํŠธ์šฉ ์ปดํฌ๋„ŒํŠธ/๋ชจ๋“ˆ์„ ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•จ
์‚ฌ์šฉ์„ฑ Android ํ”„๋กœ์ ํŠธ์— ์ตœ์ ํ™” ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๋นŒ๋“œ ์†๋„ ์ƒ๋Œ€์ ์œผ๋กœ ๋น ๋ฆ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณต์žกํ•  ๊ฒฝ์šฐ ๋นŒ๋“œ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Œ
Jetpack Navigation์—์„œ popUpTo์™€ popUpToInclusive ์ฐจ์ด

popUpTo

  • popUpTo๋Š” ์—ญ๋ฐฉํ–ฅ ํƒ์ƒ‰ ์‹œ ๋ฐฑ ์Šคํƒ์—์„œ ํŠน์ • ๋ชฉ์ ์ง€๊นŒ์ง€์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์†์„ฑ์ด๋‹ค.
  • ์ด ์†์„ฑ์€ ํƒ์ƒ‰ํ•  ๋•Œ ํŠน์ • ๋ชฉ์ ์ง€ ์ดํ›„์— ์žˆ๋Š” ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๋ฐฑ ์Šคํƒ์—์„œ ์ œ๊ฑฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, popUpTo="@+id/destination_home"๋กœ ์„ค์ •ํ•˜๋ฉด destination_home ๋ชฉ์ ์ง€๊นŒ์ง€ ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ํƒ์ƒ‰ํ•˜๋ฉฐ, destination_home ์ดํ›„์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค.
  • destination_home ์ž์ฒด๋Š” ๋ฐฑ ์Šคํƒ์— ๋‚จ์•„ ์žˆ์–ด, ์—ฌ์ „ํžˆ ๊ทธ ํ”„๋ž˜๊ทธ๋จผํŠธ๋กœ ๋Œ์•„๊ฐ€๊ฒŒ ๋œ๋‹ค.

popUpToInclusive

  • `popUpToInclusive`๋Š” `popUpTo`์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ง€์ •๋œ ๋ชฉ์ ์ง€ ์ž์ฒด๋ฅผ ๋ฐฑ ์Šคํƒ์—์„œ ์ œ๊ฑฐํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์†์„ฑ์ด๋‹ค.
  • ๊ธฐ๋ณธ๊ฐ’์€ false๋กœ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด๋•Œ๋Š” popUpTo๋กœ ์ง€์ •๋œ ๋ชฉ์ ์ง€ ์ž์ฒด๋Š” ๋ฐฑ ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋˜์ง€ ์•Š๊ณ  ๋‚จ์•„ ์žˆ๋‹ค.
  • `popUpToInclusive="true"`๋กœ ์„ค์ •ํ•˜๋ฉด, ์ง€์ •๋œ ๋ชฉ์ ์ง€๋„ ํฌํ•จํ•˜์—ฌ ๋ฐฑ ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, `popUpTo="@+id/destination_home" popUpToInclusive="true"`๋กœ ์„ค์ •ํ•˜๋ฉด, `destination_home`๊ณผ ๊ทธ ์ดํ›„์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค.

๋”ฐ๋ผ์„œ, `popUpTo`๋Š” ํŠน์ • ๋ชฉ์ ์ง€ ์ดํ›„์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , `popUpToInclusive`๋Š” ๊ทธ ์ง€์ •๋œ ๋ชฉ์ ์ง€ ์ž์ฒด๋„ ์ œ๊ฑฐํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์†์„ฑ์ด๋‹ค.

 

์ข€ ๋” ๋ถ€์—ฐ์„ค๋ช…ํ•˜๋ฉด,

 

  • ๋„ค๋น„๊ฒŒ์ด์…˜ ๊ทธ๋ž˜ํ”„๊ฐ€ A -> B -> C -> D ๋ผ๊ณ  ๊ฐ€์ •ํ•˜์ž.
  • ์—ฌ๊ธฐ์„œ D์—์„œ A๋กœ ๋Œ์•„๊ฐ€๋ฉด์„œ B์™€ C๋ฅผ ๋ฐฑ์Šคํƒ์—์„œ ์ œ๊ฑฐํ•˜๋ ค๋ฉด popUpTo="B"์™€ ํ•จ๊ป˜ popUpToInclusive=true๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿผ ๋ชฉ์ ์ง€์ธ B๋กœ ๊ฐˆ ๋•Œ, B๋„ ๋‚ ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ์ „์˜ backstack์ธ A๋งŒ ๋‚จ๊ณ  B, C, D๊ฐ€ ๋ฐฑ์Šคํƒ์—์„œ ๋ชจ๋‘ ์ œ๊ฑฐ๋œ๋‹ค.

 

  popUpTo popUpToInclusive
๊ธฐ๋Šฅ ๋ฐฑ ์Šคํƒ์—์„œ ํŠน์ • ๋ชฉ์ ์ง€๊นŒ์ง€์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ œ๊ฑฐํ•˜์ง€๋งŒ, ์ง€์ •๋œ ๋ชฉ์ ์ง€ ์ž์ฒด๋Š” ๋‚จ๊ฒจ๋‘  ๋ฐฑ ์Šคํƒ์—์„œ ํŠน์ • ๋ชฉ์ ์ง€์™€ ๊ทธ ์ดํ›„์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉฐ, ์ง€์ •๋œ ๋ชฉ์ ์ง€ ์ž์ฒด๋„ ์ œ๊ฑฐํ•จ
๋™์ž‘ ๋ฐฉ์‹ ์ง€์ •๋œ ๋ชฉ์ ์ง€๊นŒ์ง€์˜ ๊ฒฝ๋กœ์—์„œ ๊ทธ ๋ชฉ์ ์ง€ ์ดํ›„์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋งŒ ์ œ๊ฑฐํ•จ ์ง€์ •๋œ ๋ชฉ์ ์ง€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ทธ ์ด์ „์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํ•จ๊ป˜ ์ œ๊ฑฐํ•จ
์‚ฌ์šฉ ์˜ˆ์‹œ popUpTo="@+id/home": home ๋ชฉ์ ์ง€ ์ดํ›„์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  home์€ ๋‚จ๊ฒจ๋‘  popUpTo="@+id/home" popUpToInclusive="true": home๊ณผ ๊ทธ ์ดํ›„์˜ ๋ชจ๋“  ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํ•จ๊ป˜ ์ œ๊ฑฐํ•จ
์‚ฌ์šฉ ๋ชฉ์  ํŠน์ • ๋ชฉ์ ์ง€ ์ดํ›„์˜ ๋ฐฑ ์Šคํƒ์„ ์ •๋ฆฌํ•˜๊ณ , ์ง€์ •๋œ ๋ชฉ์ ์ง€๋กœ ๋Œ์•„๊ฐ€๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ ํŠน์ • ๋ชฉ์ ์ง€ ์ž์ฒด๋„ ์ œ๊ฑฐํ•˜์—ฌ, ๊ทธ ์ด์ „์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋กœ ๋Œ์•„๊ฐ€๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
Dispatchers์˜ ์ข…๋ฅ˜์™€ ํŠน์ง•?

 

  • Dispatchers.Main: UI ๊ด€๋ จ ์ž‘์—… (๋ฉ”์ธ ์Šค๋ ˆ๋“œ).
  • Dispatchers.IO: ๋„คํŠธ์›Œํฌ, ํŒŒ์ผ ์ž…์ถœ๋ ฅ ์ž‘์—… (I/O ๋ฐ”์šด๋“œ ์ž‘์—…).
  • Dispatchers.Default: CPU ์ง‘์•ฝ์ ์ธ ์ž‘์—… (CPU ๋ฐ”์šด๋“œ ์ž‘์—…).
  • Dispatchers.Unconfined: ํŠน์ • ์Šค๋ ˆ๋“œ์— ์–ฝ๋งค์ด์ง€ ์•Š์œผ๋ฉฐ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ์‹คํ–‰ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ฌ๋ผ์ง.

 

๋™๊ธฐ ๋น„๋™๊ธฐ, Blocking Non-Blocking

๋™๊ธฐ์™€ ๋น„๋™๊ธฐ๋Š” ์ž‘์—…์˜ ์ˆœ์ฐจ์„ฑ๊ณผ ๋Œ€๊ธฐ ์ƒํƒœ์— ๋Œ€ํ•œ ์ฐจ์ด๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ๋ธ”๋กœํ‚น๊ณผ ๋„Œ๋ธ”๋กœํ‚น์€ ์ž‘์—… ์ค‘ ๋Œ€๊ธฐ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๊ตฌ๋ถ„๋˜๋Š” ๊ฐœ๋…์ด๋‹ค.

graph TD
    A[Start] --> B[Task 1 - Synchronous]
    B --> C[Task 2 - Synchronous]
    C --> D[End]
    
    A1[Start] -->|Async Execution| B1[Task 1 - Asynchronous]
    B1 -->|No Waiting| C1[Task 2 - Asynchronous]
    C1 --> D1[End]
    
    A2[Start] -->|Blocking| B2[Task - Blocking]
    B2 -->|Wait until complete| C2[End]
    
    A3[Start] -->|Non-blocking| B3[Task - Non-blocking]
    B3 -->|Instant return| C3[End]

๋™๊ธฐ(Synchronous)

  • ๋™๊ธฐ ๋ฐฉ์‹์€ ์ž‘์—…์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉฐ, ํ•˜๋‚˜์˜ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์—๋Š” ๋‹ค์Œ ์ž‘์—…์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๊ฐ ์ž‘์—…์€ ์„œ๋กœ ์˜์กดํ•˜๋ฉฐ, ์ž‘์—… A๊ฐ€ ์™„๋ฃŒ๋˜์–ด์•ผ ์ž‘์—… B๊ฐ€ ์‹œ์ž‘๋œ๋‹ค.
  • ์ด ๋ฐฉ์‹์€ ๋ธ”๋กœํ‚น ์ƒํƒœ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์ด์ „ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋‹ค์Œ ์ž‘์—…์ด ๋Œ€๊ธฐ ์ƒํƒœ์— ๋†“์ด๊ฒŒ ๋œ๋‹ค.
  • ์˜ˆ: ํŒŒ์ผ์„ ์ฝ์„ ๋•Œ, ํŒŒ์ผ์ด ์™„์ „ํžˆ ์ฝํž ๋•Œ๊นŒ์ง€ ์ฝ”๋“œ๊ฐ€ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค.

๋น„๋™๊ธฐ(Asynchronous)

  • ๋น„๋™๊ธฐ ๋ฐฉ์‹์€ ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•˜๋‚˜์˜ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋‹ค์Œ ์ž‘์—…์ด ์ง„ํ–‰๋œ๋‹ค.
  • ์ž‘์—… ๊ฐ„์— ์˜์กด์„ฑ์ด ์—†์œผ๋ฉฐ, ๊ฐ ์ž‘์—…์ด ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ ๋˜๋Š” ํ”„๋กœ์„ธ์Šค์—์„œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด ๋ฐฉ์‹์€ ๋„Œ๋ธ”๋กœํ‚น ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜์—ฌ, ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜ˆ: ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ์‘๋‹ต์ด ์˜ฌ ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ๊ณ„์† ์‹คํ–‰ํ•œ๋‹ค.

Blocking

  • ๋ธ”๋กœํ‚น ์ž‘์—…์€ ํ˜ธ์ถœ๋œ ํ•จ์ˆ˜๋‚˜ ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰ ํ๋ฆ„์ด ๋ฉˆ์ถ”๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
  • ์ฆ‰, ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋žจ์ด ๋Œ€๊ธฐ ์ƒํƒœ์— ๋น ์ง€๋ฉฐ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์—†๋‹ค.
  • ์˜ˆ: ํŒŒ์ผ์„ ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ์ฝ์œผ๋ฉด, ํŒŒ์ผ์ด ์™„์ „ํžˆ ์ฝํ˜€์งˆ ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค.

Non-blocking

  • ๋…ผ๋ธ”๋กœํ‚น ์ž‘์—…์€ ํ˜ธ์ถœ๋œ ์ž‘์—…์ด ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋˜๋ฉฐ, ์‹คํ–‰ ํ๋ฆ„์ด ์ค‘๋‹จ๋˜์ง€ ์•Š๊ณ  ๊ณ„์† ์ง„ํ–‰๋œ๋‹ค.
  • ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์ž‘์—…์„ ๋™์‹œ์— ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜ˆ: ํŒŒ์ผ์„ ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ์ฝ์œผ๋ฉด, ํŒŒ์ผ ์ฝ๊ธฐ ์š”์ฒญ ํ›„ ์ฆ‰์‹œ ๋‹ค์Œ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ฉฐ, ๋‚˜์ค‘์— ํŒŒ์ผ ์ฝ๊ธฐ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ์•Œ๋ฆผ์„ ๋ฐ›๋Š”๋‹ค.

์„ž๋Š”๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ ์šฉ๋œ๋‹ค.

 

  • Sync + Blocking: ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ณ , ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
  • Async + Blocking: ๋น„๋™๊ธฐ์  ์ž‘์—…์ด์ง€๋งŒ, ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
  • Sync + Non-blocking: ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€๋งŒ, ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Async + Non-blocking: ๋น„๋™๊ธฐ์  ์ž‘์—…์ด๋ฉฐ ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋˜์–ด ๋‹ค๋ฅธ ์ž‘์—…์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
Serializable๊ณผ Pacelable ์ฐจ์ด
  Serializable Parcelable
์„ฑ๋Šฅ ๋Š๋ฆผ: Java Reflection์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์†๋„๊ฐ€ ๋Š๋ฆผ. ๋Œ€๋Ÿ‰์˜ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํ™”ํ•  ๋•Œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋” ์‹ฌํ•ด์งˆ ์ˆ˜ ์žˆ์Œ. ๋น ๋ฆ„: ํ•„๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”ํ•˜๋ฉฐ, Android์˜ Binder ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•ด ์ตœ์ ํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚จ.
๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ๋” ๋งŽ์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: Java์˜ ๊ธฐ๋ณธ ์ง๋ ฌํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์–ด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋งŽ์•„์ง. ๋” ์ ์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ง๋ ฌํ™”ํ•˜๋ฉฐ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ ์Œ.
๊ตฌํ˜„ ๋ณต์žก๋„ ๊ตฌํ˜„์ด ๊ฐ„๋‹จ: 'implements Serializable'๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋˜๋ฉฐ, ๋ณ„๋„์˜ ๋ฉ”์„œ๋“œ ์ž‘์„ฑ ์—†์ด๋„ ์ž๋™์œผ๋กœ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”๊ฐ€ ๊ฐ€๋Šฅํ•จ. ๊ตฌํ˜„์ด ๋ณต์žก: ๊ฐ ํ•„๋“œ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜๋Š” 'writeToParcel' ๋ฐ 'createFromParcel' ๋ฉ”์„œ๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๋ณต์žกํ•จ.
ํ˜ธํ™˜์„ฑ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ: ํด๋ž˜์Šค ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์—ญ์ง๋ ฌํ™” ์‹œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฒ„์ „ ๋ถˆ์ผ์น˜๋‚˜ ํ•„๋“œ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด ์—ญ์ง๋ ฌํ™” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ. ์œ ์—ฐํ•œ ํ˜ธํ™˜์„ฑ: ๋ช…์‹œ์ ์œผ๋กœ ์ง๋ ฌํ™” ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํด๋ž˜์Šค ๊ตฌ์กฐ ๋ณ€๊ฒฝ์— ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Œ. ํ•„๋“œ ์ถ”๊ฐ€๋‚˜ ์‚ญ์ œ ์‹œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ.
์‚ฌ์šฉ ํ™˜๊ฒฝ Java ๋ฐ Android์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ: Android๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Java ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. Android ์ „์šฉ: Android IPC ๋ฐ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, Java ํ‘œ์ค€ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€.
OkHttp์™€ Retrofit2์˜ ์ฐจ์ด

OkHttp์™€ Retrofit์€ ๋‘˜ ๋‹ค ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด์ง€๋งŒ, OkHttp๋Š” ๋” ๋‚ฎ์€ ์ˆ˜์ค€์˜ HTTP ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ณ , Retrofit์€ OkHttp๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ณ ์ˆ˜์ค€์˜ REST ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. Wrapping๋œ๊ฒŒ Retrofit์ด๋ผ๊ณ  ๋ด์•ผ๋œ๋‹ค.

  OkHttp Retrofit
์—ญํ•  ์ €์ˆ˜์ค€ HTTP ํด๋ผ์ด์–ธํŠธ ๊ณ ์ˆ˜์ค€ REST ํด๋ผ์ด์–ธํŠธ
API ์„ค๊ณ„ URL, ํ—ค๋”, HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ž‘์„ฑํ•ด์•ผ ํ•จ ์ธํ„ฐํŽ˜์ด์Šค(์–ด๋…ธํ…Œ์ด์…˜)๋กœ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ž๋™์œผ๋กœ HTTP ์š”์ฒญ์„ ์ƒ์„ฑํ•จ
๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ JSON ํŒŒ์‹ฑ์„ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•จ Gson, Moshi ๋“ฑ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ž๋™์œผ๋กœ JSON์„ Java/Kotlin ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•จ
๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์ˆ˜๋™ ์ฝœ๋ฐฑ์„ ํ†ตํ•ด ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•จ ์ฝ”๋ฃจํ‹ด ๋˜๋Š” RxJava์™€ ํ†ตํ•ฉ๋˜์–ด ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ
๊ธฐ๋Šฅ HTTP/2, WebSocket, ์บ์‹ฑ, ํƒ€์ž„์•„์›ƒ ์„ค์ • ๋“ฑ ๋„คํŠธ์›Œํฌ ํ”„๋กœํ† ์ฝœ๊ณผ ๊ด€๋ จ๋œ ์ €์ˆ˜์ค€ ๊ธฐ๋Šฅ ์ œ๊ณต ์ž๋™ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜, ๋” ๊ฐ„ํŽธํ•œ API ํ˜ธ์ถœ, ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋“ฑ REST API ํ˜ธ์ถœ์„ ์œ„ํ•œ ๊ณ ์ˆ˜์ค€ ๊ธฐ๋Šฅ ์ œ๊ณต
์‚ฌ์šฉ ์‚ฌ๋ก€ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์„ธ๋ถ€ ์ œ์–ด ๋ฐ HTTP ํ”„๋กœํ† ์ฝœ์˜ ์ง์ ‘ ๊ตฌํ˜„์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ REST API ํ†ต์‹ ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ž๋™ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ
git merge์™€ rebase์˜ ์ฐจ์ด?
  git merge git rebase
๋™์ž‘ ๋ฐฉ์‹ ๋‘ ๋ธŒ๋žœ์น˜์˜ ์ด๋ ฅ์„ ๋ณ‘ํ•ฉํ•˜๊ณ , ์ƒˆ๋กœ์šด **๋จธ์ง€ ์ปค๋ฐ‹(Merge Commit)**์„ ์ƒ์„ฑํ•จ ํ•œ ๋ธŒ๋žœ์น˜์˜ ์ปค๋ฐ‹๋“ค์„ ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜ ์œ„์— ์žฌ๋ฐฐ์น˜ํ•˜์—ฌ ์ด๋ ฅ์„ ์žฌ์ž‘์„ฑํ•จ
์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ ๋ณ‘ํ•ฉ ์‹œ, ๋‘ ๋ธŒ๋žœ์น˜์˜ ์ด๋ ฅ์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉฐ, ๋ณ‘ํ•ฉ ์ปค๋ฐ‹์„ ์ถ”๊ฐ€ํ•ด ์ด๋ ฅ์— ๋ถ„๊ธฐ์ ์ด ์ƒ๊น€ ๋ธŒ๋žœ์น˜์˜ ์ปค๋ฐ‹๋“ค์„ ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜ ์œ„์— ์žฌ๋ฐฐ์น˜ํ•˜๋ฏ€๋กœ, ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ๊น”๋”ํ•˜๊ฒŒ ์ง์„ ํ˜•์œผ๋กœ ์œ ์ง€๋จ
์‚ฌ์šฉ ์˜ˆ์‹œ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์ด ์™„๋ฃŒ๋œ ํ›„, ๊ธฐ์กด ์ด๋ ฅ์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณ‘ํ•ฉํ•  ๋•Œ ์‚ฌ์šฉ ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜๋ฅผ ๋ฉ”์ธ ๋ธŒ๋žœ์น˜ ์œ„์— ์žฌ๋ฐฐ์น˜ํ•˜์—ฌ ์ด๋ ฅ์„ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ
์ถฉ๋Œ ํ•ด๊ฒฐ ๋ณ‘ํ•ฉ ์‹œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด, ์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•˜๊ณ  ๋ณ‘ํ•ฉ ์ปค๋ฐ‹์„ ์ƒ์„ฑํ•จ ์žฌ๋ฐฐ์น˜ ๊ณผ์ •์—์„œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด, ๊ฐ ์ปค๋ฐ‹๋งˆ๋‹ค ์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•˜๊ณ  ์ปค๋ฐ‹์„ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•จ
์žฅ์  ์ด๋ ฅ์˜ ๋ชจ๋“  ์ปค๋ฐ‹์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ˜‘์—… ์‹œ ์ด๋ ฅ์„ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Œ ๊ฐ„๊ฒฐํ•œ ์ด๋ ฅ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ธŒ๋žœ์น˜๊ฐ€ ์ง์„ ํ˜•์œผ๋กœ ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌ๋จ
๋‹จ์  ๋ณ‘ํ•ฉ ์ปค๋ฐ‹์œผ๋กœ ์ธํ•ด ์ด๋ ฅ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Œ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์žฌ์ž‘์„ฑ๋˜๋ฏ€๋กœ, ํ˜‘์—… ์ค‘์ธ ๋ธŒ๋žœ์น˜์—์„œ rebase๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ํ˜ผ๋ž€์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Œ
์‚ฌ์šฉ ์ƒํ™ฉ ํ˜‘์—… ๋ธŒ๋žœ์น˜ ๋ณ‘ํ•ฉ ์‹œ ๊ธฐ์กด ์ด๋ ฅ ์œ ์ง€๊ฐ€ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ ๊ฐœ์ธ ์ž‘์—… ๋ธŒ๋žœ์น˜์—์„œ ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ

rebase๋Š” ์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์žฌ๊ตฌ์„ฑํ•ด์„œ ๊น”๋”ํ•œ ์ปค๋ฐ‹ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

git๊ณผ git flow์˜ ์ฐจ์ด๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?
  Git Git Flow
์ •์˜ ๋ถ„์‚ฐ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์œผ๋กœ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ด€๋ฆฌํ•˜๊ณ  ํ˜‘์—…์„ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ Git์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋ธŒ๋žœ์นญ ์ „๋žต์œผ๋กœ, ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์กฐํ™”ํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•๋ก 
๊ธฐ๋Šฅ ํŒŒ์ผ์„ ์ถ”์ ํ•˜๊ณ  ๋ฒ„์ „ ๊ธฐ๋ก์„ ๋‚จ๊ธฐ๋ฉฐ, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ํ˜‘์—…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜, ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜, ํ•ซํ”ฝ์Šค ๋ธŒ๋žœ์น˜ ๋“ฑ ๋ช…ํ™•ํ•œ ๋ธŒ๋žœ์นญ ๊ทœ์น™์„ ์ •ํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ด€๋ฆฌํ•จ
์ฃผ์š” ๋ช…๋ น์–ด git init, git add, git commit, git push, git merge, git rebase ๋“ฑ Git ๋ช…๋ น์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ, ํŠน์ • ๋ธŒ๋žœ์น˜ ์ „๋žต์„ ๋”ฐ๋ฆ„. develop, feature, release, hotfix, master ๋ธŒ๋žœ์น˜๋ฅผ ์‚ฌ์šฉ
์‚ฌ์šฉ ๋ชฉ์  ์†Œ์Šค ์ฝ”๋“œ์˜ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐ ํ˜‘์—…์„ ์ง€์›ํ•˜๋ฉฐ, ๋ชจ๋“  ํ˜•์‹์˜ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์ฃผ๋กœ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ฝ”๋“œ์˜ ๋ฆด๋ฆฌ์Šค ์ค€๋น„์™€ ๋ฐฐํฌ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ธŒ๋žœ์นญ ์ „๋žต
๋ธŒ๋žœ์น˜ ๊ตฌ์กฐ ์ž์œ ๋กญ๊ฒŒ ๋ธŒ๋žœ์น˜๋ฅผ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠน์ • ๋ธŒ๋žœ์นญ ๊ทœ์น™์€ ์—†์Œ master, develop, feature, release, hotfix ๋“ฑ์˜ ๋ช…ํ™•ํ•œ ๋ธŒ๋žœ์น˜ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฆ„
๋ณต์žก๋„ Git ์ž์ฒด๋Š” ๋ธŒ๋žœ์น˜ ์ „๋žต์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์•„ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ๊ณ ์ •๋œ ๋ธŒ๋žœ์นญ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋ฏ€๋กœ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋ณต์žกํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฒด๊ณ„์ ์ธ ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์คŒ
์‚ฌ์šฉ ์ƒํ™ฉ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ, ๋‹จ์ผ ๋ธŒ๋žœ์น˜๋‚˜ ๊ฐ„๋‹จํ•œ ๋ธŒ๋žœ์นญ ์ „๋žต์„ ์‚ฌ์šฉํ•  ๋•Œ ์ ํ•ฉ ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ๋‚˜ ํŒ€ ํ˜‘์—…์—์„œ ์ฒด๊ณ„์ ์ธ ๋ธŒ๋žœ์นญ ์ „๋žต์„ ํ•„์š”๋กœ ํ•  ๋•Œ ์ ํ•ฉ. ๋ช…ํ™•ํ•œ ๋ฆด๋ฆฌ์Šค ๊ด€๋ฆฌ์™€ ๋ฐฐํฌ ์ฃผ๊ธฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ
  • Git์€ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋„๊ตฌ์ด๋ฉฐ, ์†Œ์Šค ์ฝ”๋“œ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ์ด๋‹ค.
  • Git Flow๋Š” Git์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋ธŒ๋žœ์นญ ์ „๋žต์œผ๋กœ, ๊ฐœ๋ฐœ, ๋ฆด๋ฆฌ์Šค, ๋ฐฐํฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•๋ก ์ด๋‹ค.
interface์™€ abstract ์ฐจ์ด? -> ์ผ๋ฐ˜์ ์ธ ์ •์˜, ์ฝ”ํ‹€๋ฆฐ์—์„œ์˜ ์ •์˜
์ผ๋ฐ˜ Interface Abstract Class
๊ตฌํ˜„ ๋‚ด์šฉ ๊ตฌํ˜„์ด ์—†๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•˜๋ฉฐ, ๋””ํดํŠธ ๋ฉ”์„œ๋“œ๋กœ ์ผ๋ถ€ ๊ตฌํ˜„ ๊ฐ€๋Šฅ ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ์™€ ์ถ”์ƒ ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ํฌํ•จ ๊ฐ€๋Šฅ. ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ๋Š” ๊ตฌํ˜„ ๋‚ด์šฉ์„ ๊ฐ€์ง
๋‹ค์ค‘ ์ƒ์† ํ•œ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ ํ•œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ถ”์ƒ ํด๋ž˜์Šค๋งŒ ์ƒ์† ๊ฐ€๋Šฅ
๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ. ์ƒ์ˆ˜๋งŒ ์„ ์–ธ ๊ฐ€๋Šฅ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ณตํ†ต ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
์ƒ์„ฑ์ž ์ƒ์„ฑ์ž ์—†์Œ ์ƒ์„ฑ์ž ์žˆ์Œ. ์ƒ์†๋œ ํด๋ž˜์Šค์—์„œ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐํ™” ๊ฐ€๋Šฅ
๋ชฉ์  ์ฃผ๋กœ **๋™์ž‘(ํ–‰๋™)**์„ ์ •์˜ํ•˜๊ณ , ์„œ๋กœ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋“ค์ด ๊ฐ™์€ ๋™์ž‘์„ ๊ตฌํ˜„ํ•˜๋„๋ก ์š”๊ตฌํ•  ๋•Œ ์‚ฌ์šฉ ๊ณตํ†ต๋œ ํŠน์„ฑ ๋ฐ ๋™์ž‘์„ ๊ฐ€์ง„ ํด๋ž˜์Šค๋“ค ๊ฐ„์˜ ์ƒ์† ๊ด€๊ณ„๋ฅผ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉ
์ธ์Šคํ„ด์Šคํ™” ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ธ์Šคํ„ด์Šคํ™” ๋ถˆ๊ฐ€, ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์ถ”์ƒ ํด๋ž˜์Šค๋„ ์ธ์Šคํ„ด์Šคํ™” ๋ถˆ๊ฐ€, ๋ฐ˜๋“œ์‹œ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•จ
์‚ฌ์šฉ ์ƒํ™ฉ ํด๋ž˜์Šค ๊ฐ„ ๊ณตํ†ต์ ์ธ ํ–‰๋™์„ ์ •์˜ํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ๊ตฌํ˜„์ฒด๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ ๊ณตํ†ต์ ์ธ ์†์„ฑ๊ณผ ๋™์ž‘์„ ๊ฐ€์ง„ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์—์„œ ๊ธฐ๋ณธ ๋™์ž‘์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ผ๋ถ€๋งŒ ๊ตฌ์ฒด์ ์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ
  • ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๊ตฌํ˜„์ด ์—†๋Š” ๋ฉ”์„œ๋“œ ์„ ์–ธ์„ ์ œ๊ณตํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์—์„œ ๊ณตํ†ต์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ๋™์ž‘์„ ์ •์˜ํ•˜๊ณ , ๋‹ค์ค‘ ์ƒ์†์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ถ”์ƒ ํด๋ž˜์Šค๋Š” ์ถ”์ƒ ๋ฉ”์„œ๋“œ์™€ ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•˜๋ฉฐ, ์ƒ์†์„ ํ†ตํ•ด ๊ณตํ†ต ๋™์ž‘์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹จ์ผ ์ƒ์†๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
kotlin Interface(์—ฌ๊ธฐ์„œ ์ข€ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค) Abstract Class
๊ตฌํ˜„ ๋‚ด์šฉ ์ถ”์ƒ ๋ฉ”์„œ๋“œ์™€ ๊ธฐ๋ณธ ๊ตฌํ˜„์„ ํฌํ•จํ•œ ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ์„ ์–ธ ๊ฐ€๋Šฅ ์ถ”์ƒ ๋ฉ”์„œ๋“œ์™€ ๊ตฌํ˜„๋œ ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ํฌํ•จ ๊ฐ€๋Šฅ
๋‹ค์ค‘ ์ƒ์† ํ•œ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ ํ•œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ถ”์ƒ ํด๋ž˜์Šค๋งŒ ์ƒ์† ๊ฐ€๋Šฅ
ํ”„๋กœํผํ‹ฐ(์ค‘์š”) ์ถ”์ƒ ํ”„๋กœํผํ‹ฐ์™€ ๊ธฐ๋ณธ getter๋ฅผ ์„ ์–ธ ๊ฐ€๋Šฅ. **์ƒํƒœ(๋ฉค๋ฒ„ ๋ณ€์ˆ˜)**๋Š” ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
์ƒ์„ฑ์ž ์ƒ์„ฑ์ž๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ ์ƒ์„ฑ์ž๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ. ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ์ƒ์†์„ ํ†ตํ•ด ์ƒ์„ฑ์ž ํ˜ธ์ถœ ๊ฐ€๋Šฅ
๋ชฉ์  ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ๊ณตํ†ต ๋™์ž‘์„ ์ •์˜ํ•˜๊ณ , ๊ฐ™์€ ๋™์ž‘์„ ๊ตฌํ˜„ํ•˜๋„๋ก ์š”๊ตฌํ•  ๋•Œ ์‚ฌ์šฉ ๊ณตํ†ต๋œ ์ƒํƒœ์™€ ๋™์ž‘์„ ๊ฐ€์ง„ ํด๋ž˜์Šค๋“ค ๊ฐ„์— ๊ณตํ†ต ๊ตฌํ˜„์„ ๊ณต์œ ํ•  ๋•Œ ์‚ฌ์šฉ
์ธ์Šคํ„ด์Šคํ™” ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ธ์Šคํ„ด์Šคํ™” ๋ถˆ๊ฐ€, ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์ถ”์ƒ ํด๋ž˜์Šค๋„ ์ธ์Šคํ„ด์Šคํ™” ๋ถˆ๊ฐ€, ๋ฐ˜๋“œ์‹œ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•จ
์‚ฌ์šฉ ์ƒํ™ฉ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ๋™์ผํ•œ ๋™์ž‘์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฉฐ, ๋‹ค์ค‘ ์ƒ์†์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํด๋ž˜์Šค ๊ฐ„์— ๊ณตํ†ต๋œ ์ƒํƒœ์™€ ๋™์ž‘์„ ๊ณต์œ ํ•˜๊ณ , ๋‹จ์ผ ์ƒ์† ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ
์•ˆ๋“œ๋กœ์ด๋“œ ๊ถŒ์žฅ ์•„ํ‚คํ…์ณ?
graph TD
    subgraph Presentation Layer
        A[View]
        A -->|์‚ฌ์šฉ์ž ์ž…๋ ฅ| B[ViewModel]
        B -->|LiveData or StateFlow| A
    end
    
    subgraph Domain Layer - optional
        B --> F[UseCase]
    end
    
    subgraph Data Layer
        F --> C
        B --> C[Repository]
        C --> D[Local Database]
        C --> E[Network]
        E --> C
        D --> C
    end

https://developer.android.com/topic/architecture?hl=ko&_gl=1*ee4yak*_up*MQ..*_ga*MTQwODM3NjUyMy4xNzI3NjEyMzEz*_ga_6HH9YJMN9M*MTcyNzYxMjMxMi4xLjAuMTcyNzYxMjMxMi4wLjAuNzk3MTIxNjY0

 

์•ฑ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•ฑ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ์ด ๊ฐ€์ด๋“œ์—๋Š” ๊ณ ํ’ˆ์งˆ์˜ ๊ฐ•๋ ฅํ•œ

developer.android.com

 

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

 

Google์˜ ์•ฑ์•„ํ‚คํ…์ณ์—์„œ๋Š” ‘domain layer๊ฐ€ data layer๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค’์ด๊ณ ,
๋ฐฅ์˜ ํด๋ฆฐ ์•„ํ‚คํ…์ณ๋Š” Domain layer๋Š” ์•„๋ฌด๋„ ๋ชฐ๋ผ์•ผ ํ•˜๊ณ ,Presentation layer, Data layer๊ฐ€ Domain layer์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค.

๊ทผ๋ฐ ๊ตฌ๊ธ€์˜ ์•ฑ ์•„ํ‚คํ…์ณ์—์„œ Domain๋ ˆ์ด์–ด๋Š” optional์ด๋‹ค. ๋ทฐ๋ชจ๋ธ์—์„œ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๊ด€ํ•˜๋‹ค.

 

 


::์ถœ์ฒ˜::

https://1-three.tistory.com/129

https://velog.io/@godmin66/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A9%B4%EC%A0%91-%EC%A4%91-%EB%82%98%EC%99%94%EB%8D%98-%EC%A7%88%EB%AC%B8-%EB%AA%A9%EB%A1%9D

https://github.com/devSquad-study/2023-CS-Study/tree/main

https://github.com/4z7l/tech_interview.zip

https://github.com/VSFe/Tech-Interview?tab=readme-ov-file

https://github.com/WooVictory/Ready-For-Tech-Interview

https://github.com/gyoogle/tech-interview-for-developer

https://github.com/JaeYeopHan/Interview_Question_for_Beginner

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
09
27

https://www.acmicpc.net/problem/2559


์˜ค๋‹ต๋…ธํŠธ(ํ‘ธ๋Š” ๋™์•ˆ ์‚ฌ๊ณ ๊ณผ์ • ๋ฐ ๊ฐœ์„ ์ )

๋ˆ„์ ํ•ฉ ๋ฌธ์ œ๋‹ค. 

์ฒ˜์Œ์— ๊ณ ๋ ค์•ˆํ•œ ๋ถ€๋ถ„์€ ๋ˆ„์ ํ•ฉ ๋ฐฐ์—ด ์‹œ์ž‘ ์ธ๋ฑ์Šค ๋ถ€๋ถ„์ด๋‹ค.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
    static int n, gap;

    public static void main(String[] args) throws Exception {
        //System.setIn(new FileInputStream("res/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        gap = Integer.parseInt(st.nextToken());
        int[] in = new int[n];
        int[] prefix = new int[n];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            in[i] = Integer.parseInt(st.nextToken());
            if (i == 0) {
                prefix[i] = in[i];
            } else {
                // ๋ˆ„์ ํ•ฉ
                prefix[i] = prefix[i - 1] + in[i];
            }
        }
        PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> {
            return o2 - o1;
        });
        //์ด์ œ ๊ฐ ๊ฐ„๊ฒฉ์˜ ํ•ฉ์„ pq์— ๋„ฃ๊ธฐ
        int idx = n - 1;
        while (true) {
            if (idx - gap < 0) break;
//            System.out.println(prefix[idx] + ", " + prefix[idx - gap]);
            pq.add(prefix[idx] - prefix[idx - gap]);
            idx--;
        }

        System.out.println(pq.poll());
    }
}

์ด๋ ‡๊ฒŒ ํ•ด๋ฒ„๋ฆฌ๋ฉด ๋ฌด์Šจ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ƒ๋ฉด 

10 3
100 -1 -1 -1 -1 -1 -1 -1 -1 -1

์ด ์ž…๋ ฅ์ด ๋“ค์–ด์˜ฌ ๋•Œ ๋ˆ„์ ํ•ฉ ๋ฐฐ์—ด์—๋Š” ๋‹ค ๋“ค์–ด๊ฐ€์ง€๋งŒ, ๊ฐ„๊ฒฉ์„ ๋”ํ•  ๋•Œ 98์ด ์ตœ๋Œ€๊ฐ’์ด ์•„๋‹ˆ๋ผ 3์ด ์ตœ๋Œ€๊ฐ’์ด ๋‚˜์™€๋ฒ„๋ฆฐ๋‹ค.

[100, 99, 98, 97, 96, 95, 94, 93, 92, 91]

์ด๊ฒŒ size๊ฐ€ n์ธ prefix์ธ๋ฐ, ๋‚ด ๋กœ์ง ์ƒ `n`๋ฒˆ์งธ - `n-k`๋ฒˆ์งธ ๋ˆ„์ ํ•ฉ ํ•ญ๋ชฉ์„ ๋นผ๋ฉด ๊ทธ ์‚ฌ์ด์˜ ๋ˆ„์ ํ•ฉ์ด ๋‚˜์˜ค๋„๋ก ๋˜์–ด์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ -3์ด ๋‚˜์˜ค๊ฒŒ ๋˜๊ณ , ์›๋ž˜๋ผ๋ฉด 100, -1, -1 ์ด ์„ธ ์ˆ˜์˜ ํ•ฉ์ด 98์ธ๋ฐ, ์‹œ์ž‘ ํ•ญ๋ชฉ์„ ๊ฐ„๊ฒฉ์œผ๋กœ ์žก๊ณ  ๊ฐ€๋ฒ„๋ฆฐ ์…ˆ์ด ๋ผ์„œ -3์ด ๋‚˜์˜ค๊ฒŒ ๋๋‹ค. ๊ทธ๋ž˜์„œ ์‹œ์ž‘ ๋ถ€๋ถ„์„ ๊ณ ๋ คํ•˜๊ฒŒ ๋˜๋ฉด n+1 ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ prefix ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ณ , ์•„๋ž˜์™€ ๊ฐ™์ด ๋ˆ„์ ํ•ฉ ๋ฐฐ์—ด์ด ๊ตฌ์„ฑ๋œ๋‹ค.

prefix ๋ฐฐ์—ด ๊ตฌ์„ฑํ•  ๋•Œ๋Š” ๊ทธ๋ž˜์„œ 1์นธ ๋” ํฌ๊ฒŒ ํ•˜๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. DP๋ž‘ ๊ฑฐ์˜ ๋™์ผํ•œ ๊ฐœ๋…์œผ๋กœ ์ดํ•ดํ–ˆ๋‹ค.

[0, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91]

 

๋น„์Šทํ•œ ๋ฌธ์ œ๋กœ ์—ฐ์†ํ•ฉ์ด ์žˆ๋‹ค.

๋ช‡ ๋ฒˆ ์—ฐ์†ํ•ด๋„ ์ข‹์œผ๋‹ˆ๊นŒ ์—ฐ์†ํ•œ ํ•ฉ์ด ๊ฐ€์žฅ ํฐ ์ˆ˜๋ฅผ ๊ตฌํ•˜๋Š” ๋ฌธ์ œ๋‹ค.

https://www.acmicpc.net/problem/1912

์—ฌ๊ธฐ์„œ ๋– ์˜ฌ๋ฆฐ ์•„์ด๋””์–ด๋Š” ์–ด์ฐจํ”ผ ๋ˆ„์ ํ•ด์„œ ํ•ฉํ•˜๋Š”๋ฐ, ๊ฐ’์ด ์ž‘์•„์ง€๋Š” ๊ฒฝ์šฐ์—๋Š” ๋”ํ•˜์ง€ ์•Š๊ณ  ๊ฐ€๊ฒ ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

for (int i = 0; i < n; i++) {
    in[i] = Integer.parseInt(st.nextToken());
}
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> {
    return o2 - o1;
});
for (int i = 1; i <= n; i++) {
    prefix[i] = Math.max(prefix[i-1]+in[i-1], in[i-1]);
    pq.add(prefix[i]);
}

์ด๋Ÿฌ๋ฉด ๊ฐ’์ด ์ฃผ์–ด์ง„ ์ž…๋ ฅ ๋ฐฐ์—ด๋ณด๋‹ค ์ž‘์€ ๋ˆ„์ ํ•ฉ ์œ„์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ˆ„์ ํ•ฉ ๊ฐ’์— ์ด์ „ ์—ฐ์†ํ•ฉ์„ ๋ฌด์‹œํ•˜๊ณ , ์ƒˆ๋กœ ์‹œ์ž‘ํ•œ๋‹ค.

์ผ์ข…์˜ DP๋ผ๊ณ  ๋ณผ์ˆ˜๋„ ์žˆ์ง€์•Š์„๊นŒ?

 

์ข€ ๋” ๋‚œ์ด๋„์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ธฐ ์œ„ํ•ด ๋ˆ„์ ํ•ฉ์„ 2๋ฒˆ์“ฐ๋Š” ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ดค๋‹ค.

https://www.acmicpc.net/problem/2304

๋ฌธ์ œ๋ฅผ ์ž˜ ์ฝ์–ด์•ผํ•œ๋‹ค. ์„ ๋ถ„ ์‹œ์ž‘์ ์„ ์ฃผ๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws Exception {
        // System.setIn(new FileInputStream("res/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        int n = Integer.parseInt(st.nextToken());
        int maxH = -1;
        int maxIdx = -1;
        int[] input = new int[10001];

        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            int idx = Integer.parseInt(st.nextToken()); // ๊ธฐ๋‘ฅ ์™ผ์ชฝ๋ฉด์˜ ์œ„์น˜
            int h = Integer.parseInt(st.nextToken());
            input[idx + 1] = h; // idx ์ž์ฒด๋Š” 0์ด ์—†์œผ๋‚˜, 0-1 ,1-2 ์ด๋ ‡๊ฒŒ ๊ฐ„๊ฒฉ์œผ๋กœ ๋“ค์–ด๊ฐ -> ์ •ํ™•ํžˆ๋Š” ์™ผ์ชฝ ๋ชจ์„œ๋ฆฌ ๊ธฐ์ค€์ด๋‹ˆ๊นŒ ํ•˜๋‚˜์”ฉ ๋ฐ€๊ธฐ
            maxIdx = Math.max(idx, maxIdx);
            maxH = Math.max(h, maxH);
        }


        int[] pref = new int[maxIdx + 2]; // ์•ž๋’ค์— ํ•œ์นธ์”ฉ ์ถ”๊ฐ€ํ•˜๊ธฐ
        int[] suff = new int[maxIdx + 2]; // ์•ž๋’ค์— ํ•œ์นธ์”ฉ ์ถ”๊ฐ€ํ•˜๊ธฐ
        int[] maxPoint = new int[2]; //
        Arrays.fill(maxPoint, -1);
        int prev = 0;
        int prefSum = -1;
        int suffSum = -1;
        // l to r
        for (int i = 1; i <= maxIdx + 1; i++) {
            prev = Math.max(input[i], prev);
            pref[i] = pref[i - 1] + prev;
            if (input[i] == maxH) {
                maxPoint[0] = i;
                break;
            }
        }
        prev = 0;
        for (int i = maxIdx + 1; i >= 0; i--) {
            prev = Math.max(input[i], prev);
            suff[i - 1] = suff[i] + prev;
            if (input[i] == maxH) {
                maxPoint[1] = i;
                break;
            }
        }
        // max๊นŒ์ง€ ํ•ฉ
        int maxLToR = -1;
        int maxRToL = -1;
        for (int i = 0; i < maxPoint[0]; i++) {
            maxLToR = Math.max(maxLToR, pref[i]);
        }
        for (int i = maxIdx + 1; i >= maxPoint[1]; i--) {
            maxRToL = Math.max(maxRToL, suff[i]);
        }
//        System.out.println(maxLToR + ", " + maxRToL);

        // ํ•ฉ์น˜๊ธฐ

        int maxWidth = maxPoint[1] - maxPoint[0] + 1;

//
//        System.out.println(Arrays.toString(pref) + ", " + maxPoint[0] + ", " + maxH);
//        System.out.println(Arrays.toString(suff) + ", " + maxPoint[1] + ", " + maxH);
        System.out.println(maxLToR + maxRToL + maxWidth * maxH);
    }
}

์™„์ „ํžˆ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์•„๋ž˜ ๋ฌธ์ œ๋„ ํ’€์ˆ˜์žˆ๋‹ค.

https://www.acmicpc.net/problem/14719

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("res/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int H = Integer.parseInt(st.nextToken());
        int W = Integer.parseInt(st.nextToken());

        int[] heights = new int[W];
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < W; i++) {
            heights[i] = Integer.parseInt(st.nextToken());
        }

        int[] leftMax = new int[W]; // ์–˜๋„ค๊ฐ€ ์ฐฝ๊ณ ๋‹ค๊ฐํ˜•์— ์žˆ๋˜ prefix, suffix ์—ญํ• 
        int[] rightMax = new int[W];

        // ์™ผ์ชฝ์—์„œ ๋ณธ ์ตœ๋Œ€ ๋†’์ด ๊ณ„์‚ฐ
        leftMax[0] = heights[0];
        for (int i = 1; i < W; i++) {
            leftMax[i] = Math.max(leftMax[i - 1], heights[i]);
        }

        // ์˜ค๋ฅธ์ชฝ์—์„œ ๋ณธ ์ตœ๋Œ€ ๋†’์ด ๊ณ„์‚ฐ
        rightMax[W - 1] = heights[W - 1];
        for (int i = W - 2; i >= 0; i--) {
            rightMax[i] = Math.max(rightMax[i + 1], heights[i]);
        }

        // ๋น—๋ฌผ์ด ๊ณ ์ผ ์ˆ˜ ์žˆ๋Š” ์–‘ ๊ณ„์‚ฐ
        int water = 0;
        for (int i = 0; i < W; i++) {
            int minHeight = Math.min(leftMax[i], rightMax[i]);
            if (minHeight > heights[i]) {
                water += minHeight - heights[i];
            }
        }

        System.out.println(water);
    }
}

์ •๋‹ต์ฝ”๋“œ

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
    static int n, gap;

    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("res/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        gap = Integer.parseInt(st.nextToken());
        int[] in = new int[n];
        int[] prefix = new int[n+1];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            in[i] = Integer.parseInt(st.nextToken());
            prefix[i+1] = prefix[i] + in[i];
        }
        System.out.println(Arrays.toString(prefix));

        PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> {
            return o2 - o1;
        });
        //์ด์ œ ๊ฐ ๊ฐ„๊ฒฉ์˜ ํ•ฉ์„ pq์— ๋„ฃ๊ธฐ
        int idx = n;
        while (true) {
            if (idx - gap < 0) break;
            pq.add(prefix[idx] - prefix[idx - gap]);
            idx--;
        }

        System.out.println(pq.poll());
    }
}
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
    static int n;

    public static void main(String[] args) throws Exception {
        // System.setIn(new FileInputStream("res/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        int[] in = new int[n];
        int[] prefix = new int[n + 1];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            in[i] = Integer.parseInt(st.nextToken());
        }
        PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> {
            return o2 - o1;
        });
        for (int i = 1; i <= n; i++) {
            prefix[i] = Math.max(prefix[i-1]+in[i-1], in[i-1]);
            pq.add(prefix[i]);
        }
//        System.out.println(Arrays.toString(prefix));
        
        System.out.println(pq.poll());
    }
}
๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
09
22

์•ˆ๋“œ๋กœ์ด๋“œ ์™ธ๋ถ€ ํ™˜๊ฒฝ๊ณผ ์—ฐ๋™ํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ ์žˆ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ์™€ ์—ฐ๊ณ„๋œ ๋ถ€๋ถ„์„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋ ค๋ฉด ๋งค๋ฒˆ ์ƒˆ๋กœ ๋นŒ๋“œ ๋ฐ›์•„ ํ…Œ์ŠคํŠธ ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ์— ์„ค์น˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ ๋งค๋ฒˆ ์„ค์น˜๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ๋„, ๋งค๋ฒˆ ์—ฐ๊ฒฐํ•ด์„œ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ๋„ ์ƒ๋‹นํžˆ ๋น„ํšจ์œจ์ ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. Github Actions๋กœ buildํ•˜์—ฌ artifacts๋กœ ๋ฝ‘์•„๋ณธ ๊ฒฝํ—˜์ด ์žˆ์—ˆ๊ธฐ์—, gitlab์˜ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์ธ gitlab pipeline์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜์ง€์•Š์„๊นŒ? ๋ผ๋Š” ์ƒ๊ฐ์œผ๋กœ ์ด๋ฒˆ์— ๋„์ „ํ•ด๋ดค๋‹ค.

 

์ด๋ฒˆ์— ํ•ด๋ณผ ๊ฒƒ์€ gitlab-pipeline์œผ๋กœ  ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ build ํ•ด์„œ, artifacts๋กœ debug apk๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Windows์—์„œ ๋กœ์ปฌ ํ™˜๊ฒฝ Gitlab-Runner ์ดˆ๊ธฐ ์„ธํŒ…ํ•˜๊ธฐ

์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ํ™˜๊ฒฝ์„ ์œ„ํ•ด์„œ Docker์™€ Docker-Compose๋ฅผ ์‚ฌ์šฉํ•ด gitlab-runner๋ฅผ ์„ค์น˜ํ•  ๊ฒƒ์ด๋‹ค. docker-desktop์„ ๋จผ์ € ์„ค์น˜ํ•˜๋ฉด ๋œ๋‹ค.

 

docker์— ์žˆ๋Š” terminal์„ ์‚ฌ์šฉํ•  ๊ฑด๋ฐ, ๋ฆฌ๋ˆ…์Šค ๋ช…๋ น์–ด๊ฐ€ ๋™์ž‘ํ•˜๋Š” mingw64๋ฅผ ์“ฐ๊ฑฐ๋‚˜, wsl2 ํ™˜๊ฒฝ์—์„œ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

# ๋กœ์ปฌ ํ™˜๊ฒฝ์— ๋””๋ ‰ํ† ๋ฆฌ ๋งŒ๋“ค๊ธฐ

๋จผ์ € C๋“œ๋ผ์ด๋ธŒ๋‚˜, ์ ‘๊ทผํ•˜๊ธฐ ์‰ฌ์šด ์œ„์น˜์— gitlab runner ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

mkdir gitlab-runner
cd gitlab-runner

์ด์ œ ์—ฌ๊ธฐ๊ฐ€ gitlab-runner ์„œ๋น„์Šค๊ฐ€ ์œ„์น˜ํ•  ๊ณณ์ด๋‹ค.

vscode๋‚˜ ๋‹ค๋ฅธ ์—๋””ํ„ฐ๋กœ ํ•ด๋‹น ๋””๋ ‰ํ† ๋ฆฌ์—์„œ docker-compose.yml์„ ๋งŒ๋“ค์–ด์ค€๋‹ค. ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•˜๋ฉด ๋œ๋‹ค.

version: "3"

services:
  gitlab-runner:
    container_name: gitlab-runner
    image: 'gitlab/gitlab-runner:latest'
    restart: always
    volumes:
    - './config:/etc/gitlab-runner'
    - '/var/run/docker.sock:/var/run/docker.sock'

์ตœ์‹ ๋ฒ„์ „์˜ gitlab-runner๊ฐ€ ์„ค์น˜๋˜๋Š” compose ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค. ์œ„์—์„œ ๋ถ€ํ„ฐ ์ญ‰ ๋ณด์ž.

Docker Compose ๋ฒ„์ „์„ 3์œผ๋กœ ์ง€์ •ํ•˜๊ณ , gitlab-runner๋ผ๋Š” ์ด๋ฆ„์˜ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•œ๋‹ค. ์ปจํ…Œ์ด๋„ˆ์— ์‚ฌ์šฉ๋  ์ด๋ฏธ์ง€๋Š” gitlab-runner ์ตœ์‹ ๋ฒ„์ „์ด๊ณ , ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ค‘์ง€๋ ๋•Œ(PC ์ข…๋ฃŒ์™€ ๊ฐ™์€ ๊ฒฝ์šฐ) ์žฌ์‹œ์ž‘ ์˜ต์…˜์„ ๊ฑธ๊ณ  ์ปจํ…Œ์ด๋„ˆ์˜ ๋ณผ๋ฅจ๋งˆ์šดํŠธ ๊นŒ์ง€ ์ •์˜ํ•ด์คฌ๋‹ค. ๋ณผ๋ฅจ๋งˆ์šดํŠธ ์˜๋ฏธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
`./config:/etc/gitlab-runner`: ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์˜ config ํด๋”๋ฅผ ์ปจํ…Œ์ด๋„ˆ์˜ /etc/gitlab-runner์— ๋งˆ์šดํŠธํ•œ๋‹ค.
`/var/run/docker.sock:/var/run/docker.sock`: ํ˜ธ์ŠคํŠธ์˜ Docker ์†Œ์ผ“์„ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€๋กœ ๋งˆ์šดํŠธํ•œ๋‹ค. ์ด๋Ÿฌ๋ฉด gitlab runnner๊ฐ€ ํ˜ธ์ŠคํŠธ docker-daemon์„ ์จ์„œ docker in docker ํ™˜๊ฒฝ์ด ๊ตฌ์„ฑ๋˜๋Š”๋ฐ, ์ด๋Ÿฌ๋ฉด ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ŠคํŠธ์˜ ๋„์ปค ์—”์ง„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ํ˜ธ์ŠคํŠธ์˜ ์ด๋ฏธ์ง€ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ์ข€ ๋” ํšจ์œจ์ ์ธ ๊ตฌ์„ฑ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

 

์ปดํฌ์ฆˆ ํŒŒ์ผ์„ ์‹คํ–‰์‹œ์ผœ์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.

docker compose up -d

Gitlab-Runner ๋“ฑ๋กํ•˜๊ธฐ

gitlab ํ”„๋กœ์ ํŠธ๋กœ ๊ฐ€์„œ, runner๋ฅผ ๋งŒ๋“ค์–ด์ค˜์•ผํ•œ๋‹ค.

CI/CD์„ธํŒ…์œผ๋กœ ๊ฐ€์„œ runner๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

tag๋ฅผ ์ง€์ •ํ•ด์ฃผ๊ณ , unstaged jobs๋„ ์ฒดํฌํ•ด์„œ ํƒœ๊ทธ๊ฐ€ ์ง€์ •์•ˆ๋œ ci ์ž‘์—…๋„ ๋Œ์•„๊ฐ€๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด gitlab-runner ๋“ฑ๋ก ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ, ์ด๊ฑธ ๋„์ปค๋กœ ๋งŒ๋“  ์ปจํ…Œ์ด๋„ˆ์— ์ง€์ •ํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

docker-compose exec gitlab-runner bash

docker-compose exec๋Š” ์ด๋ฏธ ์‹คํ–‰ ์ค‘์ธ gitlab-runnner ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ์ƒˆ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. bash ์…ธ์„ ์—ด๊ฒ ๋‹ค๋Š” ์˜๋ฏธ๋กœ ์—ฌ๊ธฐ์„œ ์ € gitlab-runner register๋ฅผ ๋“ฑ๋กํ•ด์•ผ ๋„์ปค์— ์˜ฌ๋ผ๊ฐ„ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊นƒ๋žฉ ์ปจํ…Œ์ด๋„ˆ์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค.

# gitlab-runner ์ปจํ…Œ์ด๋„ˆ ์•ˆ์˜ shell์ด๋‹ค.
gitlab-runner register  --url gitlab ์„œ๋ฒ„ ์ฃผ์†Œ  --token ์•„๊นŒ ์žˆ๋˜ ํ† ํฐ ๊ฐ’

์•„๊นŒ ๋ช…๋ น์–ด๋ฅผ ๋ณต์‚ฌํ•ด์„œ ๊ทธ๋Œ€๋กœ ๋ถ™์—ฌ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ญ๋ผ๋ญ๋ผ ๋‚˜์˜ค๋Š”๋ฐ, executer ๋ฅผ `docker`, docker image๋ฅผ `alpine:latest` ๋กœ ์„ค์ •ํ•˜๋Š” ์ด์™ธ์—๋Š” ๋‹ค enter  ์ณ์„œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋„ฃ์–ด๋„ ๋œ๋‹ค.

Android CI ํŒŒ์ผ ์ž‘์„ฑํ•˜๊ธฐ

์ฒ˜์Œ์—๋Š” ๊ตฌ๋…์ค‘์ธ ํด๋กœ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์˜ ๋™๋ฌธ์„œ๋‹ต๋งŒ ๋‚˜์™€์„œ ๊ทธ๋ƒฅ gitlab ๊ณต์‹ ์˜ˆ์ œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Android.latest.gitlab-ci.yml

 

lib/gitlab/ci/templates/Android.latest.gitlab-ci.yml · master · GitLab.org / GitLab · GitLab

GitLab is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. Self-host GitLab on your own servers, in a...

gitlab.com

์ด๊ฑฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ–ˆ๊ณ , ์ตœ์‹  sdk์ธ 34์— ๋งž์ท„๋‹ค.

 

๋จผ์ € ํ”„๋กœ์ ํŠธ์— ๋“ฑ๋ก๋œ `local.properties`๊ฐ’๋“ค์„ Gitlab์—๋„ ์„ธํŒ…ํ•ด์ค˜์•ผํ•œ๋‹ค.

CI/CD ํƒญ์˜ Variables์—์„œ ์ž‘์„ฑํ•˜๋ฉด๋œ๋‹ค. ๋‹ค๋งŒ ๋‚˜์˜ ๊ฒฝ์šฐ์ฒ˜๋Ÿผ Mask๋ฅผ ํ™œ์„ฑํ™” ํ•  ๊ฒฝ์šฐ, value์— ํฐ ๋”ฐ์˜ดํ‘œ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๋”ฐ๊ฐ€ ymlํŒŒ์ผ ์ž‘์„ฑํ•  ๋•Œ ์ด์Šค์ผ€์ดํ”„ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผํ•œ๋‹ค.

ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •์€ ๊ฐ์ž ๋ชฉ์ ์— ๋งž๊ฒŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ๋‚˜๋Š” ๊ทธ๋ƒฅ root ๋””๋ ‰ํ† ๋ฆฌ์— ์œ„์น˜์‹œ์ผฐ๋‹ค.

์ด์ œ `.gitlab-ci.yml` ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์ž. ํŒŒ์ดํ”„๋ผ์ธ ์—๋””ํ„ฐ์—์„œ ๋ฐ”๋กœ ํ•ด๋„ ๋˜๊ณ , ๋กœ์ปฌPC์—์„œ ์ž‘์„ฑํ•ด์„œ ์˜ฌ๋ ค๋„ ๋œ๋‹ค.

image: eclipse-temurin:17-jdk-jammy

variables:
  ANDROID_COMPILE_SDK: "34"
  ANDROID_BUILD_TOOLS: "34.0.0"
  ANDROID_SDK_TOOLS: "9477386"

SDK34๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , gradle ๋ฒ„์ „์ด 8.7์ด๋ผ์„œ jdk๋ฒ„์ „๋„ 11์ด ์•„๋‹Œ 17๋กœ ์˜ฌ๋ ค์„œ ์‚ฌ์šฉํ•œ๋‹ค.

script๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์ „์— ํ”„๋กœ์ ํŠธ repository๋ฅผ ๋ณด๋ฉด, Android ํด๋”๊ฐ€ ๋”ฐ๋กœ ์กด์žฌํ•œ๋‹ค. ์ฆ‰ build๋ฅผ ์ € ํด๋”๋กœ ๋“ค์–ด๊ฐ€์„œ ์ง„ํ–‰ํ•ด์•ผํ•œ๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ before_script์— `cd Drtaa-Android`๊ฐ€ ์กด์žฌํ•œ๋‹ค.

before_script:
  - cd Drtaa-Android
  - apt-get --quiet update --yes
  - apt-get --quiet install --yes wget unzip
  - export ANDROID_HOME="${PWD}/android-sdk-root"
  - install -d $ANDROID_HOME
  - wget --no-verbose --output-document=$ANDROID_HOME/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS}_latest.zip
  - unzip -q -d "$ANDROID_HOME/cmdline-tools" "$ANDROID_HOME/cmdline-tools.zip"
  - mv -T "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/tools"
  - export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin
  - sdkmanager --version
  - yes | sdkmanager --licenses > /dev/null || true
  - sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}"
  - sdkmanager "platform-tools"
  - sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}"
  - chmod +x ./gradlew

๋‚˜๋จธ์ง€๋Š” ์˜ˆ์‹œ์— ์ ํžŒ ๋‚ด์šฉ๊ณผ ๋™์ผํ•˜๋‹ค. ์ด์ œ ๋ฌธ์ œ๋Š” variable์— ์ ์–ด๋‘” ๋ณ€์ˆ˜๋“ค์„ ๊ฐ€์ ธ์™€์„œ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์•ˆ๊ทธ๋Ÿฌ๋ฉด BuildConfig๋กœ ์„ค์ •ํ•ด๋‘” ๊ฐ’์ด ๋‹ค ์˜ค๋ฅ˜๋‚œ๋‹ค.

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” keystore๋ฅผ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ  local.properties์—์„œ ํ‚ค๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— local.properties ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ๊ฑฐ๊ธฐ์— ๊ฐ’์„ ์จ์ฃผ๋Š” ๋ฐฉ์‹์„ ์ฑ„ํƒํ–ˆ๋‹ค.

  - echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
  - echo "NAVER_MAP_CLIENT_ID=\"$NAVER_MAP_CLIENT_ID\"" >> local.properties
  - echo "NAVER_MAP_CLIENT_ID_MANIFEST=$NAVER_MAP_CLIENT_ID_MANIFEST" >> local.properties
  - echo "NAVER_MAP_CLIENT_SECRET=\"$NAVER_MAP_CLIENT_SECRET\"" >> local.properties
  - echo "NAVER_CLIENT_ID=\"$NAVER_CLIENT_ID\"" >> local.properties
  - echo "NAVER_CLIENT_SECRET=\"$NAVER_CLIENT_SECRET\"" >> local.properties
  - echo "GOOGLE_LOGIN_CLIENT_ID=\"$GOOGLE_LOGIN_CLIENT_ID\"" >> local.properties
  - echo "TOUR_API_KEY=\"$TOUR_API_KEY\"" >> local.properties
  - echo "BASE_URL=\"$BASE_URL\"" >> local.properties
  - echo "MQTT_URL=\"$MQTT_URL\"" >> local.properties
  - echo "BOOTPAY_APP_ID=\"$BOOTPAY_APP_ID\"" >> local.properties

์ด์Šค์ผ€์ดํ”„ ์ฒ˜๋ฆฌํ•ด๋‘” ๊ฐ’๋“ค์€ kotlin ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’๋“ค์ด๋‹ค. ๋‚ด ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ buildConfig์— ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’๋“ค์ด `" " `๋กœ ๊ฐ์‹ธ์ ธ์žˆ์ง€์•Š์œผ๋ฉด gradle์—์„œ ์ฝ์–ด์˜ฌ ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค. ์•„๊นŒ variable์„ ๋„ฃ์–ด์ค„๋•Œ mask๋ฅผ ํ™œ์„ฑํ™” ํ•ด๋‘ฌ์„œ ํฐ ๋”ฐ์˜ดํ‘œ๋ฅผ ๋ชป์“ฐ๊ธฐ ๋•Œ๋ฌธ์ด ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•ด์คฌ๋‹ค.

์ด ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด buildํ•  ๋•Œ๋„ `local.properties`๋ฅผ ์ƒ์„ฑํ•œ ํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.

 

์ด์ œ job์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค. ๋‚ด๊ฐ€ ํ•  ์ž‘์—…์ด assembleDebug๋ผ์„œ, ์ด๋ฆ„๋„ ๋˜‘๊ฐ™์ด ๋„ฃ์–ด์คฌ๋‹ค.

assembleDebug:
  interruptible: true
  stage: build
  script:
    - ./gradlew assembleDebug
  artifacts:
    paths:
      - Drtaa-Android/app/build/outputs/apk/debug/app-debug.apk
      - Drtaa-Android/app/build/outputs/logs/
    expire_in: 1 week

interruptible๋ฅผ true๋กœ ์ผœ์„œ ์ƒˆ๋กœ์šด ์ปค๋ฐ‹์ด ๋“ค์–ด์˜ค๋ฉด ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ job์„ ์ค‘๋‹จํ•˜๊ณ  ์ƒˆ job์„ ์‹œ์ž‘ํ•˜๋„๋กํ•œ๋‹ค. 
stage: build: ์ด job์ด 'build' ๋‹จ๊ณ„์—์„œ ์‹คํ–‰๋จ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. CI/CD ํŒŒ์ดํ”„๋ผ์ธ์˜ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
script์—๋Š” before_script๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹คํ–‰๋  ๋ช…๋ น์–ด๋ฅผ ์ •์˜ํ•˜๋Š” ๊ณณ์ด๋‹ค. ์ง€๊ธˆ์€ assembleDebug๋งŒ ์žˆ์ง€๋งŒ, detekt๊ฐ™์€ ๊ฑธ ์‹œ๋„ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.


artifacts์—๋Š” ์ด job์ด ์™„๋ฃŒ๋œ ํ›„ ๋ณด์กดํ•  ํŒŒ์ผ๋“ค์„ ์ •์˜ํ•ด์ฃผ๋Š”๋ฐ, ์ € paths ์— ์ ํžŒ ๊ฒฝ๋กœ์— ๋“ค์–ด๊ฐ€์„œ ํŒŒ์ผ์„ ๊ธ์–ด์˜จ๋‹ค. ๋‚˜๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ ์•ˆ์— ๋“ค์–ด๊ฐ€์•ผ ๋˜๋ฏ€๋กœ `Drtaa-Android/`๋ฅผ ๋„ฃ์–ด์„œ ๊ฒฝ๋กœ๋ฅผ ํ™•์‹คํ•˜๊ฒŒ ์ง€์ •ํ•ด์•ผํ•œ๋‹ค. ํ”„๋กœ์ ํŠธ ์ „์ฒด artifacts ์šฉ๋Ÿ‰์ด 1GB๋ผ์„œ ๋ณด์กด๊ธฐ๊ฐ„์„ ์ผ์ฃผ์ผ๋กœ ๋ช…์‹œํ•ด๋’€๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ์กด ๋ชฉ์ ์ด์—ˆ๋˜ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ๋ฝ‘๋Š” ๊ณผ์ •์ด ์™„์„ฑ๋œ๋‹ค. ๋ฌธ์ œ๋Š” ๋‹ค๋ฅธ ํด๋”์—์„œ ์ž‘์—…ํ•˜์—ฌ develop์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋„ ์ด pipeline์ด ๋Œ์•„๊ฐ„๋‹ค๋Š” ์ ์ด๋‹ค. github actions์—์„œ๋Š” workflow๋ฅผ ์„ค์ •ํ•ด์„œ ์ด๊ฑธ ๋ง‰์„ ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ๋Š” only ์˜ต์…˜์œผ๋กœ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. gitlab์—๋„ workflow๊ฐ€ ์žˆ์–ด์„œ stage๋ณ„ ์ œ์–ด์กฐ๊ฑด์„ ๊ฑธ ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๊ฑด ๋‚˜์ค‘์— ํ™•์ธํ•˜์ž.

https://docs.gitlab.com/ee/ci/yaml/#workflow

 

CI/CD YAML syntax reference | GitLab

GitLab product documentation.

docs.gitlab.com

 

.android_changes: &android_changes
  only:
    changes:
      - Drtaa-Android/**/*

assembleDebug:
  <<: *android_changes
  interruptible: true
  stage: build
  script:
    - ./gradlew assembleDebug
  artifacts:
    paths:
      - Drtaa-Android/app/build/outputs/apk/debug/app-debug.apk
      - Drtaa-Android/app/build/outputs/logs/
    expire_in: 1 week

์•ต์ปค๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๊ณ , ์•ˆ๋“œ๋กœ์ด๋“œ ํด๋”์—์„œ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ assembleDebug Job์„ ๋Œ์•„๊ฐ€๊ฒŒ ํ–ˆ๋‹ค. ์ด๋Ÿฌ๋ฉด ๋‹ค๋ฅธ ํด๋”์—์„œ ์ž‘์—…ํ•œ ๊ฑด assembleDebug Job์„ ์‹คํ–‰์‹œํ‚ค์ง€์•Š๋Š”๋‹ค!

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
09
17

class NaverRepositoryImpl @Inject constructor(
    private val naverDataSource: NaverDataSource
) : NaverRepository {
    override suspend fun getSearchList(keyword: String): Flow<Result<List<Search>>> = flow {
        when (
            val response = safeApiCall { naverDataSource.getSearchList(keyword) }
        ) {
            is ResultWrapper.Success -> {
                emit(
                    Result.success(response.data.items.map { it.toSearch() })
                )
                Timber.d("์„ฑ๊ณต")
            }

            is ResultWrapper.GenericError -> {
                emit(Result.failure(Exception(response.message)))
                Timber.d("์‹คํŒจ")
            }

            is ResultWrapper.NetworkError -> {
                emit(Result.failure(Exception("๋„คํŠธ์›Œํฌ ์—๋Ÿฌ")))
                Timber.d("๋„คํŠธ์›Œํฌ ์—๋Ÿฌ")
            }
        }
    }
}

๊ณ„์† ๋ฏธ๋ค„์™”๋˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ•™์Šตํ•ด๋ณด์ž.

์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์„ ๊ฑฐ์ณ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ด๋ฒˆ์—๋Š” `:core-data` ๋ชจ๋“ˆ์—์„œ๋งŒ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๊ฒƒ์ด๋‹ค.

ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉ๋  Repository ๊ตฌํ˜„์ฒด๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. Naver์˜ ๊ฒ€์ƒ‰ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” Repository์ด๋ฉฐ, safe api call์„ ๋„์ž…ํ•ด ์„ฑ๊ณต, ๋ณดํ†ต์—๋Ÿฌ, ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ 3๊ฐ€์ง€ ๋ถ„๊ธฐ๋กœ ๋‚˜๋ˆ„์—ˆ๋‹ค.

 

์ด์ œ ์‚ฌ์šฉ๋  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณด์ž.

//Test
testImplementation(libs.mockk)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.junit)

[versions]
# Test
junit = "4.13.2"
mockk = "1.13.3"
kotlinxCoroutinesTest = "1.7.1"

๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ์—†์ด ๋ฐ”๋กœ ๋กœ์ง์„ ๊ฒ€์ฆํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— mocking์„ ์œ„ํ•œ mockk๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. flow๋ฅผ ์“ฐ๊ณ ์žˆ์–ด์„œ ์ฝ”๋ฃจํ‹ด ํ™˜๊ฒฝ์—์„œ test๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋ฃจํ‹ด ํ…Œ์ŠคํŠธ๋„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. junit์€ ๊ธฐ๋ณธ์œผ๋กœ dependency์— ๋“ค์–ด๊ฐ€์žˆ๋‹ค.

Mockito๊ฐ€ ๋” ์œ ๋ช…ํ•˜์ง€๋งŒ ์ฝ”ํ‹€๋ฆฐ ํ™˜๊ฒฝ์ด๊ณ , mockk์ด ์ฝ”๋ฃจํ‹ด์„ ๋” ์ž˜ ์ง€์›ํ•ด์ค€๋‹ค๊ณ  ํ•ด์„œ mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ mockk์„ ์„ ํƒํ–ˆ๋‹ค.

 

์•„์ง ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ๋“ค์–ด๊ฐ€์ง€ ์•Š์•˜์ง€๋งŒ Mockito์™€ Mockk์„ ๋น„๊ตํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ์šฐ์„  ๋‘ ์ฝ”๋“œ ๋ชจ๋‘ `naverDataSource.getSearchList(keyword)๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด mockResponse๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ผ` ๋ผ๋Š” ์˜๋ฏธ๋‹ค.

// Mockito
`when`(naverDataSource.getSearchList(keyword)).thenReturn(mockResponse)
val result = naverRepository.getSearchList(keyword).toList()

// Mockk
coEvery { naverDataSource.getSearchList(keyword) } returns mockResponse
val result = naverRepository.getSearchList(keyword).toList()

 

๋ชจํ‚คํ† ์—์„œ ๋ฐฑํ‹ฑ์œผ๋กœ ๊ฐ์‹ธ์ง„ when์€ ๋†€๋ž๊ฒŒ๋„ ๋ฉ”์„œ๋“œ๋‹ค. ๊ฑฐ๊ธฐ์— ๋Œ€์‘๋˜๋Š” mockk์˜ ํ•จ์ˆ˜๊ฐ€ coEvery๋‹ค. co๋Š” coroutine์˜ co์ด๋ฉฐ, suspend ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค. ๋น„๊ต ๊ธ€์€ ๋งŽ์œผ๋‹ˆ๊นŒ ์ผ๋‹จ ๋„˜์–ด๊ฐ€๊ฒ ๋‹ค.

https://medium.com/@htj889/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-unit-test-mockito-kotlin-vs-mockk-%EB%8B%B9%EC%8B%A0%EC%97%90%EA%B2%8C-%EB%A7%9E%EB%8A%94-mocking-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EB%8A%94-%EF%B8%8F-a1e79bd7ce9a

 

์•ˆ๋“œ๋กœ์ด๋“œ Unit Test, Mockito-Kotlin vs MockK: ๋‹น์‹ ์—๊ฒŒ ๋งž๋Š” Mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š”? โš”๏ธ

์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ ๊ฐœ๋ฐœ์—์„œ ๋นผ๋†“์„ ์ˆ˜ ์—†๋Š” ๊ฒƒ, ๋ฐ”๋กœ Unit Test์ฃ ! ๐Ÿงช Unit Test๋ฅผ ์ž‘์„ฑํ•  ๋•Œ, ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ๊ฐ€์ง„ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด Mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€๋ฐ์š”. Kotlin ๊ฐœ๋ฐœ์ž๋“ค

medium.com

์ผ๋‹จ Test ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.

private lateinit var naverDataSource: NaverDataSource
private lateinit var naverRepository: NaverRepositoryImpl

@Before
fun setup() {
    naverDataSource = mockk()
    naverRepository = NaverRepositoryImpl(naverDataSource)
}

์ค€๋น„๊ณผ์ •์ด๋‹ค. repository์— ๋“ค์–ด๊ฐˆ ๋ฐ์ดํ„ฐ ์†Œ์Šค๊ฐ€ ํ•„์š”ํ•˜๋‹ˆ ์ด๊ฑธ mockk์„ ์‚ฌ์šฉํ•ด Mock ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ณ , naverRepository ์ธ์Šคํ„ด์Šค๋ฅผ Mock๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ค์–ด์ค€๋‹ค. `@Before` ์–ด๋…ธํ…Œ์ด์…˜์€ ๊ฐ ํ…Œ์ŠคํŠธ ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „์— ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ์ง€์ •ํ•œ๋‹ค. 

 

ResultWrapper๋กœ 3๊ฐœ๋กœ ๋‚˜๋ˆ ๋‘ฌ์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ์„ธ๊ฐœ๋กœ ๋งŒ๋“œ๋ ค๊ณ  ํ•œ๋‹ค. ์„ฑ๊ณต, ์ผ๋ฐ˜์—๋Ÿฌ, ๋„คํŠธ์›Œํฌ์—๋Ÿฌ๋กœ ๋งŒ๋“ค๊ฒ ๋‹ค. getSearchList์ž์ฒด๊ฐ€ suspend ๋ฉ”์„œ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์— runBlocking์œผ๋กœ ์„ ์–ธํ•ด์คฌ๋‹ค. ์›๋ž˜ runBlockingTest์˜€๋˜๊ฑธ๋กœ ๊ธฐ์–ตํ•˜๋Š”๋ฐ deprecated๋๋‹ค. ์ •ํ™•ํžˆ๋Š” Mockito์˜ ๊ฒฝ์šฐ์—๋Š” ์ฝ”๋ฃจํ‹ด์„ ์ง€์›ํ•˜์ง€์•Š์•„์„œ runBlockingTest๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ๊ณ , Mockk์€ ์ฝ”๋ฃจํ‹ด ์ง€์›์ด๋ผ runBlocking์œผ๋กœ ์จ๋„ ๋œ๋‹ค!

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์ „์—, `Given - When - Then` ๊ตฌ์กฐ๋ฅผ ์•Œ์•„์•ผํ•œ๋‹ค. Behavior-Driven Development (BDD) ๋ฐฉ๋ฒ•๋ก ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๊ตฌ์กฐํ™”ํ•ด์„œ ์ž‘์„ฑํ•œ๋‹ค.

https://www.devkuma.com/docs/testing/bdd/

  • Given (์ฃผ์–ด์ง„ ์ƒํ™ฉ): ํ…Œ์ŠคํŠธ๋ฅผ ์ „์ œ ์กฐ๊ฑด์„ ์„ค์ •ํ•˜๊ณ , ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ๊ฐ์ฒด, ๋ฐ์ดํ„ฐ๋“ค์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  • When (์‹คํ–‰ํ•  ๋•Œ): ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ์‹ค์ œ ๋ฉ”์„œ๋“œ๋‚˜ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  • Then (๊ฒฐ๊ณผ): ์˜ˆ์ƒ๊ฒฐ๊ณผ์™€ ์‹ค์ œ ๊ฒฐ๊ณผ๋ฅผ assert๋กœ ๋น„๊ตํ•ด์„œ ๊ฒ€์ฆํ•œ๋‹ค.

Given๊ณผ When ๋จผ์ € ๋ณด์ž.

@Test
fun `getSearchList success html tag remove`() = runBlocking {
    // Given
    val keyword = "test" // ํ•จ์ˆ˜ ํ˜ธ์ถœ์— ์‚ฌ์šฉ๋˜๋Š” ํ‚ค์›Œ๋“œ
    val mockResponse = ResponseSearch(
        display = 0,
        lastBuildDate = LocalDate.now().toString(),
        start = 0,
        total = 10,
        items = listOf(
            SearchItem(
                title = "<b>์ œ๋ชฉ์ด์š”</b>",
                link = "https://test.com",
                category = "Test Category",
                description = "Test Description",
                telephone = "123-456-7890",
                address = "Test Address",
                roadAddress = "Test Road Address",
                mapx = "123",
                mapy = "456"
            )
        )
    )
    coEvery { naverDataSource.getSearchList(keyword) } returns mockResponse

    // When
    val result = naverRepository.getSearchList(keyword).toList()
}

when์„ ์“ฐ๊ธฐ์ „์— ์ด๋ฏธ ์ด๋•Œ ์‚ฌ์šฉ๋  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๊ณผ ๋ฐ˜ํ™˜๊ฒฐ๊ณผ๋ฅผ coEvery๋กœ ์ •์˜ํ•ด๋‘”๋‹ค. ์ด๋Ÿฌ๋ฉด ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ coEvery๋กœ ์ •์˜ํ•ด๋‘” ํ–‰๋™์ด ์‹คํ–‰๋œ๋‹ค. ์ง€๊ธˆ์€ ๊ทธ๋ƒฅ ์˜ˆ์ƒ ๊ฒฐ๊ณผ๋ฅผ return ํ•ด๋†จ๋Š”๋ฐ, throw Exception์œผ๋กœ ์˜ˆ์™ธ ์ผ€์ด์Šค๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

val result = naverRepository.getSearchList(keyword).toList()

์—ฌ๊ธฐ์„œ toList๋Š” flow์˜ toList๋‹ค. flow์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜ํ•ด์ค˜์„œ first๋กœ ์ ‘๊ทผํ•˜๊ธฐ ์ข‹๋‹ค.

public suspend fun <T> Flow<T>.toList(destination: MutableList<T> = ArrayList()): List<T> = toCollection(destination)

์ด์ œ ํ•จ์ˆ˜ํ˜ธ์ถœ๋„ ํ–ˆ๊ณ , ๋ฏธ๋ฆฌ ๋ฐ˜ํ™˜๊ฐ’๋„ ์ •ํ•ด๋’€์œผ๋‹ˆ assert๋กœ ๋น„๊ตํ•  ์ผ๋งŒ ๋‚จ์•˜๋‹ค.

// Then
assertTrue(result.size == 1)
assertTrue(result[0].isSuccess)
val searchList = result.first().getOrNull()
assertEquals(1, searchList?.size)
assertEquals("์ œ๋ชฉ์ด์š”", searchList?.first()?.title)
  • 1. ๋‚ด๊ฐ€ ๋ฆฌ์ŠคํŠธ์— ํ•œ ๊ฐœ์˜ ๊ฐ์ฒด๋งŒ ๋„ฃ์—ˆ์œผ๋‹ˆ resultํฌ๊ธฐ๋Š” 1์ด์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.
  • 2. kotlin.Result๋กœ ๊ฐ์‹ธ๋’€์œผ๋‹ˆ ์ฒซ๋ฒˆ์งธ ์‘๋‹ต์ด isSuccess์ธ์ง€๋„ ๊ฒ€์‚ฌํ•œ๋‹ค.
  • 3. isSuccess๊ฐ€ true๋ฉด ๋‹ค์Œ ์ค„์—์„œ data๋ฅผ ๋ฝ‘์•„์ค€๋‹ค.

์ด๊ฒŒ ์ค‘์š”ํ•œ๋ฐ Result๋กœ ๊ฐ์‹ผ๊ฒฝ์šฐ์— getOrNull์„ ์“ฐ๋ฉด ์•„๋ž˜ ๋กœ์ง์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

 fold(onSuccess = { it }, onFailure = { null })

๊ทธ๋ž˜์„œ searchList์—๋Š” ํฌ๊ธฐ 1์งœ๋ฆฌ List<SearchItem> ์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ๋ฆฌ์ŠคํŠธ ํฌ๊ธฐ ๊ฒ€์ฆ๋„ ํ•ด์ฃผ๊ณ , ์›ํ•˜๋Š” ๊ฒฐ๊ณผ์™€ ๋งž๋Š” ์ง€ ๋น„๊ต๋„ ํ•ด์ค€๋‹ค.

๊ทธ๋Ÿผ ์ด๊ฒŒ pass๊ฐ€ ์•ˆ๋ ๋•Œ๋Š” ์–ด๋–ค ๊ฒฝ์šฐ์ผ๊นŒ?

is ResultWrapper.Success -> {
    emit(
        Result.success(response.data.items.map { it.toSearch() })
    )
    Timber.d("์„ฑ๊ณต")
}

mapper์ธ `toSearch` ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์žˆ๋‹ค.

fun SearchItem.toSearch(): Search {
    return Search(
        title = this.title.removeHtmlTags(),
        category = this.category.removeTextBeforeArrow(),
        roadAddress = this.roadAddress,
        lng = this.mapx.toDouble() / 10000000,
        lat = this.mapy.toDouble() / 10000000
    )
}

title์—์„œ html ํƒœ๊ทธ๋ฅผ ์ง€์›Œ์ฃผ๋Š” ์ž‘์—…์„ ํ•ด์ฃผ๋Š”๋ฐ, ์ด๊ฑธ ๋นผ๊ณ  ๋Œ๋ ค๋ณด์ž.

Tests failed๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

 

์—๋Ÿฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๊ฑฐ์˜ ๋˜‘๊ฐ™์œผ๋‹ˆ ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ๋งŒ ๊ทธ๋ƒฅ ์ฝ”๋“œ๋ฅผ ๋‘๊ณ  ๊ฐ€๊ฒ ๋‹ค.

@Test
fun `getSearchList network error`() = runBlocking {
    // Given
    val keyword = "test"
    coEvery { naverDataSource.getSearchList(keyword) } throws Exception("๋„คํŠธ์›Œํฌ ์—๋Ÿฌ")

    // When
    val result = naverRepository.getSearchList(keyword).toList()

    // Then
    assertTrue(result.size == 1)
    assertTrue(result[0].isFailure)
    assertEquals("๋„คํŠธ์›Œํฌ ์—๋Ÿฌ", result[0].exceptionOrNull()?.message)
}

coEvery๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•จ์ˆ˜ํ˜ธ์ถœํ–‰์œ„์— ๋Œ€ํ•ด Exception ๋˜์ง€๋Š” ํ–‰์œ„๋กœ ์ •์˜ํ•ด์คฌ๋‹ค.

# ๊ตฌํ˜„์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๊ฑฐ์Šฌ๋ฆฐ๋‹ค...

Hilt๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Test์šฉ Module์„ ๋งŒ๋“ค๊ณ , ๊ทธ๊ฑธ ์ฃผ์ž…๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค. hilt-android-test์™€ robolectric์ด ํ•„์š”ํ•˜๋‹ค. hilt๊ฐ€ android ํ™˜๊ฒฝ์„ ๊ธฐ๋Œ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Robolectric์„ ์‚ฌ์šฉํ•ด์ค˜์•ผํ•œ๋‹ค. ์‚ฌ์‹ค ๊ทธ๋ƒฅ AndroidJunit์œผ๋กœ ํ•ด๋„ ๋˜๋Š”๋ฐ, ๋‚˜์ค‘์— ์—๋ฎฌ๋ ˆ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ๋„ ๊ฐ€๋Šฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— Roblectric์„ ์„ ํƒํ–ˆ๋‹ค.

 

๋จผ์ € ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด ์ค„ ๊ฒƒ์ด๋‹ค. ์ค‘๋ณต ๋ฐ”์ธ๋”ฉ์„ ๋ง‰๊ธฐ์œ„ํ•ด ๊ผญ ํ•ด์ค˜์•ผํ•œ๋‹ค.

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [DataSourceModule::class]
)
object TestDataSourceModule {
    @Provides
    @Singleton
    fun provideNaverDataSource(): NaverDataSource = mockk()
}

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [RepositoryModule::class]
)
object TestRepositoryModule {
    @Provides
    @Singleton
    fun provideNaverRepository(dataSource: NaverDataSource): NaverRepository =
        NaverRepositoryImpl(dataSource)
}

์ด ๋ชจ๋“ˆ๋“ค์€ test ํŒจํ‚ค์ง€ ๋ฐ‘์— ์กด์žฌํ•œ๋‹ค. ์—ฌ๊ธฐ ์œ„์น˜์‹œํ‚ค์ง€์•Š์œผ๋ฉด @TestInstallIn์„ importํ•˜์ง€ ๋ชปํ•˜๋Š” ํ˜„์ƒ์„ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ ๋ชจ๋“ˆ ๋Œ€์‹  ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ์ง€๊ธˆ ์ •์˜ํ•œ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์˜๋ฏธ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ NaverRepositoryImplTest๋กœ ๋Œ์•„๊ฐ€ ์•„๋ž˜ ์ฝ”๋“œ๋“ค์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋๋‚œ๋‹ค.

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(RobolectricTestRunner::class)
class NaverRepositoryImplTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var naverRepository: NaverRepository
    @Inject
    lateinit var naverDataSource: NaverDataSource

    @Before
    fun setup() {
        hiltRule.inject()
        clearAllMocks()
    }
    
    // ... ๋™์ผ
}

TestRunner๋กœ ๊ธฐ๋ณธ์ด AndroidJunit์ธ๋ฐ, HiltAndroidTest๋ฅผ ์‚ฌ์šฉํ–ˆ๋”๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜์˜จ๋‹ค.

No instrumentation registered! Must run under a registering instrumentation.

๊ธฐ๋ณธ ๋Ÿฌ๋„ˆ๋ฅผ ๋ชป์žก์•„์„œ ์ด๋ ‡๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค. @RunWith์œผ๋กœ ๋Ÿฌ๋„ˆ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ํ•ด๊ฒฐ๋œ๋‹ค.

@RunWith(RobolectricTestRunner::class)

@Inject๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด ์ต์ˆ™ํ•˜๊ณ , Rule๋ถ€๋ถ„์„ ๋ณด๊ฒ ๋‹ค.

@get:Rule
var hiltRule = HiltAndroidRule(this)

ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „์— Hilt์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์ค€๋‹ค. ์ด๋Œ€๋กœ ์“ฐ๋ฉด ์•ˆ๋˜๊ณ  @Before์—์„œ inject()๋ฅผ ํ•ด์ค˜์•ผ ํ•„๋“œ ์ฃผ์ž…์ด ์‹คํ–‰๋œ๋‹ค.

 

`clearAllMocks`๋Š” ํŠน๋ณ„ํ•  ๊ฒŒ ์—†๋‹ค. ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ๋ณตํ•ฉ์ ์œผ๋กœ ๋Œ๋ฆด ๊ฒฝ์šฐ ์ด๋ฏธ ์ƒ์„ฑ๋œ Mock ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ๊นŒ ์ดˆ๊ธฐํ™” ํ•ด์ฃผ๋Š” ๋‹จ๊ณ„๋‹ค.

 

์ง์ ‘ ๊ตฌํ˜„์ฒด๋ฅผ ์•ˆ์“ฐ๊ณ  ์ฃผ์ž…๋ฐ›์•„ ์“ฐ๋‹ˆ ํ›จ์”ฌ ํŽธ์•ˆํ•˜๋‹ค... ๊ทธ ๋Œ€์‹  hilt์—†์ด ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ธ์„ ๋•Œ ๋ณด๋‹ค ์‚ด์ง ๋Š๋ ค์ง„ ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

# ์™œ ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ• ๊นŒ?

์‚ฌ์‹ค ๋‚˜๋„ ์ด๋ฒˆ์ด ํ•™์Šตํ•˜๊ฒŒ ์ฒ˜์Œ์ด๋ผ ์•„์ง ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์ค‘์š”์„ฑ์„ ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€๋Š” ๋ชปํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  repository ๊ตฌํ˜„์ฒด ์ž์ฒด๋„ ๋‹จ์ˆœ api call์— ๊ฐ€๊นŒ์›Œ์„œ mocking ํ•˜๋Š” ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์‹ค์ œ ๋„คํŠธ์›Œํฌ๋ฅผ ํƒ€๊ณ  ๋“ค์–ด์˜ค๋Š” ๊ฑด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ง€๊ธˆ ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ ๋„ˆ๋ฌด ์ข๋‹ค. ์ถ”ํ›„ ์‹ค์ œ ๋„คํŠธ์›Œํฌ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋„ ์ž‘์„ฑํ•ด์•ผ๊ฒ ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๋ถ„๋ฆฌ๋œ network ๋ชจ๋“ˆ์— ์ ‘๊ทผํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ๋„์ „์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

 

usecase์˜ ์„ฑ๊ฒฉ์„ ๋„๋Š” ๊ณณ์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ฆฌํŒฉํ† ๋งํ•  ๋•Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ, ์ด๊ฑด ๋‚˜์ค‘์— ๋˜ ๋„์ „ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

 

TDD๊ฐ€ ํšจ์œจ์ ์ธ์ง€๋„ ์•„์ง์€ ์˜๋ฌธ์ด๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์˜ ๊ฒฝ์šฐ์—๋Š” ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ป”ํ•œ ์ฝ”๋“œ๊ฐ€ ๋‚˜์™”์ง€๋งŒ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ๋Š” repository ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ ์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ ํ•ด๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋‹ค๋ผ๋Š” ์ƒ๊ฐ์€ ๋“ ๋‹ค.

 

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT