Skip to content

Commit 0b83617

Browse files
authored
Merge pull request #27 from Nico1eKim/api/#25-home
[API] home api 통신
2 parents 112c245 + 3df6b5c commit 0b83617

File tree

18 files changed

+496
-165
lines changed

18 files changed

+496
-165
lines changed

composeApp/src/androidMain/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
android:label="@string/app_name"
1111
android:roundIcon="@mipmap/ic_launcher_round"
1212
android:supportsRtl="true"
13-
android:theme="@android:style/Theme.Material.Light.NoActionBar">
13+
android:theme="@android:style/Theme.Material.Light.NoActionBar"
14+
android:usesCleartextTraffic="true">
1415
<activity
1516
android:exported="true"
1617
android:name=".MainActivity">
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.whosin.client.presentation.component
2+
3+
import androidx.activity.compose.BackHandler
4+
import androidx.compose.runtime.Composable
5+
6+
@Composable
7+
actual fun CommonBackHandler(enabled: Boolean, onBack: () -> Unit) {
8+
BackHandler(enabled = enabled, onBack = onBack)
9+
}

composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/WhosInNavGraph.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ fun WhosInNavGraph(
127127
composable<Route.Home> {
128128
HomeScreen(
129129
modifier = modifier,
130-
onNavigateBack = { navController.navigateUp() },
131130
onNavigateToMyPage = {
132131
navController.navigate(Route.MyPage)
133132
}

composeApp/src/commonMain/kotlin/org/whosin/client/core/network/HttpClientFactory.kt

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.ktor.client.plugins.logging.Logging
1515
import io.ktor.client.request.post
1616
import io.ktor.client.request.setBody
1717
import io.ktor.http.ContentType
18+
import io.ktor.http.Url
1819
import io.ktor.http.contentType
1920
import io.ktor.http.encodedPath
2021
import io.ktor.serialization.kotlinx.json.json
@@ -47,31 +48,42 @@ object HttpClientFactory {
4748
install(Auth){
4849
bearer {
4950
loadTokens {
50-
val accessToken = tokenManager.getAccessToken() ?: "no_token"
51+
val accessToken = tokenManager.getAccessToken() ?: "eyJhbGciOiJIUzI1NiJ9.eyJ0b2tlblR5cGUiOiJhY2Nlc3MiLCJ1c2VySWQiOjUsInByb3ZpZGVySWQiOiJsb2NhbGhvc3QiLCJuYW1lIjoi7Iug7KKF7JykIiwicm9sZSI6IlJPTEVfTUVNQkVSIiwiaWF0IjoxNzU5MzgyMzg3LCJleHAiOjE3NTk5ODcxODd9.kT9IH60aCA-6ByEITb-_qPAJY0Oik1bbPKqcBWXzHIk"
5152
val refreshToken = tokenManager.getRefreshToken() ?: "no_token"
5253
BearerTokens(accessToken = accessToken, refreshToken = refreshToken)
5354
}
5455
sendWithoutRequest { request ->
55-
val host = "https://"+request.url.host+"/"
56-
val path = request.url.encodedPath
57-
val pathWithNoAuth = listOf(
58-
"jokes",
59-
"users/signup",
60-
"users/find-password",
61-
"auth/login",
62-
"auth/email",
63-
"auth/email/validation"
64-
)
65-
// 결과가 true면 Authorization 헤더 추가, false면 제거
66-
if(host != BASE_URL){
56+
val requestHost = request.url.host
57+
val baseHost = try {
58+
Url(BASE_URL).host
59+
} catch (e: Exception) {
60+
// BASE_URL 형식이 잘못되었을 경우를 대비한 예외 처리
61+
null
62+
}
63+
64+
// 요청하는 API의 host가 우리 서버의 host와 다르면 외부 API로 간주하여 토큰을 보내지 않음
65+
if (requestHost != baseHost) {
6766
println("External API - No Auth")
6867
false
69-
}else{
70-
// pathWithNoAuth에 있는 경로에는 Authorization 헤더 제외
68+
} else {
69+
// 우리 서버로 요청하는 경우, 인증이 필요 없는 경로인지 확인
70+
val path = request.url.encodedPath
71+
val pathWithNoAuth = listOf(
72+
"jokes",
73+
"users/signup",
74+
"users/find-password",
75+
"auth/login",
76+
"auth/email",
77+
"auth/email/validation",
78+
"member/reissue" // 토큰 재발급 요청 자체에는 만료된 액세스 토큰을 보내면 안 됨
79+
)
80+
7181
val isNoAuthPath = pathWithNoAuth.any { noAuthPath ->
72-
path.startsWith(noAuthPath) || path.contains(noAuthPath)
82+
path.contains(noAuthPath)
7383
}
74-
println("isNoAuthPath: $isNoAuthPath")
84+
85+
// isNoAuthPath가 true이면 인증이 필요 없는 경로 -> 헤더를 보내지 않음 (false 반환)
86+
// isNoAuthPath가 false이면 인증이 필요한 경로 -> 헤더를 보냄 (true 반환)
7587
!isNoAuthPath
7688
}
7789
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.whosin.client.data.dto.response
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class ClubPresencesResponseDto(
8+
@SerialName("success")
9+
val success: Boolean,
10+
@SerialName("status")
11+
val status: Int,
12+
@SerialName("message")
13+
val message: String,
14+
@SerialName("data")
15+
val data: ClubPresencesData
16+
)
17+
18+
@Serializable
19+
data class ClubPresencesData(
20+
@SerialName("clubName")
21+
val clubName: String,
22+
@SerialName("presentMembers")
23+
val presentMembers: List<PresentMembers>
24+
)
25+
26+
@Serializable
27+
data class PresentMembers(
28+
@SerialName("userName")
29+
val userName: String,
30+
@SerialName("isMe")
31+
val isMe: Boolean
32+
)
Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,96 @@
11
package org.whosin.client.data.remote
22

33
import io.ktor.client.HttpClient
4+
import io.ktor.client.call.body
5+
import io.ktor.client.request.delete
6+
import io.ktor.client.request.get
7+
import io.ktor.client.request.post
8+
import io.ktor.client.statement.HttpResponse
9+
import io.ktor.http.isSuccess
10+
import org.whosin.client.core.network.ApiResult
11+
import org.whosin.client.data.dto.response.ClubPresencesResponseDto
12+
import org.whosin.client.data.dto.response.MyClubResponseDto
413

514
class RemoteClubDataSource(
6-
private val client : HttpClient
15+
private val client: HttpClient
716
) {
17+
suspend fun getMyClubs(): ApiResult<MyClubResponseDto> {
18+
return try {
19+
val response: HttpResponse = client.get(urlString = "clubs/my")
820

21+
if (response.status.isSuccess()) {
22+
ApiResult.Success(
23+
data = response.body(),
24+
statusCode = response.status.value
25+
)
26+
} else {
27+
ApiResult.Error(
28+
code = response.status.value,
29+
message = "HTTP Error: ${response.status.value}"
30+
)
31+
}
32+
} catch (t: Throwable) {
33+
ApiResult.Error(message = t.message, cause = t)
34+
}
35+
}
36+
37+
suspend fun getPresentMembers(clubId: Int): ApiResult<ClubPresencesResponseDto> {
38+
return try {
39+
val response: HttpResponse = client.get(urlString = "clubs/$clubId/presences")
40+
41+
if (response.status.isSuccess()) {
42+
ApiResult.Success(
43+
data = response.body(),
44+
statusCode = response.status.value
45+
)
46+
} else {
47+
ApiResult.Error(
48+
code = response.status.value,
49+
message = "HTTP Error: ${response.status.value}"
50+
)
51+
}
52+
} catch (t: Throwable) {
53+
ApiResult.Error(message = t.message, cause = t)
54+
}
55+
}
56+
57+
suspend fun checkIn(clubId: Int): ApiResult<Unit> {
58+
return try {
59+
val response: HttpResponse = client.post(urlString = "clubs/$clubId/check-in")
60+
61+
if (response.status.isSuccess()) {
62+
ApiResult.Success(
63+
data = response.body(),
64+
statusCode = response.status.value
65+
)
66+
} else {
67+
ApiResult.Error(
68+
code = response.status.value,
69+
message = "HTTP Error: ${response.status.value}"
70+
)
71+
}
72+
} catch (t: Throwable) {
73+
ApiResult.Error(message = t.message, cause = t)
74+
}
75+
}
76+
77+
suspend fun checkOut(clubId: Int): ApiResult<Unit> {
78+
return try {
79+
val response: HttpResponse = client.delete(urlString = "clubs/$clubId/check-out")
80+
81+
if (response.status.isSuccess()) {
82+
ApiResult.Success(
83+
data = response.body(),
84+
statusCode = response.status.value
85+
)
86+
} else {
87+
ApiResult.Error(
88+
code = response.status.value,
89+
message = "HTTP Error: ${response.status.value}"
90+
)
91+
}
92+
} catch (t: Throwable) {
93+
ApiResult.Error(message = t.message, cause = t)
94+
}
95+
}
996
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
package org.whosin.client.data.repository
22

3+
import org.whosin.client.core.network.ApiResult
4+
import org.whosin.client.data.dto.response.ClubPresencesResponseDto
5+
import org.whosin.client.data.dto.response.MyClubResponseDto
36
import org.whosin.client.data.remote.RemoteClubDataSource
47

58
class ClubRepository(
69
private val dataSource: RemoteClubDataSource
710
) {
11+
suspend fun getMyClubs(): ApiResult<MyClubResponseDto> =
12+
dataSource.getMyClubs()
813

14+
suspend fun getPresentMembers(clubId: Int): ApiResult<ClubPresencesResponseDto> =
15+
dataSource.getPresentMembers(clubId)
16+
17+
suspend fun checkIn(clubId: Int): ApiResult<Unit> =
18+
dataSource.checkIn(clubId)
19+
20+
suspend fun checkOut(clubId: Int): ApiResult<Unit> =
21+
dataSource.checkOut(clubId)
922
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.whosin.client.presentation.component
2+
3+
import androidx.compose.runtime.Composable
4+
5+
@Composable
6+
expect fun CommonBackHandler(enabled: Boolean = true, onBack: () -> Unit)

0 commit comments

Comments
 (0)