-
Notifications
You must be signed in to change notification settings - Fork 10
8주차 미션 / 안드로이드 1조 박경민 #29
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
base: koongmai
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.example.kuit6_android_api | ||
|
|
||
| import android.app.Application | ||
| import com.example.kuit6_android_api.data.di.AppContainer | ||
|
|
||
| class App : Application() { | ||
| lateinit var container: AppContainer | ||
|
|
||
| override fun onCreate() { | ||
| super.onCreate() | ||
| container = AppContainer() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,19 @@ | ||
| package com.example.kuit6_android_api.data.api | ||
|
|
||
| import android.R.attr.value | ||
| import com.example.kuit6_android_api.data.model.request.LoginRequest | ||
| 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.LoginResponse | ||
| 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 | ||
|
|
||
|
|
@@ -37,4 +42,21 @@ interface ApiService { | |
| suspend fun deletePost( | ||
| @Path(value = "id") id: Long, | ||
| ) : BaseResponse<Unit> | ||
|
|
||
|
|
||
| @POST("/api/upload") | ||
| suspend fun uploadImage( | ||
| @Part file: MultipartBody.Part | ||
| ): BaseResponse<Map<String, String>> | ||
|
Comment on lines
+47
to
+50
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. @multipart 어노테이션이 누락되었습니다.
+@Multipart
@POST("/api/upload")
suspend fun uploadImage(
@Part file: MultipartBody.Part
): BaseResponse<Map<String, String>>🤖 Prompt for AI Agents |
||
|
|
||
| @POST("/api/auth/signup") | ||
| suspend fun signup( | ||
| @Body request: LoginRequest | ||
| ): BaseResponse<LoginResponse> | ||
|
|
||
| @POST("/api/auth/login") | ||
| suspend fun login( | ||
| @Body request: LoginRequest | ||
| ): BaseResponse<LoginResponse> | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.example.kuit6_android_api.data.di | ||
|
|
||
| import com.example.kuit6_android_api.data.api.ApiService | ||
| import com.example.kuit6_android_api.data.api.RetrofitClient | ||
| import com.example.kuit6_android_api.data.repository.LoginRepository | ||
| import com.example.kuit6_android_api.data.repository.LoginRepositoryImpl | ||
| import com.example.kuit6_android_api.data.repository.PostRepository | ||
| import com.example.kuit6_android_api.data.repository.PostRepositoryImpl | ||
| import com.example.kuit6_android_api.data.repository.TokenRepository | ||
| import com.example.kuit6_android_api.data.repository.TokenRepositoryImpl | ||
|
|
||
| class AppContainer { | ||
| private val apiService: ApiService by lazy { | ||
| RetrofitClient.apiService | ||
| } | ||
|
|
||
| val postRepository: PostRepository by lazy{ | ||
| PostRepositoryImpl(apiService) | ||
| } | ||
|
|
||
| val loginRepository: LoginRepository by lazy{ | ||
| LoginRepositoryImpl(apiService) | ||
| } | ||
|
|
||
| val tokenRepository: TokenRepository by lazy{ | ||
| TokenRepositoryImpl() | ||
| } | ||
| } |
| 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.Serializable | ||
|
|
||
| @Serializable | ||
| data class LoginRequest( | ||
| val username:String, | ||
| val password: String | ||
| ) | ||
|
|
||
|
|
| 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.Serializable | ||
|
|
||
| @Serializable | ||
| data class LoginResponse( | ||
| val token: String, | ||
| val userId: Long, | ||
| val username: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.example.kuit6_android_api.data.repository | ||
|
|
||
| import com.example.kuit6_android_api.data.model.response.LoginResponse | ||
|
|
||
| interface LoginRepository { | ||
| suspend fun signup(id:String, password: String): Result<LoginResponse> | ||
| suspend fun login(id:String, password: String): Result<LoginResponse> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package com.example.kuit6_android_api.data.repository | ||
|
|
||
| import android.util.Log | ||
| import com.example.kuit6_android_api.data.api.ApiService | ||
| import com.example.kuit6_android_api.data.model.request.LoginRequest | ||
| import com.example.kuit6_android_api.data.model.response.LoginResponse | ||
|
|
||
| class LoginRepositoryImpl( | ||
| private val apiService: ApiService | ||
| ): LoginRepository { | ||
| override suspend fun signup(id: String, password: String): Result<LoginResponse> { | ||
| return runCatching { | ||
| val response = apiService.signup(LoginRequest(username = id, password = password)) | ||
|
|
||
| if (response.success && response.data != null) { | ||
| response.data | ||
| } else { | ||
| throw Exception(response.message ?: "회원가입 실패") | ||
| } | ||
| }.onFailure { error -> | ||
| Log.e("LoginRepository", error.message.toString()) | ||
| } | ||
| } | ||
|
|
||
| override suspend fun login(id: String, password: String): Result<LoginResponse> { | ||
| return runCatching { | ||
| val response = apiService.login(LoginRequest(username = id, password = password)) | ||
|
|
||
| if (response.success && response.data != null) { | ||
| response.data | ||
| } else { | ||
| throw Exception(response.message ?: "로그인 실패") | ||
| } | ||
| }.onFailure { error -> | ||
| Log.e("LoginRepository", error.message.toString()) | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.example.kuit6_android_api.data.repository | ||
|
|
||
| import com.example.kuit6_android_api.data.model.request.PostCreateRequest | ||
| import com.example.kuit6_android_api.data.model.response.PostResponse | ||
| import okhttp3.MultipartBody | ||
|
|
||
| interface PostRepository{ | ||
| suspend fun getPosts(): Result<List<PostResponse>> | ||
| suspend fun createPost( | ||
| author: String, | ||
| request: PostCreateRequest | ||
| ): Result<PostResponse> | ||
| suspend fun getPostDetail( | ||
| id: Long | ||
| ): Result<PostResponse> | ||
| suspend fun editPost( | ||
| id: Long, | ||
| request: PostCreateRequest | ||
| ): Result<PostResponse> | ||
| suspend fun deletePost( | ||
| id: Long | ||
| ): Result<Unit> | ||
| suspend fun uploadImage( | ||
| file: MultipartBody.Part | ||
| ): Result<Map<String, String>> | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.example.kuit6_android_api.data.repository | ||
|
|
||
| import android.util.Log | ||
| import com.example.kuit6_android_api.data.api.ApiService | ||
| import com.example.kuit6_android_api.data.model.request.PostCreateRequest | ||
| import com.example.kuit6_android_api.data.model.response.PostResponse | ||
| import okhttp3.MultipartBody | ||
|
|
||
| class PostRepositoryImpl( | ||
| private val apiService: ApiService | ||
| ) : PostRepository{ | ||
| override suspend fun getPosts(): Result<List<PostResponse>> { | ||
| return runCatching { | ||
| val response = apiService.getPosts() | ||
|
|
||
| if(response.success && response.data != null){ | ||
| response.data | ||
| }else{ | ||
| throw Exception(response.message ?: "게시글 불러오기 실패") | ||
| } | ||
| }.onFailure { error-> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
|
|
||
| override suspend fun createPost( | ||
| author: String, | ||
| request: PostCreateRequest | ||
| ): Result<PostResponse> { | ||
| return runCatching { | ||
| val response = apiService.createPost(author, request) | ||
|
|
||
| if (response.success && response.data != null) { | ||
| response.data | ||
| } else { | ||
| throw Exception(response.message ?: "게시글 작성 실패") | ||
| } | ||
| }.onFailure { error -> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
| override suspend fun getPostDetail(id: Long): Result<PostResponse> { | ||
| return runCatching { | ||
| val response = apiService.getPostDetail(id) | ||
|
|
||
| if (response.success && response.data != null) { | ||
| response.data | ||
| } else { | ||
| throw Exception(response.message ?: "게시글 불러오기 실패") | ||
| } | ||
| }.onFailure { error -> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
| override suspend fun editPost(id: Long, request: PostCreateRequest): Result<PostResponse> { | ||
| return runCatching { | ||
| val response = apiService.editPost(id, request) | ||
| if(response.success && response.data != null){ | ||
| response.data | ||
| }else{ | ||
| throw Exception(response.message ?: "게시글 수정 실패") | ||
| } | ||
| }.onFailure { error-> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
| override suspend fun deletePost(id: Long): Result<Unit> { | ||
| return runCatching { | ||
| val response = apiService.deletePost(id) | ||
| if (response.success) Unit | ||
| else throw Exception(response.message ?: "게시글 삭제 실패") | ||
| }.onFailure { error-> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
|
|
||
| override suspend fun uploadImage(file: MultipartBody.Part): Result<Map<String, String>> { | ||
| return runCatching { | ||
| val response = apiService.uploadImage(file) | ||
| if (response.success && response.data != null) { | ||
| response.data | ||
| } else { | ||
| throw Exception(response.message ?: "이미지 업로드 실패") | ||
| } | ||
| }.onFailure { error -> | ||
| Log.e("PostRepository", error.message.toString()) | ||
| } | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.example.kuit6_android_api.data.repository | ||
|
|
||
| import android.content.Context | ||
|
|
||
| interface TokenRepository{ | ||
| suspend fun saveToken(context: Context, token: String) | ||
| suspend fun getToken(context: Context): String? | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||||||
| package com.example.kuit6_android_api.data.repository | ||||||||||
|
|
||||||||||
| import android.content.Context | ||||||||||
| import androidx.datastore.core.DataStore | ||||||||||
| import androidx.datastore.preferences.core.Preferences | ||||||||||
| import androidx.datastore.preferences.core.edit | ||||||||||
| import androidx.datastore.preferences.core.stringPreferencesKey | ||||||||||
| import androidx.datastore.preferences.preferencesDataStore | ||||||||||
| import kotlinx.coroutines.flow.first | ||||||||||
|
|
||||||||||
| val Context.datastore: DataStore<Preferences> by preferencesDataStore("Token") | ||||||||||
| val KEY = stringPreferencesKey("token") | ||||||||||
|
||||||||||
| val Context.datastore: DataStore<Preferences> by preferencesDataStore("Token") | |
| val KEY = stringPreferencesKey("token") | |
| private val Context.datastore: DataStore<Preferences> by preferencesDataStore("token") | |
| private val KEY = stringPreferencesKey("token") |
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepositoryImpl.kt
around lines 11 to 12, the top-level declarations are public; make them
file-private by adding the private modifier to both the Context.datastore
extension and the KEY declaration, and change the DataStore name from "Token" to
lowercase "token" to standardize naming (i.e., private val Context.datastore:
DataStore<Preferences> by preferencesDataStore("token") and private val KEY =
stringPreferencesKey("token") so they are not exposed outside this file).
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.
🧩 Analysis chain
DataStore alpha 버전 사용을 확인하세요.
androidx.datastore.preferences라이브러리의 alpha 버전(1.3.0-alpha01)을 사용하고 있습니다. 프로덕션 환경에서는 안정적인(stable) 버전 사용을 권장합니다.다음 스크립트를 실행하여 최신 안정 버전을 확인하세요:
🏁 Script executed:
Length of output: 0
검증을 위해 추가 정보를 수집하겠습니다.
이제 실제 build.gradle.kts 파일과 버전 카탈로그를 확인하겠습니다.
Now awaiting script results to verify the actual version being used.
🏁 Script executed:
Length of output: 213
Length of output: 167
Length of output: 4394
이제 최신 안정 버전을 확인하겠습니다.
💡 Result:
Sources:
[1] Android Developers — DataStore release notes.
[2] Maven / mvnrepository listing for datastore-preferences.
androidx.datastore.preferencesalpha 버전을 안정 버전으로 업그레이드하세요.프로젝트에서 최신 안정 버전인 1.1.7(2025년 5월 20일 릴리스)로 업그레이드할 것을 권장합니다. gradle/libs.versions.toml의
datastorePreferences버전을 다음과 같이 변경하세요:변경 후 프로덕션 환경에 배포하기 전에 충분한 테스트를 수행해주세요.
🤖 Prompt for AI Agents