๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ’ป/Swemo

Swemo - Iteration 4: MVP 1 ๊ตฌํ˜„ (1/3)

interfacer_han 2026. 4. 1. 14:32

#1 ๊ฐœ๋ฐœ ๋ชฉํ‘œ (Iteration Goal)

#1-1 ์ด์ƒ(Ideal)๊ณผ ์ง•๊ฒ€๋‹ค๋ฆฌ

์ด์ƒ(Ideal)

๋‚ด๊ฐ€ ์ง์ ‘ ์“ฐ๊ณ  ์‹ถ์€ ์•ฑ์ด ์•„๋‹Œ๋ฐ, ๋‚จ์—๊ฒŒ ์–ด๋–ป๊ฒŒ ๊ถŒ์œ ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์•ž์œผ๋กœ ์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ์˜ ์•„์ด๋””์–ด๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด์„œ๋“ , ๊ฐœ๋ฐœ ์˜์š•์„ ์œ„ํ•ด์„œ๋“ , ๋ฒ„๊ทธ ๋ฐœ๊ฒฌ์„ ์œ„ํ•ด์„œ๋“  ์ œ์ž‘์ž์˜ ์‹ค์‚ฌ์šฉ์€ ํ•„์ˆ˜๋‹ค.


์ง€๊ธˆ๊นŒ์ง€์˜ ์ž‘์—… ๊ฒฐ๊ณผ๋ฌผ์„ ํ† ๋Œ€๋กœ, ๋‹น์žฅ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ์„ ๋งŒ๋“ ๋‹ค. ๋ฉ”๋ชจ์žฅ ๋Œ€์šฉ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ๋งŒ ํ•˜๋ฉด ๊ทธ๋งŒ์ด๋ฏ€๋กœ, ์™„์„ฑ๋„์—๋Š” ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„์•ผ ํ•  ๊ฒƒ์ด๋‹ค. Categoryใ†Memoใ†MemoContent ๊ฐ๊ฐ์— ๋Œ€ํ•œ CRUD๋งŒ์„ ๊ตฌํ˜„ํ•œ๋‹ค.

 

Iteration 4: MVP 1 ๊ตฌํ˜„ (1/3)

ํ˜„์žฌ๊นŒ์ง€ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ์ค‘๊ฐ„ ๋งˆ๋ฌด๋ฆฌํ•˜์—ฌ ์ฒซ ๋ฒˆ์งธ MVP๋ฅผ ๋งŒ๋“ ๋‹ค. Iteration 4์—์„œ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ถ”๊ฐ€ ๋ฐ ํ•ด๋‹น DB์— ๋ฉ”๋ชจ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ๊นŒ์ง€๋งŒ ๊ตฌํ˜„ํ•œ๋‹ค. ์ด์–ด์ง€๋Š” Iteration 5์—์„œ ๋‚˜๋จธ์ง€ ๊ธฐ๋Šฅ๋“ค๊นŒ์ง€ ๊ตฌํ˜„ํ•˜๊ณ  Iteration 6์—์„œ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง๊นŒ์ง€ ํ•œ ํ›„์—, ์ฒซ ๋ฒˆ์งธ MVP๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•˜๊ฒ ๋‹ค.

 

#1-2 ๊ฐœ๋ฐœ ๋ฒ”์œ„

์ด๋ฒˆ ๊ฐœ๋ฐœ ๋ฒ”์œ„ (In Scope)

  1. ์‹ค์ œ Database ๊ตฌํ˜„
  2. ๋ฉ”๋ชจ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ
  3. MVP๋ฅผ ์œ„ํ•œ UI ์—…๋ฐ์ดํŠธ
    1. IME์— ๋ฐ˜์‘ํ•˜๋Š” UI
    2. LabelChipGroup ์ œ๊ฑฐ
      • ์•„์ด๋””์–ด ์ž์ฒด๋ฅผ ํ๊ธฐํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๋‹น์žฅ์˜ MVP ๊ตฌํ˜„์„ ์œ„ํ•ด ์ œ๊ฑฐ
      • Iteration 4์—์„œ ์ œ๊ฑฐ ํ›„ ๋‚˜์ค‘์— ๋‹ค์‹œ ์ถ”๊ฐ€

 

๋ฒ”์œ„ ์™ธ (Out of Scope)

  1. ๋ฉ”๋ชจ ์‚ญ์ œ ๊ธฐ๋Šฅ
  2. ๋ฉ”๋ชจ ์† MemoContent ๊ฐœ๋ณ„ ์‚ญ์ œ ๊ธฐ๋Šฅ
  3. ๋ฉ”๋ชจ ์ˆ˜์ • ๊ธฐ๋Šฅ
  4. ์นดํ…Œ๊ณ ๋ฆฌ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ
  5. ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ๊ธฐ๋Šฅ
  6. ์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„ ์ˆ˜์ • ๊ธฐ๋Šฅ

 

