diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 62227e08c..894b63219 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -129,6 +129,9 @@
+
diff --git a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt
index 683a1b5cd..11a460871 100644
--- a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt
+++ b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt
@@ -1,8 +1,10 @@
package com.eatssu.android.di
+import android.content.Context
import com.eatssu.android.BuildConfig
import com.eatssu.android.BuildConfig.BASE_URL
+import com.eatssu.android.di.network.NetworkErrorInterceptor
import com.eatssu.android.di.network.TokenAuthenticator
import com.eatssu.android.di.network.TokenInterceptor
import com.eatssu.android.domain.usecase.auth.GetRefreshTokenUseCase
@@ -13,6 +15,7 @@ import com.eatssu.android.domain.usecase.auth.SetRefreshTokenUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
@@ -55,12 +58,14 @@ object NetworkModule {
@Provides
fun provideAuthOkHttpClient(
tokenInterceptor: TokenInterceptor,
- tokenAuthenticator: TokenAuthenticator
+ tokenAuthenticator: TokenAuthenticator,
+ networkErrorInterceptor: NetworkErrorInterceptor
) = if (BuildConfig.DEBUG) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
OkHttpClient.Builder()
+ .addInterceptor(networkErrorInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(tokenInterceptor)
.authenticator(tokenAuthenticator)
@@ -68,6 +73,7 @@ object NetworkModule {
} else {
// 프로덕션 환경에서는 로깅 인터셉터를 추가하지 않음
OkHttpClient.Builder()
+ .addInterceptor(networkErrorInterceptor)
.addInterceptor(tokenInterceptor)
.authenticator(tokenAuthenticator)
.build()
@@ -77,8 +83,11 @@ object NetworkModule {
@Singleton
@Provides
@NoToken
- fun provideNoAuthOkHttpClient(): OkHttpClient {
+ fun provideNoAuthOkHttpClient(
+ networkErrorInterceptor: NetworkErrorInterceptor
+ ): OkHttpClient {
val builder = OkHttpClient.Builder()
+ .addInterceptor(networkErrorInterceptor)
if (BuildConfig.DEBUG) {
builder.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
@@ -108,6 +117,14 @@ object NetworkModule {
.build()
}
+ @Provides
+ @Singleton
+ fun provideNetworkErrorInterceptor(
+ @ApplicationContext context: Context
+ ): NetworkErrorInterceptor {
+ return NetworkErrorInterceptor(context)
+ }
+
@Provides
@Singleton
fun provideTokenAuthenticator(
diff --git a/app/src/main/java/com/eatssu/android/di/network/NetworkErrorInterceptor.kt b/app/src/main/java/com/eatssu/android/di/network/NetworkErrorInterceptor.kt
new file mode 100644
index 000000000..47d45a8c9
--- /dev/null
+++ b/app/src/main/java/com/eatssu/android/di/network/NetworkErrorInterceptor.kt
@@ -0,0 +1,58 @@
+package com.eatssu.android.di.network
+
+import android.content.Context
+import android.content.Intent
+import com.eatssu.android.data.dto.response.BaseResponse
+import com.eatssu.android.presentation.error.ErrorActivity
+import com.google.gson.Gson
+import okhttp3.Interceptor
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.Protocol
+import okhttp3.Response
+import okhttp3.ResponseBody.Companion.toResponseBody
+import java.io.IOException
+import javax.inject.Inject
+
+
+/**
+ * 네트워크 오류를 처리하는 인터셉터
+ * IOException(SocketTimeoutException, UnknownHostException) 발생 시 AlertDialog를 띄우는 ErrorActivity로 이동
+ */
+class NetworkErrorInterceptor @Inject constructor(
+ private val context: Context,
+) : Interceptor {
+
+ companion object {
+ private val gson = Gson()
+ }
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val request = chain.request()
+
+ try {
+ return chain.proceed(request)
+ } catch (e: IOException) {
+ val intent = Intent(context, ErrorActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra("message", "서버 통신에 실패했습니다. 잠시 후 다시 시도해 주세요.")
+ }
+ context.startActivity(intent)
+
+ val baseResponse = BaseResponse(
+ isSuccess = false,
+ code = 500, // 서버 처리 오류인지 통신 불가인지 구분
+ message = "서버 통신 실패",
+ )
+ val json = gson.toJson(baseResponse)
+ val responseBody = json.toResponseBody("application/json".toMediaTypeOrNull())
+
+ return Response.Builder()
+ .request(request)
+ .protocol(Protocol.HTTP_1_1)
+ .code(200) // HTTP 응답 코드는 200으로 해야 Retrofit에서 에러로 처리하지 않음
+ .message("서버 통신 실패")
+ .body(responseBody)
+ .build()
+ }
+ }
+}
diff --git a/app/src/main/java/com/eatssu/android/presentation/error/ErrorActivity.kt b/app/src/main/java/com/eatssu/android/presentation/error/ErrorActivity.kt
new file mode 100644
index 000000000..08e32313e
--- /dev/null
+++ b/app/src/main/java/com/eatssu/android/presentation/error/ErrorActivity.kt
@@ -0,0 +1,31 @@
+package com.eatssu.android.presentation.error
+
+import android.app.AlertDialog
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.eatssu.android.databinding.ActivityErrorBinding
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class ErrorActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val binding = ActivityErrorBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ showDialog()
+ }
+
+ private fun showDialog() {
+ val message = intent.getStringExtra("message") ?: "알 수 없는 문제가 발생했습니다. 잠시 후 다시 시도해 주세요."
+
+ AlertDialog.Builder(this)
+ .setTitle("알림")
+ .setMessage(message)
+ .setCancelable(false)
+ .setPositiveButton("확인") { _, _ -> finish() }
+ .setOnDismissListener { finish() }
+ .show()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt
index dcbcdbc8b..eb62b81a8 100644
--- a/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt
+++ b/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt
@@ -59,7 +59,7 @@ class IntroViewModel @Inject constructor(
.collect {
if (it.result == true) { //토큰이 있고 유효함
_uiState.value = UiState.Success(IntroState.ValidToken)
- } else { //토큰이 있어도 유효하지 않음
+ } else if (it.code != 500) { // 토큰이 있어도 유효하지 않음
_uiState.value = UiState.Error
_uiEvent.emit(UiEvent.ShowToast("로그인이 필요합니다"))
}
diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml
new file mode 100644
index 000000000..95e0d8ef3
--- /dev/null
+++ b/app/src/main/res/layout/activity_error.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file