Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
857eb3d
feat: core:data 모듈 추가
HamBeomJoon Jan 19, 2026
ef77abf
feat: core:network 모듈 생성 및 네트워크 라이브러리 설정
HamBeomJoon Jan 19, 2026
335d4f3
feat: 네트워크 모듈 기본 설정 및 유틸리티 구현
HamBeomJoon Jan 19, 2026
e6b6ec9
feat: 네트워크 모듈 구조 개선 및 Ktorfit 컨버터 도입
HamBeomJoon Jan 19, 2026
a1a1ba3
refactor: ApiResponse 구조 개선 및 예외 처리 로직 수정
HamBeomJoon Jan 19, 2026
8c7694d
feat: ApiResponseConverterFactory 내 에러 로깅 추가
HamBeomJoon Jan 19, 2026
1c55e8b
refactor: ApiResponse.Failure.NetworkError 구조 변경 및 예외 전달 처리
HamBeomJoon Jan 19, 2026
47a3623
chore: NetworkModule JSON prettyPrint 비활성화
HamBeomJoon Jan 19, 2026
1f3d7c3
feat: 빌드 타입별 BASE_URL 설정 및 localProperty 확장 함수 추가
HamBeomJoon Jan 19, 2026
b740bdf
build: Hilt 컨벤션 플러그인 내 kotlin-metadata-jvm 의존성 추가
HamBeomJoon Jan 19, 2026
b7332d1
refactor: local.properties 로드 로직 개선 및 예외 처리 추가
HamBeomJoon Jan 19, 2026
edaabcf
refactor: local.properties 로드 방식 개선 및 BASE_URL 설정 로직 수정
HamBeomJoon Jan 19, 2026
66fc4d5
refactor: 사용하지 않는 로거 및 임포트 제거
HamBeomJoon Jan 19, 2026
f19c3b3
refactor: build-logic 내부 구현을 위한 패키지 구조 변경
moondev03 Jan 19, 2026
1bef85c
refactor(network): ApiResponseConverterFactory 로직 개선
moondev03 Jan 19, 2026
35377fb
refactor(network): ApiResponseConverterFactory 로직 개선
moondev03 Jan 19, 2026
2afbabe
feat(detekt): TooGenericExceptionCaught 비활성화
moondev03 Jan 19, 2026
d75301a
feat: BuildConfig BASE_URL 설정 방식 변경
moondev03 Jan 19, 2026
6813338
build: local.properties 접근 방식 개선 및 BASE_URL 설정 로직 수정
HamBeomJoon Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Prezel/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
plugins {
alias(libs.plugins.prezel.android.application)
alias(libs.plugins.prezel.android.application.compose)
alias(libs.plugins.prezel.hilt)
}

android {
namespace = "com.team.prezel"

buildFeatures {
buildConfig = true
}
}

dependencies {
implementation(projects.core.data)

implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.timber)
}
2 changes: 1 addition & 1 deletion Prezel/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:name=".PrezelApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -13,7 +14,6 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Prezel">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
15 changes: 15 additions & 0 deletions Prezel/app/src/main/java/com/team/prezel/PrezelApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.team.prezel

import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class PrezelApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.team.prezel.buildlogic.convention.external

import org.gradle.api.Project
import org.gradle.api.provider.Provider
import java.io.StringReader
import java.util.Properties

