Skip to content

Commit 045ed9c

Browse files
authored
Merge pull request #17 from JeongIlhyuk/JeongIlhyuk/week6
6주차 미션 / 안드로이드 1조 정일혁
2 parents bd77f9d + 7b4f7b3 commit 045ed9c

File tree

24 files changed

+523
-251
lines changed

24 files changed

+523
-251
lines changed

.idea/deploymentTargetSelector.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/markdown.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kotlin version: 2.0.21
2+
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
3+
1. Kotlin compile daemon is ready
4+

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.configuration.updateBuildConfiguration": "automatic"
3+
}

CLAUDE.md

Lines changed: 0 additions & 117 deletions
This file was deleted.

app/build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import java.util.Properties
2+
13
plugins {
24
alias(libs.plugins.android.application)
35
alias(libs.plugins.kotlin.android)
@@ -18,6 +20,13 @@ android {
1820

1921
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2022

23+
val properties = Properties()
24+
val localPropertiesFile = rootProject.file("local.properties")
25+
if (localPropertiesFile.exists()) {
26+
properties.load(localPropertiesFile.inputStream())
27+
}
28+
val baseUrl = properties.getProperty("BASE_URL")
29+
buildConfigField("String", "BASE_URL", "\"$baseUrl\"")
2130
}
2231

2332
buildTypes {
@@ -38,6 +47,7 @@ android {
3847
}
3948
buildFeatures {
4049
compose = true
50+
buildConfig = true
4151
}
4252
}
4353

@@ -74,4 +84,10 @@ dependencies {
7484
androidTestImplementation(libs.androidx.espresso.core)
7585
debugImplementation(libs.androidx.compose.ui.tooling)
7686
debugImplementation(libs.androidx.compose.ui.test.manifest)
87+
88+
// Retrofit & OkHttp
89+
implementation(libs.retrofit)
90+
implementation(libs.retrofit.converter.kotlinx.serialization)
91+
implementation(libs.okhttp)
92+
implementation(libs.okhttp.logging.interceptor)
7793
}

app/src/main/java/com/example/kuit6_android_api/MainActivity.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import android.widget.Toast
88
import androidx.activity.ComponentActivity
99
import androidx.activity.compose.setContent
1010
import androidx.activity.result.contract.ActivityResultContracts
11+
import androidx.compose.foundation.background
1112
import androidx.compose.foundation.layout.fillMaxSize
1213
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Scaffold
15+
import androidx.compose.material3.SnackbarHost
16+
import androidx.compose.material3.SnackbarHostState
1317
import androidx.compose.material3.Surface
18+
import androidx.compose.runtime.remember
1419
import androidx.compose.ui.Modifier
1520
import androidx.core.content.ContextCompat
1621
import androidx.navigation.compose.rememberNavController
@@ -40,15 +45,23 @@ class MainActivity : ComponentActivity() {
4045

4146
setContent {
4247
KUIT6_Android_APITheme {
43-
Surface(
44-
modifier = Modifier.fillMaxSize(),
45-
color = MaterialTheme.colorScheme.background
48+
val snackBarState = remember { SnackbarHostState() }
49+
Scaffold(
50+
modifier = Modifier
51+
.fillMaxSize()
52+
.background(
53+
MaterialTheme.colorScheme.background
54+
),
55+
snackbarHost = {
56+
SnackbarHost(hostState = snackBarState)
57+
}
4658
) {
4759
val navController = rememberNavController()
4860

4961
NavGraph(
5062
navController = navController,
51-
startDestination = PostListRoute
63+
startDestination = PostListRoute,
64+
snackBarState = snackBarState
5265
)
5366
}
5467
}
@@ -71,6 +84,7 @@ class MainActivity : ComponentActivity() {
7184
) == PackageManager.PERMISSION_GRANTED -> {
7285
// 이미 권한이 있음
7386
}
87+
7488
shouldShowRequestPermissionRationale(permission) -> {
7589
// 권한 거부 이력이 있음 - 설명 표시 후 재요청
7690
Toast.makeText(
@@ -80,6 +94,7 @@ class MainActivity : ComponentActivity() {
8094
).show()
8195
requestPermissionLauncher.launch(permission)
8296
}
97+
8398
else -> {
8499
// 권한 요청
85100
requestPermissionLauncher.launch(permission)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.example.kuit6_android_api.data.api
2+
3+
import com.example.kuit6_android_api.data.model.request.PostCreateRequest
4+
import com.example.kuit6_android_api.data.model.response.BaseResponse
5+
import com.example.kuit6_android_api.data.model.response.PostResponse
6+
import okhttp3.MultipartBody
7+
import retrofit2.http.Body
8+
import retrofit2.http.DELETE
9+
import retrofit2.http.GET
10+
import retrofit2.http.Multipart
11+
import retrofit2.http.POST
12+
import retrofit2.http.PUT
13+
import retrofit2.http.Part
14+
import retrofit2.http.Path
15+
import retrofit2.http.Query
16+
17+
interface ApiService {
18+
// 게시글 목록 조회
19+
@GET("/api/posts")
20+
suspend fun getPosts(): BaseResponse<List<PostResponse>>
21+
22+
// 게시글 생성
23+
@POST("/api/posts")
24+
suspend fun createPost(
25+
@Query("author") author: String = "규빈",
26+
@Body request: PostCreateRequest
27+
): BaseResponse<PostResponse>
28+
29+
// 게시글 상세 조회
30+
@GET("/api/posts/{id}")
31+
suspend fun getPostDetail(
32+
@Path("id") id: Long
33+
): BaseResponse<PostResponse>
34+
35+
// 게시글 수정
36+
@PUT("/api/posts/{id}")
37+
suspend fun updatePost(
38+
@Path("id") id: Long,
39+
@Body request: PostCreateRequest
40+
): BaseResponse<PostResponse>
41+
42+
// 게시글 삭제
43+
@DELETE("/api/posts/{id}")
44+
suspend fun deletePost(
45+
@Path("id") id: Long
46+
): BaseResponse<Unit>
47+
48+
// 이미지 업로드
49+
@Multipart
50+
@POST("/api/images/upload")
51+
suspend fun uploadImage(
52+
@Part file: MultipartBody.Part
53+
): BaseResponse<Map<String, String>>
54+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.example.kuit6_android_api.data.api
2+
3+
import com.example.kuit6_android_api.BuildConfig
4+
import kotlinx.serialization.json.Json
5+
import okhttp3.MediaType.Companion.toMediaType
6+
import okhttp3.OkHttpClient
7+
import okhttp3.logging.HttpLoggingInterceptor
8+
import retrofit2.Retrofit
9+
import retrofit2.converter.kotlinx.serialization.asConverterFactory
10+
import java.util.concurrent.TimeUnit
11+
12+
object RetrofitClient {
13+
private val loggingInterceptor = HttpLoggingInterceptor().apply {
14+
level = HttpLoggingInterceptor.Level.BODY
15+
}
16+
17+
private val okHttpClient = OkHttpClient.Builder()
18+
.addInterceptor(loggingInterceptor)
19+
.connectTimeout(30, TimeUnit.SECONDS)
20+
.readTimeout(30, TimeUnit.SECONDS)
21+
.writeTimeout(30, TimeUnit.SECONDS)
22+
.build()
23+
24+
private val json = Json {
25+
ignoreUnknownKeys = true // 서버에서 추가 필드가 와도 무시
26+
coerceInputValues = true // null이 와야 할 곳에 다른 값이 와도 처리
27+
}
28+
29+
private val retrofit: Retrofit = Retrofit.Builder()
30+
.baseUrl(BuildConfig.BASE_URL)
31+
.client(okHttpClient)
32+
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
33+
.build()
34+
35+
val apiService: ApiService = retrofit.create(ApiService::class.java)
36+
}

0 commit comments

Comments
 (0)