App/Kotlin

간단한 단어퀴즈 앱 만들기

원2 2025. 3. 24. 15:29
728x90
반응형

앱의 전체적인 흐름과 구조

 

앱이 시작되는 과정

MainActivity.kt (진입점)
NavGraph.kt (네비게이션 / 화면간 이동)
Jetpack compose를 사용해 화면을 구성

 

 


 

프로젝트 구조

com.example.wordapp: 앱의 진입점
MainActivity.kt (앱의 시작점)
package com.example.wordapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.navigation.compose.rememberNavController
import com.example.wordapp.navigation.NavGraph
import com.example.wordapp.ui.theme.WordAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            WordApp()
        }
    }
}

@Composable
fun WordApp() {
    WordAppTheme {
        val navController = rememberNavController()
        NavGraph(navController = navController)
    }
}
class MainActivity : ComponentActivity(): MainActivity  - 안드로이드의 기본 클래스인 ComponentActivity를 상속받음, 
Jetpack Compose를 사용할 수 있도록 도와주는 기본 클래스
 
override fun onCreate(savedInstanceState: Bundle?) 앱이 시작될 때 실행되는 함수
savedInstanceState - 앱이 이전에 종료된 상태를 복원할 때 사용하는 데이터
setContent { WordApp() } - 여기서 Jetpack compose를 사용해서 그림
setContent - Compose로 화면을 설정하는 함수
{ WordApp() } - WordApp이라는 함수를 호출해서 화면을 구성하라는 것
@Composable - 해당 함수가 화면을 그리는 데 사용된다는 것
 
WordAppTheme { ... } - 앱의 테마 설정 Theme.kt에서 정의된 테마 사용
 
val navController = rememberNavController() - 화면 간 이동을 관리하는 도구
 
navController - 네비 컨트롤러, 화면 간 이동
 
NavGraph.kt (화면 간 이동 관리)
package com.example.wordapp.navigation

import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.wordapp.ui.quiz.QuizScreen
import com.example.wordapp.ui.word_list.WordListScreen

@Composable
fun NavGraph(navController: NavHostController) {
    NavHost(navController = navController, startDestination = "word_list") {
        composable("word_list") { WordListScreen(navController) }
        composable("quiz") { QuizScreen(navController) }
    }
}

 

 

com.example.wordapp.data: 데이터를 관리하는 파일들

Word.kt (단어 데이터 정의)
package com.example.wordapp.data

data class Word(
    val english: String,
    val korean: String
)
WordRepository.kt (단어 데이터 제공)
package com.example.wordapp.data

class WordRepository {
    private val dummyWords = listOf(
        Word(english = "Apple", korean = "사과"),
        Word(english = "Dog", korean = "개"),
        Word(english = "Book", korean = "책")
    )

    fun getAllWords(): List<Word> = dummyWords
}
list Of( . . .) - 코틀린에서 리스트를 만드는 함수
fun getAllWords(): List<Word> = dummyWords - 단어 목록을 반환
 
com.example.wordapp.ui: 화면과 관련된 파일들
 
ui.common: 여러 화면에서 재사용할 수 있는 컴포넌트
WordCard.kt (단어 카드 컴포넌트)
package com.example.wordapp.ui.common

import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.wordapp.data.Word

@Composable
fun WordCard(word: Word) {
    val isFlipped = remember { mutableStateOf(false) }
    Card(
        modifier = Modifier
            .padding(8.dp)
            .clickable { isFlipped.value = !isFlipped.value }
            .animateContentSize()
    ) {
        Text(
            text = if (isFlipped.value) word.korean else word.english,
            fontSize = 24.sp,
            textAlign = TextAlign.Center,
            modifier = Modifier.padding(16.dp)
        )
    }
}
val isFlipped = remember { mutableStateOf(false) }: isFlipped는 카드가 뒤집혔는지 여부를 저장하는 변수
mutableStateOf(false)는 초기값이 false
remember는 화면이 다시 그려져도 이 값을 기억
ui.word_list: 단어 목록 화면 관련 파일
WordListScreen.kt (단어 목록 화면)
package com.example.wordapp.ui.word_list

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.wordapp.ui.common.WordCard

@Composable
fun WordListScreen(navController: NavHostController, viewModel: WordListViewModel = viewModel()) {
    LazyColumn {
        items(viewModel.words) { word ->
            WordCard(word = word)
        }
        item {
            Button(
                onClick = { navController.navigate("quiz") },
                modifier = Modifier.padding(16.dp)
            ) {
                Text("퀴즈 시작", fontSize = 16.sp)
            }
        }
    }
}

앱의 첫 화면, 단어 목록을 보여주는 화면

 
ui.quiz: 퀴즈 화면 관련 파일
WordListViewModel.kt (단어 목록 데이터 관리)
package com.example.wordapp.ui.word_list

