diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4f767c1..59f7d49 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -88,4 +88,7 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
+ // KaKao
+ implementation("com.kakao.sdk:v2-user:2.20.1")
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6533a3d..94bbce5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,6 +14,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.HsLink">
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/core/navigation/AppRoute.kt b/app/src/main/java/com/hsLink/hslink/core/navigation/AppRoute.kt
new file mode 100644
index 0000000..0a3bdeb
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/core/navigation/AppRoute.kt
@@ -0,0 +1,9 @@
+package com.hsLink.hslink.core.navigation
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data object AppMain : Route // 전체 앱의 메인 (기존 MainScreen)
+
+@Serializable
+data object Onboarding : Route
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
index bd684e6..d434a4b 100644
--- a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
@@ -3,6 +3,7 @@ package com.hsLink.hslink.data.di
import com.hsLink.hslink.BuildConfig
import com.hsLink.hslink.data.service.commuunity.CommunityPostService
import com.hsLink.hslink.data.service.home.PostService
+import com.hsLink.hslink.data.service.login.AuthService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
@@ -14,7 +15,6 @@ import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
-import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@@ -65,6 +65,11 @@ object NetworkModule {
fun providePostService(retrofit: Retrofit): PostService =
retrofit.create(PostService::class.java)
+ @Provides
+ @Singleton
+ fun provideAuthService(retrofit: Retrofit): AuthService {
+ return retrofit.create(AuthService::class.java)
+ }
@Provides
@Singleton
fun provideCommunityPostService(retrofit: Retrofit): CommunityPostService =
diff --git a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt
index 029efd8..506224b 100644
--- a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt
@@ -1,15 +1,18 @@
package com.hsLink.hslink.data.di
+import com.hsLink.hslink.data.repositoryimpl.AuthRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.CommunityRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.DummyRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.home.PostRepositoryImpl
import com.hsLink.hslink.domain.DummyRepository
+import com.hsLink.hslink.domain.repository.AuthRepository
import com.hsLink.hslink.domain.repository.community.CommunityRepository
import com.hsLink.hslink.domain.repository.home.PostRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
@@ -25,9 +28,14 @@ interface RepositoryModule {
postRepositoryImpl: PostRepositoryImpl,
): PostRepository
+ @Binds
+ @Singleton
+ fun bindAuthRepository(
+ authRepositoryImpl: AuthRepositoryImpl
+ ): AuthRepository
+
@Binds
fun bindsCommunityPostRepository(
communityPostRepositoryImpl: CommunityRepositoryImpl,
): CommunityRepository
-
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequest.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequest.kt
new file mode 100644
index 0000000..4b372a1
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequest.kt
@@ -0,0 +1,9 @@
+package com.hsLink.hslink.data.dto.request
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SocialLoginRequest(
+ val provider: String,
+ val accessToken: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponse.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponse.kt
new file mode 100644
index 0000000..42b4fb3
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponse.kt
@@ -0,0 +1,11 @@
+package com.hsLink.hslink.data.dto.response
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SocialLoginResponse(
+ val accessToken: String,
+ val refreshToken: String,
+ val isNewUser: Boolean,
+ val needsOnboarding: Boolean
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
new file mode 100644
index 0000000..4e46295
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
@@ -0,0 +1,32 @@
+package com.hsLink.hslink.data.repositoryimpl
+
+import com.hsLink.hslink.data.dto.request.SocialLoginRequest
+import com.hsLink.hslink.data.dto.response.SocialLoginResponse
+import com.hsLink.hslink.data.service.login.AuthService
+import com.hsLink.hslink.domain.repository.AuthRepository
+import javax.inject.Inject
+
+class AuthRepositoryImpl @Inject constructor(
+ private val authService: AuthService
+) : AuthRepository {
+
+ override suspend fun loginWithSocialToken(
+ provider: String,
+ accessToken: String
+ ): Result {
+ return try {
+ val request = SocialLoginRequest(provider, accessToken)
+ val response = authService.socialLogin(request)
+
+ if (response.isSuccessful) {
+ response.body()?.let { loginResponse ->
+ Result.success(loginResponse)
+ } ?: Result.failure(Exception("응답이 비어있습니다"))
+ } else {
+ Result.failure(Exception("로그인 실패: ${response.code()}"))
+ }
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
new file mode 100644
index 0000000..0985bf1
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
@@ -0,0 +1,12 @@
+package com.hsLink.hslink.data.service.login
+
+import com.hsLink.hslink.data.dto.request.SocialLoginRequest
+import com.hsLink.hslink.data.dto.response.SocialLoginResponse
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+interface AuthService {
+ @POST("auth/login")
+ suspend fun socialLogin(@Body request: SocialLoginRequest): Response
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
new file mode 100644
index 0000000..a64fcfe
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
@@ -0,0 +1,10 @@
+package com.hsLink.hslink.domain.repository
+
+import com.hsLink.hslink.data.dto.response.SocialLoginResponse
+
+interface AuthRepository {
+ suspend fun loginWithSocialToken(
+ provider: String,
+ accessToken: String
+ ): Result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/component/KakaoLoginButton.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/component/KakaoLoginButton.kt
new file mode 100644
index 0000000..f30eccd
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/component/KakaoLoginButton.kt
@@ -0,0 +1,59 @@
+package com.hsLink.hslink.presentation.login.component
+
+import android.R.attr.enabled
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.R
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun KakaoLoginButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true
+) {
+ Button(
+ onClick = onClick,
+ enabled = enabled,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Color(0xFFFEE500), // 카카오 노란색
+ contentColor = Color.Black // 텍스트 및 아이콘 색상
+ ),
+ shape = RoundedCornerShape(6.dp), // 모서리 둥글기
+ modifier = Modifier
+ .fillMaxWidth(0.85f) // 가로 폭 (조정 가능)
+ .height(54.dp) // 높이 (Figma 기준)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ // 말풍선 아이콘 (SVG 또는 PNG 리소스)
+ Image(
+ painter = painterResource(id = com.hsLink.hslink.R.drawable.ic_kakao_icon),
+ contentDescription = "카카오 로고",
+ modifier = Modifier
+ .size(20.dp)
+ .padding(end = 8.dp)
+ )
+ Text(
+ text = "카카오 로그인",
+ color = Color(0xFF191919), // 카카오 가이드 기준 검정 85%
+ fontSize = 16.sp
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/navigation/LoginNavigation.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/navigation/LoginNavigation.kt
new file mode 100644
index 0000000..f8e43f0
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/navigation/LoginNavigation.kt
@@ -0,0 +1,31 @@
+package com.hsLink.hslink.presentation.login.navigation
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.hsLink.hslink.core.navigation.Route
+import com.hsLink.hslink.presentation.login.screen.KaKaoLoginScreen
+import kotlinx.serialization.Serializable
+
+fun NavController.navigateToLogin(navOptions: NavOptions? = null) {
+ navigate(Login, navOptions)
+}
+
+fun NavGraphBuilder.loginNavGraph(
+ padding: PaddingValues,
+ onNavigateToMain: () -> Unit,
+ onNavigateToOnboarding: () -> Unit
+) {
+ composable {
+ KaKaoLoginScreen(
+ paddingValues = padding,
+ onNavigateToHome = onNavigateToMain,
+ onNavigateToOnboarding = onNavigateToOnboarding
+ )
+ }
+}
+
+@Serializable
+data object Login : Route // Route 인터페이스 구현
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/screen/KaKaoLoginScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/screen/KaKaoLoginScreen.kt
new file mode 100644
index 0000000..7b9af50
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/screen/KaKaoLoginScreen.kt
@@ -0,0 +1,161 @@
+package com.hsLink.hslink.presentation.login.screen
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
+import com.hsLink.hslink.presentation.login.component.KakaoLoginButton
+import com.hsLink.hslink.presentation.login.state.KakaoLoginManager
+import com.hsLink.hslink.presentation.login.state.LoginState
+import com.hsLink.hslink.presentation.login.viewmodel.LoginViewModel
+
+@Preview(showBackground = true)
+@Composable
+fun KaKaoLoginScreenPreview() {
+ KaKaoLoginScreen()
+}
+
+
+@Composable
+fun KaKaoLoginScreen(
+ modifier: Modifier = Modifier,
+ paddingValues: PaddingValues = PaddingValues(0.dp),
+ viewModel: LoginViewModel = hiltViewModel(),
+ onNavigateToHome: () -> Unit = {},
+ onNavigateToOnboarding: () -> Unit = {}
+) {
+ val context = LocalContext.current
+ val loginState by viewModel.loginState.collectAsState()
+
+ // 로그인 성공 시 네비게이션 처리
+ LaunchedEffect(loginState) {
+ // 지역 변수로 만들어서 스마트 캐스팅 가능하게 만들기
+ val currentState = loginState
+ when (currentState) {
+ is LoginState.Success -> {
+ if (currentState.needsOnboarding) {
+ onNavigateToOnboarding()
+ } else {
+ onNavigateToHome()
+ }
+ }
+
+ else -> {}
+ }
+ }
+
+ Box(
+ modifier = modifier
+ .fillMaxSize()
+ .background(
+ brush = Brush.verticalGradient(
+ colors = listOf(
+ HsLinkTheme.colors.SkyBlue400,
+ Color.White
+ ),
+ startY = 0f,
+ endY = Float.POSITIVE_INFINITY
+ )
+ )
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(id = com.hsLink.hslink.R.drawable.img_login_logo),
+ contentDescription = "앱 로고",
+ modifier = Modifier.size(213.dp)
+ )
+ Text(
+ text = "한성인들이 연결되는 곳",
+ style = HsLinkTheme.typography.title_24Strong,
+ color = HsLinkTheme.colors.DeepBlue600
+ )
+ }
+
+ Spacer(modifier = Modifier.padding(115.dp))
+
+ KakaoLoginButton(
+ onClick = {
+ KakaoLoginManager.startKakaoLogin(context) { accessToken, error ->
+ if (error != null) {
+ // TODO: 에러 처리 개선
+ println("카카오 로그인 실패: ${error.message}")
+ } else if (accessToken != null) {
+ // 서버에 토큰 전송
+ viewModel.loginWithKakaoToken(accessToken)
+ }
+ }
+ },
+ enabled = loginState !is LoginState.Loading
+ )
+
+ // 로그인 상태에 따른 UI - 여기도 지역 변수 사용
+ val currentLoginState = loginState
+ when (currentLoginState) {
+ is LoginState.Loading -> {
+ Spacer(modifier = Modifier.padding(16.dp))
+ CircularProgressIndicator(
+ color = HsLinkTheme.colors.DeepBlue600
+ )
+ Text(
+ text = "로그인 중...",
+ style = HsLinkTheme.typography.body_14Normal,
+ color = HsLinkTheme.colors.DeepBlue600,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+ }
+
+ is LoginState.Error -> {
+ Spacer(modifier = Modifier.padding(16.dp))
+ Text(
+ text = currentLoginState.message,
+ color = Color.Red,
+ style = HsLinkTheme.typography.body_14Normal
+ )
+ }
+
+ is LoginState.Success -> {
+ Spacer(modifier = Modifier.padding(16.dp))
+ Text(
+ text = "로그인 성공!",
+ color = Color.Green,
+ style = HsLinkTheme.typography.body_14Normal
+ )
+ }
+
+ LoginState.Idle -> {
+ // 아무것도 표시하지 않음
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/state/KakaoLoginManager.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/state/KakaoLoginManager.kt
new file mode 100644
index 0000000..3f75144
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/state/KakaoLoginManager.kt
@@ -0,0 +1,41 @@
+package com.hsLink.hslink.presentation.login.state
+
+import android.content.Context
+import com.kakao.sdk.user.UserApiClient
+
+// 카카오 로그인을 처리하는 도우미 클래스
+object KakaoLoginManager {
+
+ // 카카오 로그인을 시작하는 함수
+ fun startKakaoLogin(
+ context: Context, // 현재 화면 정보
+ onResult: (String?, Throwable?) -> Unit // 결과를 받을 함수
+ ) {
+ // 1. 카카오톡이 설치되어 있는지 확인
+ if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
+ // 카카오톡으로 로그인 시도
+ UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
+ if (error != null) {
+ // 카카오톡 로그인 실패 -> 카카오계정으로 시도
+ loginWithKakaoAccount(context, onResult)
+ } else {
+ // 카카오톡 로그인 성공
+ onResult(token?.accessToken, null)
+ }
+ }
+ } else {
+ // 카카오톡이 없으면 바로 카카오계정으로 로그인
+ loginWithKakaoAccount(context, onResult)
+ }
+ }
+
+ // 카카오계정으로 로그인하는 함수
+ private fun loginWithKakaoAccount(
+ context: Context,
+ onResult: (String?, Throwable?) -> Unit
+ ) {
+ UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
+ onResult(token?.accessToken, error)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/state/LoginState.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/state/LoginState.kt
new file mode 100644
index 0000000..3ee05b9
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/state/LoginState.kt
@@ -0,0 +1,14 @@
+package com.hsLink.hslink.presentation.login.state
+
+// 로그인의 여러 상태를 정의하는 클래스
+sealed class LoginState {
+ object Idle : LoginState() // 대기중 (아무것도 안 한 상태)
+ object Loading : LoginState() // 로딩중 (로그인 진행중)
+ data class Success( // 성공 (로그인 완료)
+ val isNewUser: Boolean, // 새로운 사용자인지
+ val needsOnboarding: Boolean // 온보딩이 필요한지
+ ) : LoginState()
+ data class Error( // 실패 (에러 발생)
+ val message: String // 에러 메시지
+ ) : LoginState()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/login/viewmodel/LoginViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/login/viewmodel/LoginViewModel.kt
new file mode 100644
index 0000000..76ff200
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/login/viewmodel/LoginViewModel.kt
@@ -0,0 +1,47 @@
+package com.hsLink.hslink.presentation.login.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.hsLink.hslink.domain.repository.AuthRepository
+import com.hsLink.hslink.presentation.login.state.LoginState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class LoginViewModel @Inject constructor(
+ private val authRepository: AuthRepository
+) : ViewModel() {
+
+ private val _loginState = MutableStateFlow(LoginState.Idle)
+ val loginState: StateFlow = _loginState.asStateFlow()
+
+ fun loginWithKakaoToken(accessToken: String) {
+ _loginState.value = LoginState.Loading
+
+ viewModelScope.launch {
+ authRepository.loginWithSocialToken("kakao", accessToken)
+ .onSuccess { response ->
+ println("서버 로그인 성공: $response")
+ _loginState.value = LoginState.Success(
+ isNewUser = response.isNewUser,
+ needsOnboarding = response.needsOnboarding
+ )
+ }
+ .onFailure { error ->
+ println("서버 로그인 실패: ${error.message}")
+ println("에러 상세: ${error.printStackTrace()}")
+ _loginState.value = LoginState.Error(
+ error.message ?: "로그인에 실패했습니다"
+ )
+ }
+ }
+ }
+
+ fun resetLoginState() {
+ _loginState.value = LoginState.Idle
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/main/MainActivity.kt b/app/src/main/java/com/hsLink/hslink/presentation/main/MainActivity.kt
index 5353856..cc81aab 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/main/MainActivity.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/main/MainActivity.kt
@@ -1,21 +1,134 @@
package com.hsLink.hslink.presentation.main
+import android.content.pm.PackageManager
+import android.os.Build
import android.os.Bundle
+import android.util.Base64
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
+import com.hsLink.hslink.presentation.login.screen.KaKaoLoginScreen
+import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.serialization.Serializable
+import java.security.MessageDigest
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
+ // 카카오 SDK 초기화
+ KakaoSdk.init(this, "a0bbd28c9384baa131a731d1b914307c")
+
+ // 키 해시 확인 (API 레벨에 따라 다른 방법 사용)
+ try {
+ val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
+ } else {
+ @Suppress("DEPRECATION")
+ packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
+ }
+
+ val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ packageInfo.signingInfo?.apkContentsSigners
+ } else {
+ @Suppress("DEPRECATION")
+ packageInfo.signatures
+ }
+
+ signatures?.let { sigs ->
+ for (signature in sigs) {
+ val md = MessageDigest.getInstance("SHA")
+ md.update(signature.toByteArray())
+ val keyHash = Base64.encodeToString(md.digest(), Base64.NO_WRAP)
+ Log.d("KeyHash", "KeyHash: $keyHash")
+ println("KeyHash: $keyHash")
+ }
+ }
+ } catch (e: Exception) {
+ Log.e("KeyHash", "Error getting key hash", e)
+ }
+
enableEdgeToEdge()
setContent {
HsLinkTheme {
- MainScreen()
+ AppNavigation()
}
}
}
}
+
+@Serializable
+data object Login
+
+@Serializable
+data object AppMain
+
+@Composable
+private fun AppNavigation() {
+ val navController = rememberNavController()
+ var isLoginChecked by remember { mutableStateOf(false) }
+ var isLoggedIn by remember { mutableStateOf(false) }
+
+ LaunchedEffect(Unit) {
+ isLoggedIn = checkLoginStatus()
+ isLoginChecked = true
+ }
+
+ if (!isLoginChecked) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+ return
+ }
+
+ NavHost(
+ navController = navController,
+ startDestination = if (isLoggedIn) AppMain else Login
+ ) {
+ composable {
+ KaKaoLoginScreen(
+ paddingValues = PaddingValues(),
+ onNavigateToHome = {
+ navController.navigate(AppMain) {
+ popUpTo(Login) { inclusive = true }
+ }
+ },
+ onNavigateToOnboarding = {
+ navController.navigate(AppMain) {
+ popUpTo(Login) { inclusive = true }
+ }
+ }
+ )
+ }
+
+ composable {
+ MainScreen()
+ }
+ }
+}
+
+private suspend fun checkLoginStatus(): Boolean {
+ return false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt b/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
index c3a7b47..9958277 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
@@ -9,6 +9,7 @@ import com.hsLink.hslink.presentation.community.navigation.post.communityPostNav
import com.hsLink.hslink.presentation.community.navigation.write.communityWriteNavGraph
import com.hsLink.hslink.presentation.home.navigation.homeNavGraph
import com.hsLink.hslink.presentation.home.navigation.searchNavGraph
+import com.hsLink.hslink.presentation.login.navigation.loginNavGraph
import com.hsLink.hslink.presentation.mypage.navigation.main.mypageNavGraph
import com.hsLink.hslink.presentation.mypage.navigation.profile.profileEditNavGraph
import com.hsLink.hslink.presentation.mypage.navigation.career.careerNavGraph
@@ -25,6 +26,13 @@ fun MainNavHost(
startDestination = navigator.startDestination,
modifier = modifier
) {
+
+ loginNavGraph(
+ padding = padding,
+ onNavigateToMain = { navigator.navigateToHome() },
+ onNavigateToOnboarding = { navigator.navigateToHome() }
+ )
+
homeNavGraph(padding)
searchNavGraph(padding)
communityNavGraph(
diff --git a/app/src/main/res/drawable/ic_kakao_icon.xml b/app/src/main/res/drawable/ic_kakao_icon.xml
new file mode 100644
index 0000000..1f43a04
--- /dev/null
+++ b/app/src/main/res/drawable/ic_kakao_icon.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/img_login_logo.png b/app/src/main/res/drawable/img_login_logo.png
new file mode 100644
index 0000000..19d21ff
Binary files /dev/null and b/app/src/main/res/drawable/img_login_logo.png differ
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 9cd993d..852fbc7 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -16,6 +16,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
+ maven { url = java.net.URI("https://devrepo.kakao.com/nexus/content/groups/public/") }
}
}