Skip to content

chore: debug, release 앱 환경 분리 #107

Merged
easyhooon merged 5 commits intodevelopfrom
BOOK-219-chore/#106
Aug 5, 2025
Merged

chore: debug, release 앱 환경 분리 #107
easyhooon merged 5 commits intodevelopfrom
BOOK-219-chore/#106

Conversation

@easyhooon
Copy link
Copy Markdown
Contributor

@easyhooon easyhooon commented Aug 5, 2025

🔗 관련 이슈

📙 작업 설명

  • debug, release 앱 환경 분리
  • release signingConfig 설정
  • release keyHash 카카오 디벨로퍼에 등록 -> release 환경에서 카카오 로그인 동작 확인

🧪 테스트 내역 (선택)

  • 주요 기능 정상 동작 확인
  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

📸 스크린샷 또는 시연 영상 (선택)

image

💬 추가 설명 or 리뷰 포인트 (선택)

  • 이후 출시할때 playStore SHA-1 key 카카오 디벨로퍼에 등록해줘야함(메모)
  • reed.jks 파일 및 keystore.properties 파일 노션에 올려두도록 하겠습니다. key password 별로 맘에 안들면 스토어 배포이전에 수정하면 될듯함다
  • 참고로 아직 release server baseUrl이 나오지 않아서 baseUrl dev를 바라보게 설정해야 로그인 정상적으로 동작합니다

Summary by CodeRabbit

  • 새로운 기능

    • 앱 이름이 빌드 타입에 따라 동적으로 변경됩니다. (정식: "Reed", 개발: "Reed.dev")
    • keystore 관련 파일 관리가 개선되어 보안성이 향상되었습니다.
  • 버그 수정

    • 앱 이름 리소스가 중복 정의되던 문제를 해결했습니다.
  • 문서화

    • 빌드 및 배포 관련 워크플로우가 업데이트되었습니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 5, 2025

Walkthrough

이번 변경에서는 Android 프로젝트의 debug와 release 빌드 환경을 명확히 분리하도록 빌드 스크립트와 관련 리소스, 설정 파일이 수정되었습니다. keystore 관리, 앱 이름 및 패키지명, 서명 설정, 문자열 리소스가 빌드 타입에 따라 분리 적용되도록 구성되었습니다.

Changes

Cohort / File(s) Change Summary
Keystore 관리 설정
.gitignore
keystore.properties 파일을 git에서 무시하도록 패턴 추가
빌드 환경 분리 및 서명 설정
app/build.gradle.kts
debug/release 빌드 타입 분리, 각 빌드에 맞는 앱 이름/패키지명/서명/최적화 옵션 적용, keystore.properties 로드 추가
앱 이름 동적 지정
app/src/main/AndroidManifest.xml
<application>android:label을 정적 문자열에서 ${appName} 플레이스홀더로 변경
앱 이름 리소스 추가
app/src/main/res/values/strings.xml
app_name(Reed), app_name_dev(Reed.dev) 문자열 리소스 추가
중복 앱 이름 리소스 제거
core/designsystem/src/main/res/values/strings.xml
app_name 문자열 리소스 제거
CI 워크플로우 수정
.github/workflows/android-ci.yml
CI 빌드에서 keystore.properties 파일을 시크릿으로부터 생성하는 단계 추가

Sequence Diagram(s)

