diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0c6dbbc..c07b6da 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -18,6 +20,15 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + // defaultConfig 블록 안에 추가 + val properties = Properties() + val localPropertiesFile = rootProject.file("local.properties") + if (localPropertiesFile.exists()) { + properties.load(localPropertiesFile.inputStream()) + } + val baseUrl = properties.getProperty("BASE_URL") + buildConfigField("String", "BASE_URL", "\"$baseUrl\"") + } buildTypes { @@ -38,6 +49,7 @@ android { } buildFeatures { compose = true + buildConfig = true } } @@ -68,10 +80,16 @@ dependencies { // Coroutines implementation(libs.kotlinx.coroutines.android) + implementation(libs.retrofit2.retrofit) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.test.manifest) + + //retro & okhttp + implementation(libs.retrofit.converter.gson) + implementation(libs.okhttp) + implementation(libs.okhttp.logging.interceptor) } diff --git a/app/src/main/java/com/example/kuit6_android_api/MainActivity.kt b/app/src/main/java/com/example/kuit6_android_api/MainActivity.kt index 5129dff..b2e9835 100644 --- a/app/src/main/java/com/example/kuit6_android_api/MainActivity.kt +++ b/app/src/main/java/com/example/kuit6_android_api/MainActivity.kt @@ -1,6 +1,7 @@ package com.example.kuit6_android_api import android.Manifest +import android.annotation.SuppressLint import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -8,9 +9,14 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.core.content.ContextCompat import androidx.navigation.compose.rememberNavController @@ -32,6 +38,7 @@ class MainActivity : ComponentActivity() { } } + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,15 +47,23 @@ class MainActivity : ComponentActivity() { setContent { KUIT6_Android_APITheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background + + val snackBarState = remember { SnackbarHostState() } + + Scaffold( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + snackbarHost = { + SnackbarHost(snackBarState) + } ) { val navController = rememberNavController() NavGraph( navController = navController, - startDestination = PostListRoute + startDestination = PostListRoute, + snackBarState = snackBarState ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt b/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt new file mode 100644 index 0000000..1399be5 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt @@ -0,0 +1,41 @@ +package com.example.kuit6_android_api.data.api + +import com.example.kuit6_android_api.data.model.response.BaseResponse +import com.example.kuit6_android_api.data.model.response.PostResponse +import com.example.kuit6_android_api.data.model.request.PostCreateRequest +import okhttp3.MultipartBody +import retrofit2.http.* + +interface ApiService { + @GET("/api/posts") + suspend fun getPosts(): BaseResponse> + + @POST("/api/posts") + suspend fun createPost( + @Query("author") author: String = "시헌", + @Body request: PostCreateRequest + ): BaseResponse + + @DELETE("/api/posts/{id}") + suspend fun deletePost( + @Path("id") id: Long + ): BaseResponse // data에 빈 객체 반환 + + @GET("/api/posts/{id}") + suspend fun getPostDetail( + @Path("id") id: Long + ): BaseResponse + + // 업데이트 요청 + @PUT("/api/posts/{id}") + suspend fun updatePost( + @Path("id") id: Long, + @Body request: PostCreateRequest // 수정된 내용 전달 + ): BaseResponse + + @Multipart + @POST("/api/images/upload") + suspend fun uploadImage( + @Part file: MultipartBody.Part + ): BaseResponse> +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt b/app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt new file mode 100644 index 0000000..897b8b7 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt @@ -0,0 +1,30 @@ +package com.example.kuit6_android_api.data.api + +import com.example.kuit6_android_api.BuildConfig +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitClient { + + private val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + private val okHttpClient = OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build() + + private val retrofit: Retrofit = Retrofit.Builder() + .baseUrl(BuildConfig.BASE_URL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + val apiService: ApiService = retrofit.create(ApiService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt b/app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt deleted file mode 100644 index e4c1d08..0000000 --- a/app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.kuit6_android_api.data.model - -data class Author( - val id: Long, - val username: String, - val profileImageUrl: String? -) - -data class Post( - val id: Long, - val title: String, - val content: String, - val imageUrl: String?, - val author: Author, - val createdAt: String, - val updatedAt: String -) diff --git a/app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt b/app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt new file mode 100644 index 0000000..c3cece2 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt @@ -0,0 +1,9 @@ +package com.example.kuit6_android_api.data.model.request + +import kotlinx.serialization.SerialName + +data class PostCreateRequest( + @SerialName(value = "title") val title:String, + @SerialName(value = "content") val content:String, + @SerialName(value = "imageUrl") val imageUrl:String? +) \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/model/response/AuthResponse.kt b/app/src/main/java/com/example/kuit6_android_api/data/model/response/AuthResponse.kt new file mode 100644 index 0000000..7bc828a --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/model/response/AuthResponse.kt @@ -0,0 +1,9 @@ +package com.example.kuit6_android_api.data.model.response + +import kotlinx.serialization.SerialName + +data class AuthorResponse ( + @SerialName(value = "id") val id : Long, + @SerialName(value = "username") val username: String, + @SerialName(value = "profileImageUrl") val profileImageUrl:String? +) \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt b/app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt new file mode 100644 index 0000000..2f626a0 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt @@ -0,0 +1,10 @@ +package com.example.kuit6_android_api.data.model.response + +import kotlinx.serialization.SerialName + +data class BaseResponse( + @SerialName(value = "success") val success : Boolean, + @SerialName(value = "message") val message : String?, + @SerialName(value = "data") val data: T?, + @SerialName(value = "timestamp") val timestamp: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt b/app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt new file mode 100644 index 0000000..a9d18cc --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt @@ -0,0 +1,13 @@ +package com.example.kuit6_android_api.data.model.response + +import kotlinx.serialization.SerialName + +data class PostResponse( + @SerialName(value = "id") val id : Long, + @SerialName(value = "title") val title:String, + @SerialName(value = "content") val content : String, + @SerialName(value = "imageUrl") val imageUrl:String?, + @SerialName(value = "author") val author: AuthorResponse, + @SerialName(value = "createdAt") val createdAt:String, + @SerialName(value = "updatedAt") val updatedAt:String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt index 07aa322..cbcdbd0 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt @@ -1,6 +1,8 @@ package com.example.kuit6_android_api.ui.navigation +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -13,7 +15,8 @@ import com.example.kuit6_android_api.ui.post.screen.PostListScreen @Composable fun NavGraph( navController: NavHostController, - startDestination: Any = PostListRoute + startDestination: Any = PostListRoute, + snackBarState: SnackbarHostState ) { NavHost( navController = navController, @@ -40,7 +43,8 @@ fun NavGraph( }, onEditClick = { postId -> navController.navigate(PostEditRoute(postId)) - } + }, + snackBarState = snackBarState ) } @@ -51,7 +55,8 @@ fun NavGraph( }, onPostCreated = { navController.popBackStack() - } + }, + snackBarState = snackBarState ) } @@ -65,7 +70,8 @@ fun NavGraph( }, onPostUpdated = { navController.popBackStack() - } + }, + snackBarState = snackBarState ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt index df2e5fe..b5344a8 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt @@ -29,13 +29,13 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import com.example.kuit6_android_api.data.model.Author -import com.example.kuit6_android_api.data.model.Post +import com.example.kuit6_android_api.data.model.response.AuthorResponse +import com.example.kuit6_android_api.data.model.response.PostResponse import com.example.kuit6_android_api.util.formatDateTime @Composable fun PostItem( - post: Post, + post: PostResponse, onClick: () -> Unit ) { Card( @@ -153,12 +153,12 @@ fun PostItem( fun PostItemPreview() { MaterialTheme { PostItem( - post = Post( + post = PostResponse( id = 1, title = "샘플 게시글 제목", content = "이것은 샘플 게시글 내용입니다. 미리보기에서는 두 줄까지만 표시됩니다.", imageUrl = null, - author = Author(1, "testuser", null), + author = AuthorResponse(1, "testuser", null), createdAt = "2025-10-03T12:00:00", updatedAt = "2025-10-03T12:00:00" ), diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt index 57ea2ab..07ec04f 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt @@ -29,33 +29,41 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun PostCreateScreen( onNavigateBack: () -> Unit, onPostCreated: () -> Unit, + snackBarState: SnackbarHostState, viewModel: PostViewModel = viewModel() ) { + val context = LocalContext.current var author by remember { mutableStateOf("") } var title by remember { mutableStateOf("") } var content by remember { mutableStateOf("") } var selectedImageUri by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() val imagePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent() @@ -187,9 +195,10 @@ fun PostCreateScreen( Button( onClick = { - val finalAuthor = author.ifBlank { "anonymous" } + val finalAuthor = author viewModel.createPost(finalAuthor, title, content, null) { onPostCreated() + scope.launch { snackBarState.showSnackbar("게시글이 작성되었습니다.") } } }, modifier = Modifier @@ -226,7 +235,8 @@ fun PostCreateScreenPreview() { MaterialTheme { PostCreateScreen( onNavigateBack = {}, - onPostCreated = {} + onPostCreated = {}, + snackBarState = remember { SnackbarHostState() } ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt index 6635ced..4c1dd61 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -34,6 +35,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -46,6 +48,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel import com.example.kuit6_android_api.util.formatDateTime +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -53,10 +56,12 @@ fun PostDetailScreen( postId: Long, onNavigateBack: () -> Unit, onEditClick: (Long) -> Unit = {}, - viewModel: PostViewModel = viewModel() + viewModel: PostViewModel = viewModel(), + snackBarState: SnackbarHostState ) { val post = viewModel.postDetail var showDeleteDialog by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() LaunchedEffect(postId) { viewModel.getPostDetail(postId) @@ -197,6 +202,7 @@ fun PostDetailScreen( viewModel.deletePost(postId) { showDeleteDialog = false onNavigateBack() + scope.launch { snackBarState.showSnackbar("게시글이 삭제되었습니다.") } } }) { Text("삭제") @@ -219,7 +225,8 @@ fun PostDetailScreenPreview() { PostDetailScreen( postId = 1L, onNavigateBack = {}, - onEditClick = {} + onEditClick = {}, + snackBarState = remember { SnackbarHostState() } ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt index 0e5eaca..23a991c 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.ui.tooling.preview.Preview @@ -36,6 +37,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -45,6 +47,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -52,7 +55,8 @@ fun PostEditScreen( postId: Long, onNavigateBack: () -> Unit, onPostUpdated: () -> Unit, - viewModel: PostViewModel = viewModel() + viewModel: PostViewModel = viewModel(), + snackBarState: SnackbarHostState ) { val post = viewModel.postDetail @@ -60,6 +64,7 @@ fun PostEditScreen( var content by remember { mutableStateOf("") } var selectedImageUri by remember { mutableStateOf(null) } var isLoaded by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() val imagePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent() @@ -204,6 +209,7 @@ fun PostEditScreen( onClick = { viewModel.updatePost(postId, title, content, null) { onPostUpdated() + scope.launch { snackBarState.showSnackbar("게시글이 수정되었습니다.") } } }, modifier = Modifier @@ -235,7 +241,8 @@ fun PostEditScreenPreview() { PostEditScreen( postId = 1L, onNavigateBack = {}, - onPostUpdated = {} + onPostUpdated = {}, + snackBarState = remember { SnackbarHostState() } ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt index 444229c..d245bd7 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt @@ -1,96 +1,72 @@ package com.example.kuit6_android_api.ui.post.viewmodel import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.kuit6_android_api.data.model.Author -import com.example.kuit6_android_api.data.model.Post -import kotlinx.coroutines.delay +import com.example.kuit6_android_api.data.api.RetrofitClient +import com.example.kuit6_android_api.data.model.request.PostCreateRequest +import com.example.kuit6_android_api.data.model.response.PostResponse import kotlinx.coroutines.launch import java.time.LocalDateTime class PostViewModel : ViewModel() { - - // 더미 데이터 - private val dummyPosts = mutableStateListOf( - Post( - id = 1, - title = "Jetpack Compose 시작하기", - content = "Jetpack Compose는 Android의 최신 UI 툴킷입니다. 선언형 UI로 더 쉽고 빠르게 UI를 만들 수 있습니다.", - imageUrl = null, - author = Author(1, "개발자A", null), - createdAt = "2025-10-05T10:00:00", - updatedAt = "2025-10-05T10:00:00" - ), - Post( - id = 2, - title = "Kotlin Coroutines 완벽 가이드", - content = "비동기 프로그래밍을 쉽게! Coroutines를 사용하면 복잡한 비동기 코드를 간단하게 작성할 수 있습니다.", - imageUrl = null, - author = Author(2, "개발자B", null), - createdAt = "2025-10-05T11:30:00", - updatedAt = "2025-10-05T11:30:00" - ), - Post( - id = 3, - title = "Android MVVM 아키텍처", - content = "MVVM 패턴으로 코드를 구조화하면 테스트와 유지보수가 쉬워집니다. ViewModel과 LiveData/StateFlow를 활용해봅시다.", - imageUrl = null, - author = Author(1, "개발자A", null), - createdAt = "2025-10-05T14:20:00", - updatedAt = "2025-10-05T14:20:00" - ) - ) - - private var nextId = 4L - - var posts by mutableStateOf>(emptyList()) + var posts by mutableStateOf>(emptyList()) private set - var postDetail by mutableStateOf(null) + var postDetail by mutableStateOf(null) private set var uploadedImageUrl by mutableStateOf(null) private set + private val apiService = RetrofitClient.apiService fun getPosts() { viewModelScope.launch { - delay(500) // 네트워크 시뮬레이션 - posts = dummyPosts.toList() + runCatching { + apiService.getPosts() + }.onSuccess { response -> + response.data?.let { + if (response.success){ + posts = response.data + } + } + } } } fun getPostDetail(postId: Long) { viewModelScope.launch { - delay(300) - postDetail = dummyPosts.find { it.id == postId } + runCatching { + apiService.getPostDetail(postId) + }.onSuccess { response -> + response.data?.let{ + if(response.success) + postDetail = response.data + } + } } } fun createPost( - author: String = "anonymous", + author: String, title: String, content: String, imageUrl: String? = null, onSuccess: () -> Unit = {} ) { viewModelScope.launch { - delay(500) - val newPost = Post( - id = nextId++, - title = title, - content = content, - imageUrl = imageUrl, - author = Author(nextId, author, null), - createdAt = getCurrentDateTime(), - updatedAt = getCurrentDateTime() - ) - dummyPosts.add(0, newPost) - posts = dummyPosts.toList() - onSuccess() + runCatching { + val request = PostCreateRequest(title, content, imageUrl) + apiService.createPost(author, request) + }.onSuccess { response -> + if (response.success) { + // 이미지 업로드 관련 코드 + clearUploadedImageUrl() + onSuccess() + } + } } } @@ -102,30 +78,31 @@ class PostViewModel : ViewModel() { onSuccess: () -> Unit = {} ) { viewModelScope.launch { - delay(500) - val index = dummyPosts.indexOfFirst { it.id == postId } - if (index != -1) { - val oldPost = dummyPosts[index] - val updatedPost = oldPost.copy( - title = title, - content = content, - imageUrl = imageUrl, - updatedAt = getCurrentDateTime() - ) - dummyPosts[index] = updatedPost - postDetail = updatedPost - posts = dummyPosts.toList() - onSuccess() + runCatching { + val request = PostCreateRequest(title,content,imageUrl) + apiService.updatePost(postId, request) + }.onSuccess { response -> + if(response.success && response.data != null){ + posts = posts.map{ + if(it.id == postId) response.data else it + } + postDetail = response.data // postDetail을 갱신된 객체로 바꾸기 + onSuccess() + } } } } fun deletePost(postId: Long, onSuccess: () -> Unit = {}) { viewModelScope.launch { - delay(300) - dummyPosts.removeIf { it.id == postId } - posts = dummyPosts.toList() - onSuccess() + runCatching { + apiService.deletePost(postId) + }.onSuccess { response -> + if (response.success) { + posts = posts.filterNot { it.id == postId } + onSuccess() + } + } } }