#2 ๋ณ€๊ฒฝ ๋‚ด์—ญ (์ปค๋ฐ‹ ๋ชฉ๋ก)

#2-1 Iteration 4-1: IME์— ๋ฐ˜์‘ํ•˜๋Š” UI

์‹ค์‚ฌ์šฉ์„ ์œ„ํ•ด์„œ, ์šฐ์„  UI๊ฐ€ IME์— ๋ฐ˜์‘ํ•˜์ง€ ์•Š๋Š” ์ ์„ ์ˆ˜์ •ํ•œ๋‹ค.

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • MVP๋ฅผ ์œ„ํ•œ UI ์—…๋ฐ์ดํŠธ
    • IME์— ๋ฐ˜์‘ํ•˜๋Š” UI

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • App์˜ Window๊ฐ€ IME์— ์˜ํ–ฅ๋ฐ›์ง€ ์•Š๋„๋ก ์„ค์ •
  • MemoEditor๊ฐ€ IME์— ๋งž์ถฐ ์›€์ง์ด๋„๋ก ๊ตฌํ˜„
  • Iteration 1-3์—์„œ ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ ๋’€๋˜ topBar ์† ๋ฒ„ํŠผ์„ ์ œ๊ฑฐํ•˜๊ณ , ํ•ด๋‹น ๋ฒ„ํŠผ์ด ์ง€๋…”๋˜ ๊ธฐ๋Šฅ์„ MemoEditor ์† "Send" ๋ฒ„ํŠผ์— ํ• ๋‹น

 

์Šคํฌ๋ฆฐ์ƒท

 

์ฐธ๊ณ 

 

#2-2 Iteration 4-2: LabelChipGroup ๋“ฑ ์ œ๊ฑฐ

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

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • MVP๋ฅผ ์œ„ํ•œ UI ์—…๋ฐ์ดํŠธ
    • LabelChipGroup ์ œ๊ฑฐ

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • MemoFeed์˜ ์•„์ดํ…œ์ด ์•„๋ž˜์—์„œ ์œ„๋กœ ์˜ฌ๋ผ์˜ค๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ
  • MemoEditor์—์„œ "-" ๋ฒ„ํŠผ ์ œ๊ฑฐ
  • LabelChipGroup ์ œ๊ฑฐ
  • MemoContentTextField ๋””์ž์ธ ์‚ด์ง ๋‹ค๋“ฌ์Œ

 

์Šคํฌ๋ฆฐ์ƒท

 

์ฝ”๋“œ ์Šค๋‹ˆํŽซ - MemoFeed

package com.example.memo.components

...

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MemoFeed(
    memos: List<Memo>,
    modifier: Modifier
) {
    LazyColumn(
        modifier = modifier,
        reverseLayout = true,
    ) {
        items(memos.asReversed()) { memo ->
            Spacer(modifier = Modifier.height(4.dp))
            MemoCard(memo = memo)
        }
    }
}

reverseLayout = true ๋กœ ๋ ˆ์ด์•„์›ƒ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋’ค์ง‘๊ณ , ๊ทธ ๋’ค์ง‘ํžŒ ์ปจํ…Œ์ด๋„ˆ์— ์•„์ดํ…œ์„ ์—ญ์ˆœ(asReversed)์œผ๋กœ ์ค€๋‹ค. ์ด๋Ÿฌ๋ฉด ์ตœ์‹  ๋ฉ”๋ชจ์ผ์ˆ˜๋ก ์•„๋ž˜์— ์žˆ๊ณ  ์ตœ์ดˆ ์Šคํฌ๋กค๋ฐ”์˜ ์œ„์น˜๋„ ๋งจ ์•„๋ž˜์—์„œ ์‹œ์ž‘ํ•˜๋Š”, ์ด๋ฅธ๋ฐ” ์ฑ„ํŒ… UI๊ฐ€ ๋œ๋‹ค.

 

์ฐธ๊ณ 

 