import androidx.lifecycle.ViewModel
import com.example.wordapp.data.Word
import com.example.wordapp.data.WordRepository

class WordListViewModel(private val repository: WordRepository = WordRepository()) : ViewModel() {
    val words: List<Word> = repository.getAllWords()
}
class WordListViewModel(...) : ViewModel(): ViewModel은 안드로이드에서 데이터를 관리하는 데 사용하는 클래스. 화면이 꺼졌다 켜져도 데이터를 유지
 
QuizScreen.kt (퀴즈 화면)
package com.example.wordapp.ui.quiz

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController

@Composable
fun QuizScreen(navController: NavHostController, viewModel: QuizViewModel = viewModel()) {
    val currentWord = viewModel.currentWord.collectAsState() //현재 표시할 단어를 저장 collectAsState()- 데이터를 실시간으로 화면에 반영
    Column( // 세로로 컴포넌트 배치
        modifier = Modifier.fillMaxSize(), //화면 전체를 채움
        verticalArrangement = Arrangement.Center, //세로방향 가운데 정렬
        horizontalAlignment = Alignment.CenterHorizontally //가로 방향 가운데 정렬
    ) {
        currentWord.value?.let { //currentWord.value가 null이 아니면 실행 ?.let은 값이 null이 아닐 때만 실행하는 코틀린문법
            Text(it.english, fontSize = 28.sp)
            Button(
                onClick = { viewModel.nextWord() },
                modifier = Modifier.padding(16.dp)
            ) {
                Text("다음 단어", fontSize = 16.sp)
            }
        }
        Button(
            onClick = { navController.popBackStack() }, //popBackStack() 이전 화면으로 돌아감
            modifier = Modifier.padding(16.dp)
        ) {
            Text("돌아가기", fontSize = 16.sp)
        }
    }
}

 

QuizViewModel.kt (퀴즈 데이터 관리)
package com.example.wordapp.ui.quiz

import androidx.lifecycle.ViewModel
import com.example.wordapp.data.Word
import com.example.wordapp.data.WordRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class QuizViewModel(private val repository: WordRepository = WordRepository()) : ViewModel() {
    private val _currentWord = MutableStateFlow<Word?>(null) // 현재 단어를 저장하는 변수
    val currentWord: StateFlow<Word?> = _currentWord // 데이터 외부 공개, StateFlow는 값을 실시간으로 화면에 반영
    private val words = repository.getAllWords()
    private var index = 0

    init {
        nextWord()
    }

    fun nextWord() {
        if (index < words.size) {
            _currentWord.value = words[index]
            index++
        } else {
            _currentWord.value = null
        }
    }
}

 

com.example.wordapp.navigation: 화면 간 이동을 관리
NavGraph.kt
package com.example.wordapp.navigation

import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.wordapp.ui.quiz.QuizScreen
import com.example.wordapp.ui.word_list.WordListScreen

@Composable
fun NavGraph(navController: NavHostController) {
    println("navController = ${navController}")
    NavHost(navController = navController, startDestination = "word_list") {
        composable("word_list") { WordListScreen(navController) }
        composable("quiz") { QuizScreen(navController) }
    }
}
com.example.wordapp.ui.theme: 앱의 테마 설정

 

Theme.kt (앱 테마 설정)
package com.example.wordapp.ui.theme

import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable

@Composable
fun WordAppTheme(content: @Composable () -> Unit) {
    MaterialTheme(
        content = content
    )
}
 

 

이런 허졉한 단어 어플 완성zz

 

 

코틀린 초보자를 위한 팁.feat GPT (본인)

  • 코틀린의 valvar:
    • val은 값을 변경할 수 없는 변수야. 예를 들어, val words = repository.getAllWords()words 값을 나중에 변경할 수 없어.
    • var은 값을 변경할 수 있는 변수야. var index = 0index 값을 나중에 index++로 변경할 수 있어.
  • Compose의 @Composable 함수:
    • @Composable 함수는 화면을 그리는 데 사용돼. 예를 들어, WordListScreen, WordCard, QuizScreen은 모두 화면을 그리는 함수야.
    • 이 함수들은 화면을 새로 그릴 때마다 다시 호출돼.
  • Modifier:
    • Modifier는 Compose에서 컴포넌트의 스타일을 설정하는 도구야. padding, clickable 같은 설정을 추가할 수 있어.
  • 네비게이션:
    • navController는 화면 간 이동을 관리해. navigate("quiz")는 "quiz"라는 경로로 이동하라는 뜻이고, popBackStack()은 이전 화면으로 돌아가라는 뜻이야.

 

728x90
반응형

'App > Kotlin' 카테고리의 다른 글

Scaffold, 상단 앱 바 및 스크롤 설정  (0) 2025.03.25
옵트인(Opt-in)  (0) 2025.03.25
코틀린 시작하기  (1) 2025.03.21