-
Notifications
You must be signed in to change notification settings - Fork 10
6주차 미션 / 안드로이드 2조 서아영 #19
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
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.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| 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 { | ||
| // 게시글 목록 조회 | ||
| // http://3.34.136.227:8080/api/posts | ||
| @GET(value = "/api/posts") | ||
| suspend fun getPosts(): BaseResponse<List<PostResponse>> // suspend fun: 일시 중단 될 수도 있는 함수 | ||
|
|
||
| // 게시글 생성 | ||
| // http://3.34.136.227:8080/api/posts | ||
| @POST(value = "/api/posts") | ||
| suspend fun createPost( | ||
| @Query(value = "author") author: String = "anonymous", | ||
| @Body request: PostCreateRequest | ||
| ): BaseResponse<PostResponse> | ||
|
|
||
| // 게시글 상세 조회 | ||
| @GET(value = "/api/posts/{id}") | ||
| suspend fun getDetail( | ||
| @Path("id") id: Long | ||
| ): BaseResponse<PostResponse> | ||
|
|
||
| // 게시글 수정 [스낵바 - 6주차 미션] | ||
| @PUT(value = "/api/posts/{id}") | ||
| suspend fun updatePost( | ||
| @Path(value = "id") id: Long, | ||
| @Body request: PostCreateRequest | ||
| ): BaseResponse<PostResponse> | ||
|
|
||
| // 게시글 삭제 [스낵바 - 6주차 미션] | ||
| @DELETE(value = "/api/posts/{id}") | ||
| suspend fun deletePost( | ||
| @Path("id") id: Long | ||
| ): BaseResponse<Unit> // 서버가 data 없으면 Unit/Any? 로 받기 | ||
|
|
||
| // 이미지 업로드 | ||
| @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 | ||
| } | ||
|
|
||
| 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() | ||
|
Comment on lines
+23
to
+27
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. 직렬화 라이브러리 불일치 문제를 해결해야 합니다.
해결 방법 1 (권장): kotlinx-serialization 컨버터 사용
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")RetrofitClient 수정: package com.example.kuit6_android_api.data.api
import com.example.kuit6_android_api.BuildConfig
+import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
+import kotlinx.serialization.json.Json
+import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object RetrofitClient {
+ private val json = Json {
+ ignoreUnknownKeys = true
+ coerceInputValues = true
+ }
+
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())
+ .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
val apiService: ApiService = retrofit.create(ApiService::class.java)
}그리고 모든 데이터 클래스에 해결 방법 2: 모든 데이터 모델을 Gson 어노테이션으로 변경 모든 데이터 모델의 🤖 Prompt for AI Agents |
||
|
|
||
| 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,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? // nullable | ||
| ) |
| 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? // nullable | ||
| ) | ||
|
Comment on lines
+5
to
+9
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. @serializable 어노테이션이 누락되었습니다.
다음 중 하나를 선택하여 수정해야 합니다:
해결 방법 1 (권장): kotlinx-serialization 사용
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")데이터 클래스 수정: package com.example.kuit6_android_api.data.model.response
import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+@Serializable
data class AuthorResponse(
@SerialName(value = "id") val id: Long,
@SerialName(value = "username") val username: String,
@SerialName(value = "profileImageUrl") val profileImageUrl: String?
)그리고 해결 방법 2: Gson 사용 package com.example.kuit6_android_api.data.model.response
-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 |
||
| 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?, //nullable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @SerialName(value = "timestamp") val timestamp: String | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+10
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. @serializable 어노테이션이 누락되었습니다.
kotlinx-serialization 사용 시: package com.example.kuit6_android_api.data.model.response
import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+@Serializable
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
)Gson 사용 시: package com.example.kuit6_android_api.data.model.response
-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
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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?, // nullable | ||
| @SerialName(value = "author") val author: AuthorResponse, | ||
| @SerialName(value = "createdAt") val createdAt: String, | ||
| @SerialName(value = "updatedAt") val updatedAt: String | ||
| ) |
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 null 안전성 문제를 해결해야 합니다.
local.properties에BASE_URL속성이 없으면baseUrl이 null이 되어 빌드 에러가 발생합니다. 주석 처리된 기본값을 보아 fallback 값을 고려했던 것으로 보입니다.다음과 같이 수정하여 null 안전성을 확보하세요:
val properties = Properties() val localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { properties.load(localPropertiesFile.inputStream()) } - val baseUrl = properties.getProperty("BASE_URL")// ?: "http://10.0.2.2:8080/" + val baseUrl = properties.getProperty("BASE_URL") ?: "http://10.0.2.2:8080/" buildConfigField("String", "BASE_URL", "\"$baseUrl\"")또는 더 명확하게 에러를 발생시키려면:
📝 Committable suggestion
🤖 Prompt for AI Agents