Skip to content

Conversation

@rhkrwngud445
Copy link
Contributor

작업 내용

  • Okhttp, Retrofit
  • 임시 토큰 주입
  • Home 예시 API 연동

확인 방법

홈 화면 진입, 로그 확인

참고 사항

세부 에러 핸들링 시, 에러 로직 구성 필요

관련 이슈

@rhkrwngud445 rhkrwngud445 self-assigned this Aug 20, 2025
@rhkrwngud445 rhkrwngud445 linked an issue Aug 20, 2025 that may be closed by this pull request
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @rhkrwngud445, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 OkHttp와 Retrofit을 사용하여 앱의 기본 API 통신 인프라를 구축하고, 임시 토큰 주입 기능을 추가하며, 홈 화면에 예시 API 연동을 구현합니다. 이를 통해 향후 API 기반 기능 개발을 위한 기반을 마련합니다.

Highlights

  • 네트워크 스택 설정: OkHttp와 Retrofit 라이브러리를 추가하고, Kotlin Serialization을 위한 컨버터를 설정했습니다.
  • API 환경 설정: build.gradle.kts에 개발 및 프로덕션 환경에 따른 API 기본 URL과 디버그 빌드용 임시 토큰을 BuildConfig 필드로 추가했습니다.
  • 인터넷 권한 추가: AndroidManifest.xml에 인터넷 사용 권한을 선언했습니다.
  • Hilt 의존성 주입: 네트워크 모듈(NetworkModule, ServiceModule)과 리포지토리 모듈(RepositoryModule)을 Hilt를 사용하여 구성하여 OkHttp 클라이언트, Retrofit 인스턴스, API 서비스, 그리고 친구 데이터 리포지토리(FriendRepository)를 주입할 수 있도록 했습니다.
  • 토큰 인터셉터 구현: OkHttp 인터셉터를 통해 모든 API 요청에 임시 인증 토큰을 자동으로 추가하도록 구현했습니다.
  • 친구 데이터 모델 및 서비스 추가: 친구 목록을 가져오는 API 서비스(FriendService), 네트워크 응답을 위한 FriendEntity, 그리고 앱 내에서 사용할 Friend 모델 및 매퍼를 정의했습니다.
  • 홈 화면 API 연동: 홈 화면 ViewModel을 FriendRepository를 사용하도록 업데이트하여 친구 목록 데이터를 가져오고 UI에 표시할 수 있도록 했습니다.
  • 초기 시작 화면 변경: 앱 실행 시 시작 화면을 친구 프로필 편집기에서 홈 화면으로 변경했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이 PR은 Retrofit과 OkHttp를 사용하여 기본 API 통신 계층을 설정하는 중요한 작업을 수행합니다. Hilt를 사용한 의존성 주입, Kotlinx Serialization을 사용한 데이터 직렬화, 임시 토큰을 사용한 인증 등 좋은 관행들이 많이 적용되었습니다. 몇 가지 개선 사항을 제안합니다. 주요 내용은 보안(토큰 저장), 에러 처리, 코드 안정성 및 유지보수성 향상에 중점을 둡니다. 전반적으로 프로젝트의 네트워킹 기반을 잘 다지는 훌륭한 시작입니다.

Comment on lines +44 to +51
fun provideLoggingInterceptor(): HttpLoggingInterceptor =
HttpLoggingInterceptor().let {
if (BuildConfig.DEBUG) {
it.setLevel(HttpLoggingInterceptor.Level.BODY)
} else {
it.setLevel(HttpLoggingInterceptor.Level.NONE)
}
}

Choose a reason for hiding this comment

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

critical

현재 provideLoggingInterceptor 함수는 HttpLoggingInterceptor를 반환해야 하지만, let 블록의 마지막 구문인 it.setLevel(...)Unit을 반환하므로 실제로는 Unit이 반환되어 컴파일 오류가 발생합니다. 객체 생성 후 속성을 설정하는 경우에는 apply 스코프 함수를 사용하는 것이 더 관용적이고 안전합니다.