fun Project.localProperty(key: String): Provider<String> {
val localPropertiesFile = isolated.rootProject.projectDirectory.file("local.properties")

return providers.provider {
val file = localPropertiesFile.asFile
if (!file.exists()) {
logger.warn("local.properties not found")
return@provider null
}

val properties = Properties()
properties.load(StringReader(file.readText()))
val value = properties.getProperty(key)

if (value == null) {
logger.warn("Key '$key' not found in local.properties")
}

value
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.team.prezel.buildlogic.convention
package com.team.prezel.buildlogic.convention.internal

import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.team.prezel.buildlogic.convention
package com.team.prezel.buildlogic.convention.internal

import com.android.build.api.dsl.CommonExtension
import org.gradle.api.JavaVersion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.team.prezel.buildlogic.convention
package com.team.prezel.buildlogic.convention.internal

import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType

val Project.libs
internal val Project.libs
get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named("libs")
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.team.prezel.buildlogic.convention.plugin

import com.android.build.api.dsl.ApplicationExtension
import com.team.prezel.buildlogic.convention.configureAndroidCompose
import com.team.prezel.buildlogic.convention.internal.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.team.prezel.buildlogic.convention.plugin

import com.android.build.api.dsl.ApplicationExtension
import com.team.prezel.buildlogic.convention.configureKotlinAndroid
import com.team.prezel.buildlogic.convention.internal.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.team.prezel.buildlogic.convention.plugin

import com.android.build.api.dsl.LibraryExtension
import com.team.prezel.buildlogic.convention.libs
import com.team.prezel.buildlogic.convention.internal.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.team.prezel.buildlogic.convention.plugin

import com.android.build.api.dsl.LibraryExtension
import com.team.prezel.buildlogic.convention.configureAndroidCompose
import com.team.prezel.buildlogic.convention.internal.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.team.prezel.buildlogic.convention.plugin

import com.android.build.api.dsl.LibraryExtension
import com.team.prezel.buildlogic.convention.configureKotlinAndroid
import com.team.prezel.buildlogic.convention.libs
import com.team.prezel.buildlogic.convention.internal.configureKotlinAndroid
import com.team.prezel.buildlogic.convention.internal.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.team.prezel.buildlogic.convention.plugin

import com.team.prezel.buildlogic.convention.libs
import com.team.prezel.buildlogic.convention.internal.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand All @@ -19,6 +19,7 @@ class HiltConventionPlugin : Plugin<Project> {

dependencies {
"ksp"(libs.findLibrary("hilt.compiler").get())
"ksp"(libs.findLibrary("kotlin.metadata.jvm").get())
}

pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.team.prezel.buildlogic.convention.plugin

import com.team.prezel.buildlogic.convention.configureKotlinJvm
import com.team.prezel.buildlogic.convention.libs
import com.team.prezel.buildlogic.convention.internal.configureKotlinJvm
import com.team.prezel.buildlogic.convention.internal.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand Down
1 change: 1 addition & 0 deletions Prezel/core/data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
14 changes: 14 additions & 0 deletions Prezel/core/data/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
alias(libs.plugins.prezel.android.library)
alias(libs.plugins.prezel.hilt)
}

android {
namespace = "com.team.prezel.core.data"
testOptions.unitTests.isIncludeAndroidResources = true
Comment thread
moondev03 marked this conversation as resolved.
}

dependencies {
implementation(projects.core.network)
implementation(libs.kotlinx.coroutines.core)
}
Empty file.
21 changes: 21 additions & 0 deletions Prezel/core/data/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
4 changes: 4 additions & 0 deletions Prezel/core/data/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.team.prezel.core.data

import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule
Empty file.
1 change: 1 addition & 0 deletions Prezel/core/network/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
47 changes: 47 additions & 0 deletions Prezel/core/network/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import com.android.build.api.variant.BuildConfigField
import com.team.prezel.buildlogic.convention.external.localProperty

plugins {
alias(libs.plugins.prezel.android.library)
alias(libs.plugins.prezel.hilt)
alias(libs.plugins.kotlinx.serialization)
}

android {
buildFeatures {
buildConfig = true
}

namespace = "com.team.prezel.core.network"
testOptions.unitTests.isIncludeAndroidResources = true
}

dependencies {
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.client.logging)
implementation(libs.kotlinx.serialization.json)
implementation(libs.timber)

implementation(libs.ktorfit.lib)
ksp(libs.ktorfit.ksp)

testImplementation(libs.kotlinx.coroutines.test)
}

androidComponents {
onVariants { variant ->
val buildType = variant.buildType ?: return@onVariants
val buildConfigFields = variant.buildConfigFields ?: return@onVariants
val baseUrlKey = "${buildType.uppercase()}_BASE_URL"

buildConfigFields.put(
"BASE_URL",
localProperty(baseUrlKey).map { value ->
BuildConfigField("String", "\"$value\"", null)
},
)
Comment thread
moondev03 marked this conversation as resolved.
}
}
Empty file.
21 changes: 21 additions & 0 deletions Prezel/core/network/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
6 changes: 6 additions & 0 deletions Prezel/core/network/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.team.prezel.core.network

import com.team.prezel.core.network.model.ApiResponse
import de.jensklingenberg.ktorfit.Ktorfit
import de.jensklingenberg.ktorfit.converter.Converter
import de.jensklingenberg.ktorfit.converter.KtorfitResult
import de.jensklingenberg.ktorfit.converter.TypeData
import io.ktor.client.call.body
import io.ktor.client.plugins.ResponseException
import io.ktor.client.statement.HttpResponse
import io.ktor.util.reflect.TypeInfo
import timber.log.Timber
import java.io.IOException
import kotlin.coroutines.cancellation.CancellationException

class ApiResponseConverterFactory : Converter.Factory {
override fun suspendResponseConverter(
typeData: TypeData,
ktorfit: Ktorfit,
): Converter.SuspendResponseConverter<HttpResponse, *>? {
if (typeData.typeInfo.type != ApiResponse::class) return null
val bodyTypeInfo = typeData.typeArgs.firstOrNull()?.typeInfo ?: return null

return object : Converter.SuspendResponseConverter<HttpResponse, ApiResponse<Any>> {
override suspend fun convert(result: KtorfitResult): ApiResponse<Any> =
when (result) {
is KtorfitResult.Success -> parseSuccess(result.response, bodyTypeInfo)
is KtorfitResult.Failure -> mapFailure(result.throwable)
}
}
}

private suspend fun parseSuccess(
response: HttpResponse,
bodyTypeInfo: TypeInfo,
): ApiResponse<Any> =
try {
val body = response.body<Any>(bodyTypeInfo)
ApiResponse.Success(body)
} catch (t: Throwable) {
t.rethrowIfCancellation()
Timber.e(t, "Response parsing failed")
ApiResponse.Failure.NetworkError(t)
}

private fun mapFailure(t: Throwable): ApiResponse<Any> {
t.rethrowIfCancellation()

return when (t) {
is IOException -> {
Timber.e(t, "Network error")
ApiResponse.Failure.NetworkError(t)
}

is ResponseException -> {
Timber.e(t, "HTTP error ${t.response.status.value}")
ApiResponse.Failure.HttpError(t)
}

else -> {
Timber.e(t, "Unknown error")
ApiResponse.Failure.NetworkError(t)
}
}
}

private fun Throwable.rethrowIfCancellation() {
if (this is CancellationException) throw this
}
}
Loading