Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ insert_final_newline = true
trim_trailing_whitespace = true

[*.{kt,kts}]
# CI(ScaCap/action-ktlint, android: true)와 동일한 Android 스타일 강제
ktlint_code_style = android_studio

# Composable 함수는 대문자 시작 허용 (PascalCase)
ktlint_function_naming_ignore_when_annotated_with = Composable

Expand All @@ -18,4 +21,3 @@ ktlint_standard_no-wildcard-imports = disabled

[*.md]
trim_trailing_whitespace = false

50 changes: 32 additions & 18 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,40 @@ jobs:
java-version: '17'
cache: 'gradle'

# fail-fast — secret 누락 시 Gradle 까지 가지 않고 즉시 실패.
# secret 값은 echo 하지 않음 (env 로만 받아서 길이 검사). 누락 키 이름은 안전.
- name: Verify required secrets are set
env:
GOOGLE_SERVICES_JSON_B64: ${{ secrets.GOOGLE_SERVICES_JSON_B64 }}
run: |
missing=""
[ -z "$GOOGLE_SERVICES_JSON_B64" ] && missing="$missing GOOGLE_SERVICES_JSON_B64"
if [ -n "$missing" ]; then
echo "::error::필수 secret 누락:$missing — Settings → Secrets and variables → Actions 에 등록"
exit 1
fi

