Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import java.util.Properties

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
Expand All @@ -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\"")

Comment on lines +23 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

BASE_URL null 처리 필요

local.properties 파일이 없거나 BASE_URL 속성이 설정되지 않은 경우 baseUrl 변수가 null이 되어 빌드 실패가 발생합니다.

다음 diff를 적용하여 null 처리를 추가하세요:

 // defaultConfig 블록 안에 추가
 val properties = Properties()
 val localPropertiesFile = rootProject.file("local.properties")
 if (localPropertiesFile.exists()) {
     properties.load(localPropertiesFile.inputStream())
 }
-val baseUrl = properties.getProperty("BASE_URL")
+val baseUrl = properties.getProperty("BASE_URL") ?: "https://default-api-url.com/"
 buildConfigField("String", "BASE_URL", "\"$baseUrl\"")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 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\"")
// defaultConfig 블록 안에 추가
val properties = Properties()
val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
properties.load(localPropertiesFile.inputStream())
}
val baseUrl = properties.getProperty("BASE_URL") ?: "https://default-api-url.com/"
buildConfigField("String", "BASE_URL", "\"$baseUrl\"")
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 23 to 31, the code reads BASE_URL from
local.properties but does not handle the case where local.properties is missing
or BASE_URL is unset, causing baseUrl to be null and the build to fail; update
the code to guard against null by providing a safe default or failing fast with
a clear error: after reading properties, check if
properties.getProperty("BASE_URL") is null or blank and either assign a sensible
default string (e.g., empty string or a placeholder) before passing to
buildConfigField, or throw a GradleException with a clear message that BASE_URL
is required; ensure the value passed to buildConfigField is properly quoted so
it remains a valid String literal in the generated BuildConfig.

}

buildTypes {
Expand All @@ -38,6 +49,7 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
}

Expand Down Expand Up @@ -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)
}
23 changes: 19 additions & 4 deletions app/src/main/java/com/example/kuit6_android_api/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
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
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
Expand All @@ -32,6 +38,7 @@ class MainActivity : ComponentActivity() {
}
}

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -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
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<PostResponse>>

@POST("/api/posts")
suspend fun createPost(
@Query("author") author: String = "시헌",
@Body request: PostCreateRequest
): BaseResponse<PostResponse>

@DELETE("/api/posts/{id}")
suspend fun deletePost(
@Path("id") id: Long
): BaseResponse<Unit> // data에 빈 객체 반환

@GET("/api/posts/{id}")
suspend fun getPostDetail(
@Path("id") id: Long
): BaseResponse<PostResponse>

// 업데이트 요청
@PUT("/api/posts/{id}")
suspend fun updatePost(
@Path("id") id: Long,
@Body request: PostCreateRequest // 수정된 내용 전달
): BaseResponse<PostResponse>

@Multipart
@POST("/api/images/upload")
suspend fun uploadImage(
@Part file: MultipartBody.Part
): BaseResponse<Map<String, String>>
}
Original file line number Diff line number Diff line change
@@ -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
}
Comment on lines +12 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

프로덕션 환경에서 로깅 레벨 주의

HttpLoggingInterceptor.Level.BODY는 요청/응답의 전체 본문을 로그에 기록하여 민감한 정보(토큰, 개인정보 등)가 노출될 수 있습니다. 프로덕션 빌드에서는 로깅을 비활성화하거나 레벨을 낮춰야 합니다.

다음과 같이 빌드 타입에 따라 로깅 레벨을 조정하세요:

 private val loggingInterceptor = HttpLoggingInterceptor().apply {
-    level = HttpLoggingInterceptor.Level.BODY
+    level = if (BuildConfig.DEBUG) {
+        HttpLoggingInterceptor.Level.BODY
+    } else {
+        HttpLoggingInterceptor.Level.NONE
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
🤖 Prompt for AI Agents
In app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt
around lines 12 to 14, the HttpLoggingInterceptor is set to Level.BODY which
logs full request/response bodies and can expose sensitive data in production;
change the implementation to set the interceptor level conditionally based on
the build type (e.g., if (BuildConfig.DEBUG) use BODY or BASIC else NONE) or
remove the interceptor entirely for release builds, ensuring production builds
do not log sensitive payloads while keeping detailed logs for debug builds.


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)
}
17 changes: 0 additions & 17 deletions app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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?
)
Original file line number Diff line number Diff line change
@@ -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?
Comment on lines +3 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

직렬화 라이브러리 불일치 문제

이 파일도 동일한 직렬화 라이브러리 불일치 문제가 있습니다.

Gson 사용 시:

-import kotlinx.serialization.SerialName
+import com.google.gson.annotations.SerializedName
 
 data class AuthorResponse (
-    @SerialName(value = "id") val id : Long,
-    @SerialName(value = "username") val username: String,
-    @SerialName(value = "profileImageUrl") val profileImageUrl:String?
+    @SerializedName("id") val id : Long,
+    @SerializedName("username") val username: String,
+    @SerializedName("profileImageUrl") val profileImageUrl: String?
 )
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/model/response/AuthResponse.kt
around lines 3–8, the file mixes kotlinx.serialization import with Gson-style
usage and also has a class/name mismatch (file AuthResponse.kt vs class
AuthorResponse). Fix by choosing one serialization library: if using Gson,
replace kotlinx.serialization.SerialName import with
com.google.gson.annotations.SerializedName and update annotations to
@SerializedName(...); if using kotlinx.serialization, add
@kotlinx.serialization.Serializable to the data class and keep @SerialName
imports. Also rename the class to match the file (AuthResponse) and ensure
nullable types and property names remain correct.

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.kuit6_android_api.data.model.response

import kotlinx.serialization.SerialName

data class BaseResponse<T>(
@SerialName(value = "success") val success : Boolean,
@SerialName(value = "message") val message : String?,
@SerialName(value = "data") val data: T?,
@SerialName(value = "timestamp") val timestamp: String
Comment on lines +3 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

직렬화 라이브러리 불일치 문제

PostResponse.kt와 동일하게, kotlinx.serialization 어노테이션이 사용되었지만 Retrofit은 Gson을 사용하도록 구성되어 있습니다.

PostResponse.kt의 리뷰 코멘트에서 제안한 해결 방법을 이 파일에도 동일하게 적용하세요.

Gson 사용 시:

-import kotlinx.serialization.SerialName
+import com.google.gson.annotations.SerializedName
 
 data class BaseResponse<T>(
-    @SerialName(value = "success") val success : Boolean,
-    @SerialName(value = "message") val message : String?,
-    @SerialName(value = "data") val data: T?,
-    @SerialName(value = "timestamp") val timestamp: String
+    @SerializedName("success") val success : Boolean,
+    @SerializedName("message") val message : String?,
+    @SerializedName("data") val data: T?,
+    @SerializedName("timestamp") val timestamp: String
 )

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt
around lines 3 to 9, the class uses kotlinx.serialization annotations while the
project is configured to use Gson; replace the kotlinx.serialization import and
@SerialName annotations with Gson's com.google.gson.annotations.SerializedName
(or remove annotations if field names already match JSON), update the import to
com.google.gson.annotations.SerializedName, and ensure the annotation values
match the JSON keys ("success", "message", "data", "timestamp").

)
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +3 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

직렬화 라이브러리 불일치 문제

이 파일은 kotlinx.serialization@SerialName 어노테이션을 사용하지만, RetrofitClient.kt에서는 GsonConverterFactory를 사용하고 있습니다. 이는 런타임에 역직렬화 실패를 일으킵니다.

해결 방법 1 (권장): Gson 어노테이션 사용

 package com.example.kuit6_android_api.data.model.response
 
-import kotlinx.serialization.SerialName
+import com.google.gson.annotations.SerializedName
 
 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
+    @SerializedName("id") val id : Long,
+    @SerializedName("title") val title: String,
+    @SerializedName("content") val content : String,
+    @SerializedName("imageUrl") val imageUrl: String?,
+    @SerializedName("author") val author: AuthorResponse,
+    @SerializedName("createdAt") val createdAt: String,
+    @SerializedName("updatedAt") val updatedAt: String
 )

해결 방법 2: kotlinx.serialization 컨버터 사용

RetrofitClient.kt에서 컨버터를 변경:

// build.gradle.kts에 추가
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")

// RetrofitClient.kt
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))

동일한 수정이 BaseResponse.ktAuthResponse.kt에도 필요합니다.

🤖 Prompt for AI Agents
app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt
lines 3-12: the file uses kotlinx.serialization @SerialName while Retrofit is
configured with Gson, causing runtime deserialization failures; either (A)
replace kotlinx.serialization annotations with Gson's @SerializedName on
PostResponse (and similarly update BaseResponse.kt and AuthResponse.kt) so they
match the Gson converter, or (B) switch Retrofit to the kotlinx.serialization
converter by adding the retrofit kotlinx-serialization converter dependency and
configuring Retrofit to use
Json.asConverterFactory("application/json".toMediaType()) so the existing
@SerialName annotations work; apply the same change consistently for
BaseResponse.kt and AuthResponse.kt.

)
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand All @@ -40,7 +43,8 @@ fun NavGraph(
},
onEditClick = { postId ->
navController.navigate(PostEditRoute(postId))
}
},
snackBarState = snackBarState
)
}