#2-3 Iteration 4-3: MemoContent ์ถ”๊ฐ€ ๋ฒ„ํŠผ ๊ตฌํ˜„

  • MVP๋ฅผ ์œ„ํ•œ UI ์—…๋ฐ์ดํŠธ๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค
  • MemoEditor๋ฅผ ํ†ตํ•ด FakeMemoRepository์— ๋ฉ”๋ชจ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • MVP๋ฅผ ์œ„ํ•œ UI ์—…๋ฐ์ดํŠธ
  • ๋ฉ”๋ชจ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • MemoEditor ์†์— MemoContent ์ถ”๊ฐ€ ๋ฒ„ํŠผ ๊ตฌํ˜„
  • MemoEditor๋กœ ๋ฉ”๋ชจ ์ž…๋ ฅ → (FakeMemoRepository์—) ๋ฉ”๋ชจ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„

 

์Šคํฌ๋ฆฐ์ƒท

 

์ฐธ๊ณ 

 

#2-4 Iteration 4-4: id ํ”„๋กœํผํ‹ฐ์˜ ๋ฐ์ดํ„ฐํ˜• ๋ณ€๊ฒฝ

  • ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๋ฏ€๋กœ, id๋Š” ์„œ๋ฒ„ ํ™˜๊ฒฝ์ฒ˜๋Ÿผ String ํƒ€์ž…์œผ๋กœ ๋‘˜ ํ•„์š”๊ฐ€ ํฌ์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. ์˜คํžˆ๋ ค ์ „ํ†ต์ ์ธ ๋ฐฉ์‹๋Œ€๋กœ ์ˆซ์žํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ์ €์žฅ, ์กฐํšŒ, ๊ด€๋ฆฌ ์ธก๋ฉด์—์„œ ๋” ์ ์ ˆํ•˜๋‹ค๊ณ  ๋ณธ๋‹ค.
  • MemoContent๋Š” Memo์— ์ข…์†๋œ ํ•ญ๋ชฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ id ํ”„๋กœํผํ‹ฐ๋ฅผ ๋‘์ง€ ์•Š์•˜๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•ž์œผ๋กœ ๋‹ค๋ฅธ Memo๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ธฐ๋Šฅ ๋“ฑ์ด ์ถ”๊ฐ€๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค๊ณ  ํŒ๋‹จํ•ด, ์„ ์ œ์ ์œผ๋กœ id ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
    • ์‹๋ณ„์ž๊ฐ€ ๋’ค๋Šฆ๊ฒŒ ํ•„์š”ํ•ด์ง€๋ฉด ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ + ์‚ฌ์šฉ์ฒ˜ ์ „๋ฐ˜์„ ํ•จ๊ป˜ ์ˆ˜์ •ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์–ด, ์—ฌ๊ธฐ์—์„œ ๋ฏธ๋ฆฌ ๋ฐ˜์˜ํ•˜๋Š” ํŽธ์ด ๋” ์•ˆ์ •์ ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • ์‹ค์ œ Database ๊ตฌํ˜„

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • Model ํด๋ž˜์Šค๋“ค์˜ String ํ˜• "id" ํ”„๋กœํผํ‹ฐ์˜ ๋ฐ์ดํ„ฐํ˜•์„ Long ํ˜•์œผ๋กœ ๋ณ€๊ฒฝ
  • MemoContent์— (Long ํ˜•) "id" ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€

 

์ฐธ๊ณ 

 

#2-5 Iteration 4-5: database ๋ชจ๋“ˆ ๊ตฌํ˜„

database ๋ชจ๋“ˆ์„ ์„ ์–ธ ํ›„ ๊ฐ„๋‹จํ•˜๊ณ  ์ •์„์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • ์‹ค์ œ Database ๊ตฌํ˜„

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • database ๋ชจ๋“ˆ ์ถ”๊ฐ€ ๋ฐ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌํ˜„
  • .gitignore ์—…๋ฐ์ดํŠธ

 

์ฐธ๊ณ 

 

#2-6 Iteration 4-6: ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๋™

Iteration 4-5์—์„œ ๋งŒ๋“  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๊ด€๋ จ ๊ฐœ๋ฐœ ๋ฒ”์œ„

  • ๋ฉ”๋ชจ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

 

์ฃผ์š” ๋ณ€๊ฒฝ์ 

  • FakeMemoRepository → FakeMemoRepositoryImpl๋กœ ์ด๋ฆ„ ๋ณ€๊ฒฝ
  • database ๋ชจ๋“ˆ์„ ์˜์กดํ•˜๋Š” MemoRepositoryImpl ๊ตฌํ˜„
  • MemoViewModel์— FakeMemoRepositoryImpl ๋Œ€์‹  MemoRepositoryImpl ์ฃผ์ž…

 

์Šคํฌ๋ฆฐ์ƒท

 

์ฐธ๊ณ