# google-services plugin 이 app/google-services.json 을 요구. 파일은 .gitignore 라
# CI 환경엔 없음. base64 인코딩된 secret 으로 받아서 decode.
- name: Decode google-services.json
env:
GOOGLE_SERVICES_JSON_B64: ${{ secrets.GOOGLE_SERVICES_JSON_B64 }}
# CI 환경엔 없음. Android Lint 는 실제 Firebase 가 불필요하므로 stub JSON 생성
# (plugin 통과 목적, secret 비의존).
- name: Create stub google-services.json (CI only)
run: |
echo "$GOOGLE_SERVICES_JSON_B64" | base64 -d > app/google-services.json
cat > app/google-services.json <<'EOF'
{
"project_info": {
"project_number": "000000000000",
"project_id": "hackathon-ci-stub",
"storage_bucket": "hackathon-ci-stub.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000000000",
"android_client_info": {
"package_name": "com.example.hackathon"
}
},
"oauth_client": [],
"api_key": [
{ "current_key": "AIzaSyDfake_ci_only_no_real_value_xxxxxxxxxxx" }
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
EOF

- name: Run Android Lint
run: ./gradlew lintDebug --continue --parallel
Expand Down
51 changes: 32 additions & 19 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,39 @@ jobs:
java-version: '17'
cache: 'gradle'

# fail-fast — secret 누락 시 Gradle 까지 가지 않고 즉시 실패.
# secret 값은 echo 하지 않음 (env 로만 받아서 길이 검사). 누락 키 이름은 안전.
- name: Verify required secrets are set
env:
GOOGLE_SERVICES_JSON_B64: ${{ secrets.GOOGLE_SERVICES_JSON_B64 }}
# google-services plugin 이 app/google-services.json 을 요구. 파일은 .gitignore 라 CI 엔 없음.
# unit test 는 실제 Firebase 가 불필요하므로 stub JSON 생성 (plugin 통과 목적, secret 비의존).
- name: Create stub google-services.json (CI only)
run: |
missing=""
[ -z "$GOOGLE_SERVICES_JSON_B64" ] && missing="$missing GOOGLE_SERVICES_JSON_B64"
if [ -n "$missing" ]; then
echo "::error::필수 secret 누락:$missing — Settings → Secrets and variables → Actions 에 등록"
exit 1
fi

# google-services plugin 이 app/google-services.json 을 요구. 파일은 .gitignore 라
# CI 환경엔 없음. base64 인코딩된 secret 으로 받아서 decode.
- name: Decode google-services.json
env:
GOOGLE_SERVICES_JSON_B64: ${{ secrets.GOOGLE_SERVICES_JSON_B64 }}
run: |
echo "$GOOGLE_SERVICES_JSON_B64" | base64 -d > app/google-services.json
cat > app/google-services.json <<'EOF'
{
"project_info": {
"project_number": "000000000000",
"project_id": "hackathon-ci-stub",
"storage_bucket": "hackathon-ci-stub.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000000000",
"android_client_info": {
"package_name": "com.example.hackathon"
}
},
"oauth_client": [],
"api_key": [
{ "current_key": "AIzaSyDfake_ci_only_no_real_value_xxxxxxxxxxx" }
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
EOF

- name: Run unit tests
run: ./gradlew testDebugUnitTest --continue --parallel
Expand Down
107 changes: 23 additions & 84 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,36 +1,23 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
alias(libs.plugins.detekt)
id("hackathon.android.application")
id("hackathon.android.application.compose")
id("hackathon.android.hilt")
alias(libs.plugins.google.services)
}

android {
namespace = "com.example.hackathon"
compileSdk {
version = release(36)
}

defaultConfig {
applicationId = "com.example.hackathon"
minSdk = 36
targetSdk = 36
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

// 서버 준비 전까지 Mock 모드 사용 (true: Mock, false: 실제 API)
// HackathonApplication / MainViewModel 의 Mock 모드 분기용
buildConfigField("boolean", "USE_MOCK_API", "false")
buildConfigField("String", "BASE_URL", "\"http://13.125.27.133/\"")
}

buildFeatures {
compose = true
buildConfig = true
}

Expand All @@ -43,93 +30,45 @@ android {
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 // Detekt 호환성을 위해 17로 설정
}
kotlinOptions {
jvmTarget = "17" // Java 컴파일러와 일치시킴 (compileOptions와 동일)
}
}

dependencies {
implementation("androidx.core:core-splashscreen:1.2.0")
// 모듈 의존
implementation(project(":core:model"))
implementation(project(":core:domain"))
implementation(project(":core:data"))
implementation(project(":core:network"))
implementation(project(":core:datastore"))
implementation(project(":core:designsystem"))
implementation(project(":feature:auth"))
implementation(project(":feature:home"))
implementation(project(":feature:combination"))
implementation(project(":feature:onboarding"))
implementation(project(":feature:profile"))

// AndroidX / Compose
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.foundation.layout)

// Navigation
implementation(libs.androidx.navigation.compose)

// Network
implementation(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit)
implementation(libs.retrofit.kotlinx.serialization)
implementation(libs.kotlinx.serialization.json)

// Image Loading
implementation(libs.coil.compose)
implementation(libs.coil.network.okhttp)

// Hilt
implementation(libs.hilt.android)
implementation(libs.androidx.compose.foundation.layout)
ksp(libs.hilt.compiler)
implementation(libs.hilt.navigation.compose)

// DataStore
implementation(libs.androidx.datastore.preferences)

// Kotlin Metadata (메타데이터 버전 호환성 해결)
// Kotlin 2.0+에서는 kotlin-metadata-jvm 사용 (kotlinx가 아님)
implementation("org.jetbrains.kotlin:kotlin-metadata-jvm:2.0.0")
implementation(libs.kotlin.metadata.jvm)

// Firebase
implementation(platform("com.google.firebase:firebase-bom:34.7.0"))
implementation("com.google.firebase:firebase-analytics")
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)

testImplementation(libs.junit)
testImplementation(libs.mockk)
testImplementation(libs.turbine)
testImplementation(libs.kotlinx.coroutines.test)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

// Detekt 설정
detekt {
buildUponDefaultConfig = true
allRules = false
config.setFrom("$projectDir/../config/detekt.yml")
baseline = file("$projectDir/../config/detekt-baseline.xml")

// Compose Rules는 나중에 추가 (의존성 문제 해결 후)
// dependencies {
// detektPlugins(libs.compose.rules.detekt)
// }
}


// 코드 품질 검사 통합 Task
tasks.register("codeQualityCheck") {
group = "verification"
description = "Runs all code quality checks (Detekt + Tests)"

dependsOn("detekt", "test")

doLast {
println("✅ All code quality checks completed!")
}
}

15 changes: 9 additions & 6 deletions app/src/main/java/com/example/hackathon/HackathonApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package com.example.hackathon

import android.app.Application
import com.example.hackathon.BuildConfig
import com.example.hackathon.data.local.DummyData
import com.example.hackathon.core.model.DummyData
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class HackathonApplication : Application() {
override fun onCreate() {
super.onCreate()

// 앱 시작 시 currentUser 초기화
// Mock 모드가 아닐 때는 null로 시작 (로그인 안 됨)

// 앱 시작 시 currentUser 초기화: Mock 모드가 아닐 때만 null(비로그인)로 시작.
//
// USE_MOCK_API 는 app/build.gradle.kts 의 buildConfigField 로 주입되는 컴파일 상수다.
// 지금은 false 로 고정돼 있어 IDE 가 "!USE_MOCK_API 는 항상 true" 경고를 띄우지만,
// 이는 단일 빌드의 상수값만 보고 내린 판단이다. Mock/실 API 를 전환하는 빌드 플래그라
// 값을 true 로 바꾸면 분기도 갈린다 → 의도된 동작이므로 경고는 무시 가능.
if (!BuildConfig.USE_MOCK_API) {
DummyData.currentUser = null
}
// Mock 모드일 때도 명시적으로 null로 시작 (실제 로그인 시에만 설정)
// DummyData.currentUser = null
// Mock 모드일 때는 DummyData.currentUser 의 기본값을 그대로 둔다(명시 설정 안 함).
}
}
Loading
Loading