sequenceDiagram
    participant Developer
    participant Gradle
    participant Keystore
    participant Manifest
    participant Resources

    Developer->>Gradle: 빌드 실행 (debug/release)
    Gradle->>Keystore: keystore.properties 로드 (release 시)
    Gradle->>Manifest: appName 플레이스홀더 설정 (debug: app_name_dev, release: app_name)
    Gradle->>Resources: strings.xml에서 해당 앱 이름 리소스 적용
    Manifest->>Gradle: 앱 라벨에 적용
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
debug, release 앱 환경 분리 (#106)

Assessment against linked issues: Out-of-scope changes

(해당 변경사항 내에서 linked issue(#106)와 무관한 기능적 코드 변경은 발견되지 않았습니다.)

Poem

🐰
빌드가 달라, 이름도 달라
debug는 Reed.dev, release는 Reed라네
keystore는 비밀이야, git엔 안 올려
환경이 분리돼서 토끼도 안심!
hop hop, 앱도 토끼처럼 깔끔하게 뛰어오르네!
🌱✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec06d98 and 693c615.

📒 Files selected for processing (3)
  • .github/workflows/android-ci.yml (1 hunks)
  • app/build.gradle.kts (2 hunks)
  • app/src/main/AndroidManifest.xml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/AndroidManifest.xml
  • .github/workflows/android-ci.yml
  • app/build.gradle.kts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci-build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-219-chore/#106

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/src/main/res/values/strings.xml (1)

1-4: translatable="false" 속성 누락

앱 이름은 현 시점에서 다국어 번역 대상이 아니므로 번들 크기를 줄이고 번역 파일 노이즈를 방지하기 위해 translatable="false"를 명시해두는 것을 권장합니다.

-    <string name="app_name">Reed</string>
-    <string name="app_name_dev">Reed.dev</string>
+    <string name="app_name" translatable="false">Reed</string>
+    <string name="app_name_dev" translatable="false">Reed.dev</string>
app/build.gradle.kts (2)

28-35: debug 빌드 설정 중복 표시

isDebuggable = true는 이미 Gradle 기본값입니다. 필요 없는 설정은 제거하여 스크립트 가독성을 높일 수 있습니다.


102-104: API Key 로딩 함수, 공백·줄바꿈 처리 확인 필요

gradleLocalProperties에서 읽은 값이 개행이나 공백을 포함할 경우 런타임 오류가 발생할 수 있습니다. trim() 추가 여부를 검토해주세요.
(현재 manifestPlaceholders에서는 trim('"') 처리로 안전하지만 buildConfigField에는 그대로 전달됩니다.)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between afa26ff and eb89b8b.

📒 Files selected for processing (5)
  • .gitignore (1 hunks)
  • app/build.gradle.kts (2 hunks)
  • app/src/main/AndroidManifest.xml (1 hunks)
  • app/src/main/res/values/strings.xml (1 hunks)
  • core/designsystem/src/main/res/values/strings.xml (0 hunks)
💤 Files with no reviewable changes (1)
  • core/designsystem/src/main/res/values/strings.xml
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt:11-18
Timestamp: 2025-07-31T16:58:59.404Z
Learning: Reed-Android 프로젝트는 현재 다국어 지원 계획이 없어서 모델에 한글 문자열을 직접 포함하는 것이 허용된다.
📚 Learning: reed-android 프로젝트는 현재 다국어 지원 계획이 없어서 모델에 한글 문자열을 직접 포함하는 것이 허용된다....
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt:11-18
Timestamp: 2025-07-31T16:58:59.404Z
Learning: Reed-Android 프로젝트는 현재 다국어 지원 계획이 없어서 모델에 한글 문자열을 직접 포함하는 것이 허용된다.

Applied to files:

  • app/src/main/res/values/strings.xml
📚 Learning: reed-android 프로젝트에서 core:model 모듈은 순수 kotlin 모듈이므로 android 리소스(r.string 등)에 접근할 수 없다....
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt:11-18
Timestamp: 2025-07-31T16:58:59.404Z
Learning: Reed-Android 프로젝트에서 core:model 모듈은 순수 Kotlin 모듈이므로 Android 리소스(R.string 등)에 접근할 수 없다.

Applied to files:

  • app/src/main/res/values/strings.xml
📚 Learning: reed-android 프로젝트에서는 api가 준비되지 않은 상황에서 ui를 먼저 구현하고, api 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. ...
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Applied to files:

  • app/src/main/res/values/strings.xml
📚 Learning: reed android 프로젝트에서 타이포그래피 사용 규칙: 톱 앱바(top app bar)에서는 `headline2semibold`를 사용하고, 바텀시트(bottom sheet)...
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#45
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/appbar/ReedTopAppBar.kt:65-65
Timestamp: 2025-07-12T01:33:57.101Z
Learning: Reed Android 프로젝트에서 타이포그래피 사용 규칙: 톱 앱바(Top App Bar)에서는 `headline2SemiBold`를 사용하고, 바텀시트(Bottom Sheet)에서는 `heading2SemiBold`를 사용한다. 이는 의도적인 디자인 시스템 차별화이다.

Applied to files:

  • app/src/main/res/values/strings.xml
📚 Learning: reed android 프로젝트에서는 다크모드/라이트모드 대응 계획이 없고 기본 테마 값만 사용할 예정이다....
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#32
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Theme.kt:15-21
Timestamp: 2025-07-08T03:21:24.693Z
Learning: Reed Android 프로젝트에서는 다크모드/라이트모드 대응 계획이 없고 기본 테마 값만 사용할 예정이다.

Applied to files:

  • app/src/main/res/values/strings.xml
📚 Learning: reed-android 프로젝트에서는 `booket.android.feature` convention plugin을 사용하여 feature 모듈들의 공통 의존성을 관리한다. 이 p...
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#61
File: feature/webview/build.gradle.kts:17-21
Timestamp: 2025-07-20T12:34:23.786Z
Learning: Reed-Android 프로젝트에서는 `booket.android.feature` convention plugin을 사용하여 feature 모듈들의 공통 의존성을 관리한다. 이 plugin은 Circuit, Compose, 그리고 core 모듈들의 의존성을 자동으로 포함하므로, 각 feature 모듈의 build.gradle.kts에서는 특별한 의존성(예: libs.logger, libs.kakao.auth)만 별도로 선언하면 된다.

Applied to files:

  • app/build.gradle.kts
📚 Learning: the project uses kotlinx serialization with json configuration that includes `ignoreunknownkeys = tr...
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#46
File: core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/BookSearchResponse.kt:33-44
Timestamp: 2025-07-14T00:51:38.952Z
Learning: The project uses Kotlinx Serialization with JSON configuration that includes `ignoreUnknownKeys = true`, `encodeDefaults = true`, `isLenient = true`, and `prettyPrint = true` to handle API response parsing robustly, making additional null safety measures less critical if the API consistently provides all expected fields.

Applied to files:

  • app/build.gradle.kts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci-build
🔇 Additional comments (3)
.gitignore (1)

30-33: keystore.properties ignore 추가 👍

릴리스 서명 정보가 담긴 keystore.properties를 VCS 추적 대상에서 제외한 결정은 보안·컴플라이언스 측면에서 적절합니다.
더 이상 조치할 사항 없습니다.

app/src/main/AndroidManifest.xml (1)

18-18: 빌드 타입별 앱 이름 플레이스홀더 적용 확인 완료

${appName} 플레이스홀더 도입으로 Debug/Release 환경 분리가 잘 반영되었습니다. 빌드 스크립트에서도 두 타입 모두에 매핑이 정의돼 있어 누락 위험이 없습니다.

app/build.gradle.kts (1)

36-44: manifestPlaceholders += 사용 시 초기화 순서 주의

현재는 정상 동작하지만, 빌드 스크립트의 다른 플러그인(예: Firebase Crashlytics)이 manifestPlaceholders에 쓰기 전에 실행될 경우 덮어쓰기 위험이 있습니다. 명확성을 위해 manifestPlaceholders = mapOf(...)로 지정하거나 putAll 호출을 권장합니다.

Comment thread app/build.gradle.kts
Comment on lines +15 to 25
signingConfigs {
create("release") {
val propertiesFile = rootProject.file("keystore.properties")
val properties = Properties()
properties.load(propertiesFile.inputStream())
storeFile = rootProject.file(properties["STORE_FILE"] as String)
storePassword = properties["STORE_PASSWORD"] as String
keyAlias = properties["KEY_ALIAS"] as String
keyPassword = properties["KEY_PASSWORD"] as String
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

keystore.properties 존재 여부 미검사 → CI 빌드 실패 가능성

rootProject.file("keystore.properties")가 존재하지 않는 환경(예: PR CI, 외부 컨트리뷰터)에서 inputStream() 호출 시 FileNotFoundException으로 빌드가 즉시 실패합니다.
파일 부재 시 디버그·스냅샷 빌드는 서명 없이 통과하도록 가드 로직을 넣어주세요.

-val propertiesFile = rootProject.file("keystore.properties")
-val properties = Properties()
-properties.load(propertiesFile.inputStream())
+val propertiesFile = rootProject.file("keystore.properties")
+if (!propertiesFile.exists()) {
+    logger.warn("⚠️  keystore.properties not found – skipping release signing config.")
+    return@create               // signingConfig.release 생성 중단
+}
+val properties = Properties().apply {
+    propertiesFile.inputStream().use { load(it) }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
signingConfigs {
create("release") {
val propertiesFile = rootProject.file("keystore.properties")
val properties = Properties()
properties.load(propertiesFile.inputStream())
storeFile = rootProject.file(properties["STORE_FILE"] as String)
storePassword = properties["STORE_PASSWORD"] as String
keyAlias = properties["KEY_ALIAS"] as String
keyPassword = properties["KEY_PASSWORD"] as String
}
}
signingConfigs {
create("release") {
val propertiesFile = rootProject.file("keystore.properties")
if (!propertiesFile.exists()) {
logger.warn("⚠️ keystore.properties not found – skipping release signing config.")
return@create
}
val properties = Properties().apply {
propertiesFile.inputStream().use { load(it) }
}
storeFile = rootProject.file(properties["STORE_FILE"] as String)
storePassword = properties["STORE_PASSWORD"] as String
keyAlias = properties["KEY_ALIAS"] as String
keyPassword = properties["KEY_PASSWORD"] as String
}
}
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 15 to 25, the code assumes the
keystore.properties file always exists, causing a build failure if it is
missing. Add a check to verify if the keystore.properties file exists before
loading it. If the file is absent, skip the signing configuration to allow debug
and snapshot builds to proceed without signing, preventing CI build failures in
environments without the file.

Comment thread app/build.gradle.kts
Comment on lines +20 to +24
storeFile = rootProject.file(properties["STORE_FILE"] as String)
storePassword = properties["STORE_PASSWORD"] as String
keyAlias = properties["KEY_ALIAS"] as String
keyPassword = properties["KEY_PASSWORD"] as String
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

NPE 위험: 프로퍼티 강제 캐스팅

properties["STORE_FILE"] as String 식은 키 누락 시 NullPointerException을 유발합니다. 안전한 프로퍼티 조회로 변경해 주세요.

-storeFile = rootProject.file(properties["STORE_FILE"] as String)
-storePassword = properties["STORE_PASSWORD"] as String
-keyAlias = properties["KEY_ALIAS"] as String
-keyPassword = properties["KEY_PASSWORD"] as String
+storeFile = rootProject.file(requireNotNull(properties["STORE_FILE"]) { "STORE_FILE missing" } as String)
+storePassword = requireNotNull(properties["STORE_PASSWORD"]) { "STORE_PASSWORD missing" } as String
+keyAlias = requireNotNull(properties["KEY_ALIAS"]) { "KEY_ALIAS missing" } as String
+keyPassword = requireNotNull(properties["KEY_PASSWORD"]) { "KEY_PASSWORD missing" } as String
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
storeFile = rootProject.file(properties["STORE_FILE"] as String)
storePassword = properties["STORE_PASSWORD"] as String
keyAlias = properties["KEY_ALIAS"] as String
keyPassword = properties["KEY_PASSWORD"] as String
}
storeFile = rootProject.file(requireNotNull(properties["STORE_FILE"]) { "STORE_FILE missing" } as String)
storePassword = requireNotNull(properties["STORE_PASSWORD"]) { "STORE_PASSWORD missing" } as String
keyAlias = requireNotNull(properties["KEY_ALIAS"]) { "KEY_ALIAS missing" } as String
keyPassword = requireNotNull(properties["KEY_PASSWORD"]) { "KEY_PASSWORD missing" } as String
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 20 to 24, the code uses forced casting like
properties["STORE_FILE"] as String, which can cause a NullPointerException if
the key is missing. Change these to safely retrieve the properties by using safe
calls or providing default values, for example by using
properties.get("STORE_FILE")?.toString() or checking for null before assignment
to avoid runtime exceptions.

@seoyoon513
Copy link
Copy Markdown
Contributor

reed.jks 파일 및 keystore.properties 파일 노션에 올려두도록 하겠습니다. key password 별로 맘에 안들면 스토어 배포이전에 수정하면 될듯함다

요건 노션 어느 페이지에서 확인할 수 있나요??

@easyhooon
Copy link
Copy Markdown
Contributor Author

요건 노션 어느 페이지에서 확인할 수 있나요??

방금 올렸어염

@easyhooon
Copy link
Copy Markdown
Contributor Author

easyhooon commented Aug 5, 2025

@seoyoon513 dev, release 앱을 분리하면서 google-services.json 도 업데이트 되었슴다.(.dev android_client_info 추가) 노션에 업데이트해두었으니 프로젝트 app 모듈에 있는 파일 업데이트 부탁함다

Copy link
Copy Markdown
Contributor

@seoyoon513 seoyoon513 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM~

@easyhooon easyhooon merged commit 6698a94 into develop Aug 5, 2025
3 of 5 checks passed
@easyhooon easyhooon deleted the BOOK-219-chore/#106 branch August 5, 2025 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-219/chore] debug, release 앱 환경 분리

2 participants