-
Notifications
You must be signed in to change notification settings - Fork 10
6주차 미션 / 안드로이드 1조 정일혁 #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a7f4b51
49b5ffc
cb36cf2
bc6f534
9ecbd44
a047a58
c62340d
7b4f7b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| kotlin version: 2.0.21 | ||
| error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output: | ||
| 1. Kotlin compile daemon is ready | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "java.configuration.updateBuildConfiguration": "automatic" | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,9 +8,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 | ||
|
|
@@ -40,15 +45,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(hostState = snackBarState) | ||
| } | ||
| ) { | ||
| val navController = rememberNavController() | ||
|
|
||
| NavGraph( | ||
| navController = navController, | ||
| startDestination = PostListRoute | ||
| startDestination = PostListRoute, | ||
| snackBarState = snackBarState | ||
| ) | ||
| } | ||
|
Comment on lines
+49
to
66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scaffold의 content padding을 적용하세요.
다음과 같이 수정하세요: - ) {
+ ) { paddingValues ->
val navController = rememberNavController()
NavGraph(
navController = navController,
startDestination = PostListRoute,
- snackBarState = snackBarState
+ snackBarState = snackBarState,
+ modifier = Modifier.padding(paddingValues)
)
}참고:
🤖 Prompt for AI Agents |
||
| } | ||
|
|
@@ -71,6 +84,7 @@ class MainActivity : ComponentActivity() { | |
| ) == PackageManager.PERMISSION_GRANTED -> { | ||
| // 이미 권한이 있음 | ||
| } | ||
|
|
||
| shouldShowRequestPermissionRationale(permission) -> { | ||
| // 권한 거부 이력이 있음 - 설명 표시 후 재요청 | ||
| Toast.makeText( | ||
|
|
@@ -80,6 +94,7 @@ class MainActivity : ComponentActivity() { | |
| ).show() | ||
| requestPermissionLauncher.launch(permission) | ||
| } | ||
|
|
||
| else -> { | ||
| // 권한 요청 | ||
| requestPermissionLauncher.launch(permission) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package com.example.kuit6_android_api.data.api | ||
|
|
||
| import com.example.kuit6_android_api.data.model.request.PostCreateRequest | ||
| import com.example.kuit6_android_api.data.model.response.BaseResponse | ||
| import com.example.kuit6_android_api.data.model.response.PostResponse | ||
| import okhttp3.MultipartBody | ||
| import retrofit2.http.Body | ||
| import retrofit2.http.DELETE | ||
| import retrofit2.http.GET | ||
| import retrofit2.http.Multipart | ||
| import retrofit2.http.POST | ||
| import retrofit2.http.PUT | ||
| import retrofit2.http.Part | ||
| import retrofit2.http.Path | ||
| import retrofit2.http.Query | ||
|
|
||
| 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> | ||
|
|
||
| // 게시글 상세 조회 | ||
| @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> | ||
|
|
||
| // 게시글 삭제 | ||
| @DELETE("/api/posts/{id}") | ||
| suspend fun deletePost( | ||
| @Path("id") id: Long | ||
| ): BaseResponse<Unit> | ||
|
|
||
| // 이미지 업로드 | ||
| @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,36 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.example.kuit6_android_api.data.api | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.kuit6_android_api.BuildConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import kotlinx.serialization.json.Json | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import okhttp3.MediaType.Companion.toMediaType | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import okhttp3.OkHttpClient | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import okhttp3.logging.HttpLoggingInterceptor | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import retrofit2.Retrofit | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import retrofit2.converter.kotlinx.serialization.asConverterFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||
| 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() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+13
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로그 레벨을 운영 빌드에서 낮춰주세요. Line [13]~[22]에서 HttpLoggingInterceptor를 항상 BODY로 두면 운영 빌드에서 민감한 요청/응답 본문이 로그에 남고, 큰 payload에서는 성능까지 악화됩니다. 디버그 빌드에서만 BODY를 사용하고 운영에서는 NONE으로 낮춰주세요. - 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
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| private val json = Json { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ignoreUnknownKeys = true // 서버에서 추가 필드가 와도 무시 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| coerceInputValues = true // null이 와야 할 곳에 다른 값이 와도 처리 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| private val retrofit: Retrofit = Retrofit.Builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .baseUrl(BuildConfig.BASE_URL) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .client(okHttpClient) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .build() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| val apiService: ApiService = retrofit.create(ApiService::class.java) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.example.kuit6_android_api.data.model.request | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class PostCreateRequest( | ||
| @SerialName("title") val title: String, | ||
| @SerialName("content") val content: String, | ||
| @SerialName("imageUrl") val imageUrl: String? | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.example.kuit6_android_api.data.model.response | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class AuthorResponse( | ||
| @SerialName("id") val id: Long, | ||
| @SerialName("username") val username: String, | ||
| @SerialName("profileImageUrl") val profileImageUrl: String? | ||
| ) | ||
|
Comment on lines
+1
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파일명과 클래스명의 불일치를 수정하세요. 파일명은 파일명을 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BASE_URL 누락 시 런타임 크래시가 발생합니다.
Line [23]~[29]에서
local.properties에 BASE_URL이 없으면buildConfigField에"null"이 들어가고,Retrofit.Builder().baseUrl("null")호출 시 즉시 예외가 터집니다. 빌드 단계에서 명시적으로 검증하거나 기본값을 강제해 주세요.📝 Committable suggestion
🤖 Prompt for AI Agents