Expand All @@ -51,7 +55,8 @@ fun NavGraph(
},
onPostCreated = {
navController.popBackStack()
}
},
snackBarState = snackBarState
)
}

Expand All @@ -65,7 +70,8 @@ fun NavGraph(
},
onPostUpdated = {
navController.popBackStack()
}
},
snackBarState = snackBarState
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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"
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uri?>(null) }
val scope = rememberCoroutineScope()

val imagePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
Expand Down Expand Up @@ -187,9 +195,10 @@ fun PostCreateScreen(

Button(
onClick = {
val finalAuthor = author.ifBlank { "anonymous" }
val finalAuthor = author
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

작성자 필드 유효성 검증을 추가하세요.

작성자 필드가 비어있을 수 있습니다. 기존에는 "anonymous" 기본값이 있었으나 제거되었습니다. API 엔드포인트에서 기본값("시헌")을 사용하는 것으로 보이지만, 사용자가 입력한 빈 문자열이 의도대로 처리되는지 확인하세요.


🏁 Script executed:

#!/bin/bash
# Description: ApiService에서 author 파라미터 처리 방식 확인

# createPost 메서드에서 author 파라미터 기본값 및 처리 확인
ast-grep --pattern $'suspend fun createPost(
  @Query("author") author: String$$$
)$$$'

Length of output: 0


추가 검증을 위해 리포지토리를 탐색하겠습니다.




---

🏁 Script executed:

```shell
#!/bin/bash
# Find ApiService file and examine author parameter handling
fd -e "\.kt$" | xargs grep -l "fun createPost" | head -5

# Also find PostCreateScreen.kt
fd -name "PostCreateScreen.kt"

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Search for createPost method definition with context
rg -n "suspend fun createPost" -A 5 -B 2 --type=kotlin

Length of output: 799


🏁 Script executed:

#!/bin/bash
# Check PostCreateScreen.kt for author field handling around line 198
cat "app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt" | sed -n '190,210p'

Length of output: 780





---

🏁 Script executed:

```shell
#!/bin/bash
# Find author variable declaration and initialization in PostCreateScreen.kt
rg -n "var author|val author" "app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt" -B 2 -A 2

Length of output: 207


🏁 Script executed:

#!/bin/bash
# Check the viewModel.createPost method signature
rg -n "fun createPost" "app/src/main/java/com/example/kuit6_android_api" -A 3 -B 2 --type=kotlin | grep -v "ApiService"

Length of output: 617


🏁 Script executed:

#!/bin/bash
# Get more context around line 198 in PostCreateScreen.kt - check lines 1-100 to find author initialization
sed -n '1,100p' "app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt" | grep -n "author"

Length of output: 52


작성자 필드 유효성 검증이 필요합니다.

작성자 필드(author)가 라인 62에서 빈 문자열("")로 초기화되고, 라인 198의 버튼 활성화 조건에서 제목과 내용만 검증합니다(title.isNotBlank() && content.isNotBlank()). 사용자가 작성자를 입력하지 않은 채 게시글을 작성할 수 있으며, 빈 문자열이 API에 명시적으로 전달되면 서버의 기본값("시헌")이 적용되지 않습니다.

조치: 다음 중 하나를 선택하세요:

  • 버튼 활성화 조건에 author.isNotBlank() 추가
  • 또는 사용자 입력 전 author에 기본값 설정
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt
around lines 62 and 198, the author field is initialized to an empty string at
line 62 but the submit button enablement at line 198 only checks title and
content, allowing empty author to be sent and bypass server default; either
require author input or apply a client-side default. Fix by choosing one: (A)
include author.isNotBlank() in the button enabled condition so the button stays
disabled until author is provided, or (B) set finalAuthor just before submit to
use the server default when author.isBlank() (e.g., replace empty author with
"시헌") and keep UI behavior unchanged; implement one of these options and ensure
the request payload never contains an empty author string.

viewModel.createPost(finalAuthor, title, content, null) {
onPostCreated()
scope.launch { snackBarState.showSnackbar("게시글이 작성되었습니다.") }
}
},
modifier = Modifier
Expand Down Expand Up @@ -226,7 +235,8 @@ fun PostCreateScreenPreview() {
MaterialTheme {
PostCreateScreen(
onNavigateBack = {},
onPostCreated = {}
onPostCreated = {},
snackBarState = remember { SnackbarHostState() }
)
}
}
Loading