Suggested change
fun provideLoggingInterceptor(): HttpLoggingInterceptor =
HttpLoggingInterceptor().let {
if (BuildConfig.DEBUG) {
it.setLevel(HttpLoggingInterceptor.Level.BODY)
} else {
it.setLevel(HttpLoggingInterceptor.Level.NONE)
}
}
fun provideLoggingInterceptor(): HttpLoggingInterceptor =
HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}

}
debug {
buildConfigField("String", "NEAR_URL", getProperty("NEAR_DEV_URL"))
buildConfigField("String", "TEMP_TOKEN", getProperty("TEMP_TOKEN")) // TODO 추후 삭제 필요

Choose a reason for hiding this comment

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

high

BuildConfig에 토큰과 같은 민감 정보를 저장하는 것은 보안상 매우 위험합니다. APK를 디컴파일하면 이 값을 쉽게 추출할 수 있기 때문입니다. 임시 토큰이라 할지라도, 이러한 방식은 지양하는 것이 좋습니다. 향후에는 Android Keystore 시스템을 사용하거나 Secrets Gradle Plugin과 같은 라이브러리를 활용하여 보다 안전하게 민감 정보를 관리하는 것을 강력히 권장합니다. TODO 주석으로 이미 인지하고 계신 점은 좋지만, 이는 높은 수준의 보안 위험이므로 프로덕션 출시 전 반드시 해결해야 합니다.

Comment on lines +15 to +22
override fun fetchFriends(): Flow<List<Friend>> =
flow {
emit(
friendService.fetchFriends().map {
it.toModel()
},
)
}

Choose a reason for hiding this comment

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

high

현재 DefaultFriendRepositoryfetchFriends 함수는 API 호출 시 발생할 수 있는 예외(네트워크 오류 등)를 처리하지 않습니다. 예외가 발생하면 flow가 비정상적으로 종료되고, 이를 수집하는 ViewModel에서 처리하지 않으면 앱이 강제 종료될 수 있습니다. try-catch 블록을 사용하여 예외를 처리하고, 도메인 특화 예외를 던지거나 Result와 같은 래퍼 클래스를 사용하여 결과를 반환하는 것이 좋습니다.

        override fun fetchFriends(): Flow<List<Friend>> =
            flow {
                try {
                    emit(
                        friendService.fetchFriends().map {
                            it.toModel()
                        },
                    )
                } catch (e: Exception) {
                    // TODO: 도메인에 맞는 에러로 변환하여 처리하거나 로깅 필요
                    throw e
                }
            }

Comment on lines +26 to 33
val friendsFlow: StateFlow<List<Friend>> =
friendRepository
.fetchFriends()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = HomeUiState.Loading,
initialValue = emptyList(),
)

Choose a reason for hiding this comment

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

high

friendRepository.fetchFriends()에서 발생하는 예외를 처리하지 않고 stateIn으로 StateFlow를 만들고 있습니다. 만약 네트워크 요청이 실패하여 예외가 발생하면, 이 Flow를 구독하는 UI 레이어에서 앱이 비정상 종료될 수 있습니다. catch 연산자를 사용하여 예외를 처리하고, 사용자에게 오류 상태를 알리거나 안정적인 상태(예: 빈 목록)를 반환하도록 처리해야 합니다.

Suggested change
val friendsFlow: StateFlow<List<Friend>> =
friendRepository
.fetchFriends()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = HomeUiState.Loading,
initialValue = emptyList(),
)
val friendsFlow: StateFlow<List<Friend>> =
friendRepository
.fetchFriends()
.catch { exception ->
// TODO: 에러 로깅 및 사용자에게 보여줄 에러 상태 처리
emit(emptyList()) // 임시로 빈 리스트를 반환하여 크래시 방지
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList(),
)

implementation(libs.kotlin.serialization.json)
}

fun getProperty(propertyKey: String): String = gradleLocalProperties(rootDir, providers).getProperty(propertyKey)

Choose a reason for hiding this comment

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

medium

com.android.build.gradle.internal 패키지에 속한 API는 Gradle의 내부 구현에 해당하므로 직접 사용하는 것은 위험합니다. Gradle 버전이 업데이트될 때 예고 없이 변경되거나 제거될 수 있어, 향후 빌드 실패의 원인이 될 수 있습니다. java.util.Properties를 사용하여 local.properties 파일을 직접 읽는 것이 더 안정적이고 권장되는 방법입니다.

fun getProperty(propertyKey: String): String {
    val properties = java.util.Properties()
    val localPropertiesFile = rootProject.file("local.properties")
    if (localPropertiesFile.exists()) {
        localPropertiesFile.inputStream().use { input ->
            properties.load(input)
        }
    }
    return properties.getProperty(propertyKey) ?: error("'$propertyKey' not found in local.properties")
}

Comment on lines +6 to +8
interface FriendService {
@GET("/friend/list")
suspend fun fetchFriends(): List<FriendEntity>

Choose a reason for hiding this comment

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

medium

엔드포인트 경로 /friend/list가 문자열로 하드코딩되어 있습니다. 이렇게 '매직 스트링'을 사용하면 오타가 발생하기 쉽고, 나중에 경로를 변경할 때 여러 곳을 수정해야 하는 번거로움이 있습니다. companion object 내에 상수로 정의하여 재사용성과 유지보수성을 높이는 것이 좋습니다.

Suggested change
interface FriendService {
@GET("/friend/list")
suspend fun fetchFriends(): List<FriendEntity>
interface FriendService {
@GET(FRIEND_LIST)
suspend fun fetchFriends(): List<FriendEntity>
companion object {
private const val FRIEND_LIST = "/friend/list"
}
}

@rhkrwngud445 rhkrwngud445 merged commit c342013 into dev Aug 20, 2025
1 check passed
@rhkrwngud445 rhkrwngud445 deleted the feat/#13-retrofit_base_api branch August 20, 2025 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Retrofit Base&Api

2 participants