Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
8715b4d
init: add kakao login sdk
gdaegeun539 Oct 3, 2025
a4b3b1d
feat: implement kakao auth data source
gdaegeun539 Oct 3, 2025
0af1915
chore: change gradlew permission to 755
gdaegeun539 Oct 4, 2025
9e6b7e2
chore: modify ai agent rules
gdaegeun539 Oct 4, 2025
c15a24c
feat: add token models
gdaegeun539 Oct 4, 2025
40b7f8b
feat: covert kakao oauth token to common model
gdaegeun539 Oct 4, 2025
b1c1bc2
ci: exclude TooManyFunctions rule at datasource
gdaegeun539 Oct 4, 2025
b44658b
chore: modify ai agent rules
gdaegeun539 Oct 4, 2025
14a897c
build: Add DataStore dependency for token persistence
gdaegeun539 Oct 4, 2025
d80b745
feat: Add string parsing to OAuthProvider enum
gdaegeun539 Oct 4, 2025
a47b341
feat: Add AuthLocalDataSource for token persistence
gdaegeun539 Oct 4, 2025
4d642a8
feat: Add AuthManager for centralized auth state
gdaegeun539 Oct 4, 2025
7fe87b4
feat: Add AuthInterceptor for automatic token injection
gdaegeun539 Oct 4, 2025
761a8e1
feat: Integrate AuthManager into AuthRepository
gdaegeun539 Oct 4, 2025
b274429
feat: Add Hilt dependency injection module
gdaegeun539 Oct 4, 2025
00a7dc3
feat: move profile image type to domain
gdaegeun539 Oct 5, 2025
f506344
init: add serializer, okhttp logger
gdaegeun539 Oct 5, 2025
fe80d5e
feat: add auth api service, model, server dto
gdaegeun539 Oct 5, 2025
d2bc19c
feat: implement jwt rtr authenticator logic
gdaegeun539 Oct 5, 2025
3071e8f
feat: set okhttp, retrofit, api service
gdaegeun539 Oct 5, 2025
d7957e1
ci: exclude authenticator retruncount check
gdaegeun539 Oct 5, 2025
b19923a
feat: modify reissue token api request body
gdaegeun539 Oct 5, 2025
85ec761
feat: modify signIn, signUp api service
gdaegeun539 Oct 12, 2025
70b4824
feat: add httpexception to serverdto converter
gdaegeun539 Oct 12, 2025
0c6e10c
feat: add own server exception
gdaegeun539 Oct 12, 2025
4937bde
feat: allow authmanager save own server jwt only
gdaegeun539 Oct 12, 2025
41dbaa8
feat: implement server remote source
gdaegeun539 Oct 12, 2025
a6aaeef
feat: connect remote source to auth repository
gdaegeun539 Oct 12, 2025
311d13b
refactor: remove unused annotation in auth repo
gdaegeun539 Oct 12, 2025
3283ef7
fix: fix serializer version typo
gdaegeun539 Oct 12, 2025
5e02a6d
fix: fix profiletype import path
gdaegeun539 Oct 12, 2025
ee2fc96
init: hilt plugin, application setting
gdaegeun539 Oct 13, 2025
6ad2758
fix: datasource hilt binding fix
gdaegeun539 Oct 13, 2025
cc2c9bc
feat: di loginViewModel with hilt
gdaegeun539 Oct 13, 2025
a77772c
refactor: utilize kakao token convertor
gdaegeun539 Nov 2, 2025
20b85b5
refactor: remove context from kakao auth source
gdaegeun539 Nov 2, 2025
e23c1ff
refactor: let login view integrate login sdk
gdaegeun539 Nov 2, 2025
b7a7b98
feat: allow sever connection to http
gdaegeun539 Nov 2, 2025
ad35758
feat: add app version, logging interceptor
gdaegeun539 Nov 2, 2025
2694422
docs: social login architecture helper doc add
gdaegeun539 Nov 2, 2025
4e63bfd
refactor: fix missing newline issue
gdaegeun539 Nov 2, 2025
6d3138c
Merge branch 'develop' into feature/SCRUM-243-SCRUM-244-SCRUM-245
gdaegeun539 Mar 8, 2026
723f693
fix: resolve merge error
gdaegeun539 Mar 8, 2026
3d0d3a6
fix: align login result state with navigation
gdaegeun539 Mar 15, 2026
a27b5ec
feat: show server error at login screen
gdaegeun539 Mar 15, 2026
2c3298a
build: change server url
gdaegeun539 Mar 21, 2026
0946911
ci: adjust detekt's max return count 2 to 3
gdaegeun539 Mar 23, 2026
dca7f79
refactor: Simplify authentication failure handling
gdaegeun539 Mar 23, 2026
8fe9ea0
chore: remove legacy ai agent guidelines
gdaegeun539 Mar 23, 2026
db549a1
chore: fix typo
gdaegeun539 Apr 15, 2026
68b6a9c
fix: throw build error when kakao key is missing
gdaegeun539 Apr 15, 2026
6eadc52
fix: remove datasource provider class
gdaegeun539 Apr 15, 2026
bb9a4fe
fix: Reduce unsafe network logging exposure
gdaegeun539 Apr 15, 2026
1a6797b
fix: Prevent repeated JWT refresh retries
gdaegeun539 Apr 15, 2026
c7728c6
fix: prevent key lack issue at CI
gdaegeun539 Apr 15, 2026
60c2b53
fix: Support CI fallback for Kakao app key
gdaegeun539 Apr 15, 2026
65e8ee4
fix: Handle HttpException fallback parsing safely
gdaegeun539 Apr 18, 2026
a97de6d
fix: Show Login Error dialog safely
gdaegeun539 Apr 18, 2026
0a2491c
fix: Prevent empty JWT requests at startup
gdaegeun539 Apr 18, 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
4 changes: 4 additions & 0 deletions .github/workflows/android-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
permissions:
contents: read

env:
# PR CI는 compile/build 체크만 하므로, fork PR에서는 dummy 값으로도 통과 가능하게 둔다.
KAKAO_NATIVE_APP_KEY_DEV: ${{ secrets.KAKAO_NATIVE_APP_KEY_DEV || 'dummy-ci-kakao-key' }}

Comment thread
gdaegeun539 marked this conversation as resolved.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand Down
296 changes: 296 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
# AI Agent Development Guidelines

## 📝 Git 커밋 메시지 규칙

### 핵심 원칙

좋은 Git 커밋 메시지는 코드 변경사항의 **맥락(context)**을 전달하는 가장 효과적인 방법입니다. diff는 **무엇이** 바뀌었는지 보여주지만, 커밋 메시지만이 **왜** 바뀌었는지 설명할 수 있습니다.

### ⚠️ 중요: 영문 작성 필수

**모든 커밋 메시지는 반드시 영문으로 작성해야 합니다.**

- 국제적인 협업과 코드베이스 일관성을 위해 영문 사용
- 기술 용어의 명확한 전달
- Git 히스토리의 표준화

### 팀 커밋 접두사 컨벤션

변경사항의 유형을 명확히 하기 위해 다음 접두사를 사용합니다:

- `init:` - Initial setup
- `feat:` - New feature
- `docs:` - Documentation changes
- `build:` - Build system or dependency changes
- `design:` - UI/UX design changes
- `fix:` - Bug fixes
- `chore:` - Other minor changes
- `refactor:` - Code refactoring
- `ci:` - CI configuration changes (e.g., detekt.yml, GitHub Actions)
- `test:` - Test code changes

**Format**: `prefix: message` (lowercase prefix + colon + space + message)

**Examples**:

```
feat: Add Kakao login feature
fix: Resolve token refresh error
ci: Optimize detekt rules
```
Comment thread
gdaegeun539 marked this conversation as resolved.

### 7가지 필수 규칙

#### 1. 제목과 본문을 빈 줄로 분리

```
Subject: Summary of changes

Body: Detailed explanation (if needed)
```

#### 2. 제목을 50자로 제한

- Recommended: Within 50 characters
- Hard limit: 72 characters

#### 3. 제목의 첫 글자를 대문자로 작성

```
✅ feat: Add user authentication
✅ fix: Resolve login validation bug

❌ feat: add user authentication
❌ fix: resolved login validation bug
```

#### 4. 제목 끝에 마침표 사용 금지

```
✅ fix: Login validation bug
❌ fix: Login validation bug.
```

#### 5. 제목에서 명령형 어조 사용

```
✅ feat: Add new feature
✅ fix: Fix critical bug
✅ refactor: Remove deprecated code

❌ feat: Added new feature
❌ fix: Fixed critical bug
❌ refactor: Removing deprecated code
```

**Test**: "If applied, this commit will [subject]" should read naturally

#### 6. 본문을 72자에서 줄바꿈

Git does not automatically wrap text, so manual line breaks are required

#### 7. 본문에서 '무엇'과 '왜'를 설명

- ✅ Explain why the change is needed
- ✅ Describe the problem being solved
- ✅ Note any side effects or considerations
- ❌ Don't explain how (the code shows that)

### 커밋 메시지 템플릿

```
prefix: Subject within 50 characters

Body (if needed):
- Why is this change necessary?
- What problem does it solve?
- Are there any side effects or considerations?

Issue references:
Resolves: #123
See also: #456
```

### 실제 예시

#### 좋은 커밋 메시지

```
feat: Add user session timeout feature

Implement automatic logout after 30 minutes of inactivity
to enhance security. Users receive warning 5 minutes before
timeout with option to extend session.

- Add session monitoring service
- Implement warning modal component
- Update authentication middleware

Resolves: #234
```

```
fix: Resolve token refresh infinite loop

Token refresh was triggering multiple simultaneous requests
causing race conditions. Add mutex lock to ensure only one
refresh attempt at a time.

Resolves: #456
```

```
ci: Update detekt configuration

Enable autoCorrect and add exception rules for Composable
functions to improve code quality automation and reduce
false positives.

- Set autoCorrect: true
- Add Composable complexity exception
- Add modifier unused parameter exception
```

#### 나쁜 커밋 메시지

```
fixed stuff
updated files
minor changes
bug fix
update code
WIP
```

### 원자적 커밋 (Atomic Commits)

- Include only one logical change per commit
- Each commit should build and test independently
- Separate unrelated changes into different commits

## 🚀 필수 실행 지침

### ⚠️ **코드 편집 완료 후 필수 작업**

```bash
# 모든 Kotlin 코드 편집이 완료되면 반드시 실행
./gradlew detekt
```

**중요:** `detektFormat`은 존재하지 않는 task입니다. `./gradlew detekt`를 사용하세요.

**목적:**

- 코드 분석 및 품질 검사
- 자동 formatting 적용 (autoCorrect: true 설정)
- ktlint 규칙 및 Compose 규칙 적용
- 일관된 코드 품질 유지

## 📋 프로젝트 설정 정보

### 🔧 도구 구성

- **detekt**: 1.23.7 (기본 룰셋 사용)
- **ktlint**: detekt-formatting을 통해 통합
- **Compose Rules**: 0.4.14 (Compose 전용 규칙)

### 📏 코딩 규칙

- **최대 라인 길이**: 120자
- **Composable 함수**: 대문자로 시작, modifier 매개변수 필수
- **네이밍**: Android 컨벤션 준수 (camelCase, PascalCase)
- **들여쓰기**: 4 spaces

### 🎯 주요 체크 포인트

1. **Modifier 관련**:

- 모든 Composable에 modifier 매개변수 포함
- modifier에 기본값 `= Modifier` 설정
- Root에서 modifier 사용 확인

2. **CompositionLocal 사용**:

- 허용된 CompositionLocal만 사용
- 네이밍 규칙: `Local` 접두사

3. **매개변수 순서**:
- Composable에서 modifier는 첫 번째 선택적 매개변수
- lambda는 마지막 매개변수

## 🛠️ detekt 사용법

### 사용 가능한 task들

```bash
# 기본 detekt 실행 (분석 + 자동 formatting)
./gradlew detekt

# 다른 유용한 task들
./gradlew detektMain # production 코드만 분석
./gradlew detektTest # test 코드만 분석
./gradlew detektBaseline # baseline 파일 생성
```

### 주요 이슈 유형

- **Formatting**: autoCorrect로 자동 수정됨
- **Complexity**: 수동 리팩토링 필요 (함수 분할, 매개변수 축소)
- **Naming**: 네이밍 컨벤션 수동 수정
- **Compose**: Compose 규칙 준수 확인

### 예외 처리 설정 완료

- **@Composable**: complexity 규칙에서 제외
- **@Preview**: UnusedPrivateMember에서 제외
- **modifier**: UnusedParameter에서 제외
- **fromXXX**: ReturnCount에서 제외

## 🧪 Kotlin Experimental API 사용 규칙

### ⚠️ @OptIn 및 Experimental Import 보호

**개발자가 수동으로 추가한 `@OptIn` 어노테이션 및 관련 Experimental import는 절대 삭제하지 마세요.**

#### 핵심 원칙

```kotlin
// ✅ 올바른 패턴 - Experimental import 유지
import kotlin.time.ExperimentalTime // @OptIn에서 사용 - 삭제 금지!
import kotlin.time.Instant

@OptIn(ExperimentalTime::class)
data class OAuthToken(val expiresAt: Instant? = null)
```

#### AI Agent 필수 체크사항

1. **파일 수정 전:**

- `@OptIn` 어노테이션이 있는지 확인
- `@OptIn(...)`에 전달된 클래스의 import 식별
- 해당 import를 "보호 목록"에 추가

2. **Import 정리 시:**

- `@OptIn`에 사용된 import는 **"unused"로 표시되어도 절대 삭제 금지**
- detekt가 경고를 표시해도 무시하고 유지
- 개발자가 명시적으로 제거를 요청하기 전까지 보존

3. **일반적인 Experimental API:**
```kotlin
import kotlin.time.ExperimentalTime
import kotlinx.coroutines.ExperimentalCoroutinesApi
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.material3.ExperimentalMaterial3Api
```

> **Golden Rule**: `@OptIn` 어노테이션과 함께 사용되는 Experimental import는 **코드에서 직접 참조되지 않아도 컴파일에 필수적**입니다. 절대 자동으로 삭제하지 마세요!

## 📚 참고 리소스

- [Compose Rules](https://mrmans0n.github.io/compose-rules/detekt/)
- [detekt Documentation](https://detekt.dev/)
- [Android Coding Style](https://developer.android.com/kotlin/style-guide)
- [Kotlin Opt-in Requirements](https://kotlinlang.org/docs/opt-in-requirements.html)
File renamed without changes.
89 changes: 89 additions & 0 deletions SOCIAL_LOGIN_REFACTOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 소셜 로그인 아키텍처 리팩토링: `Activity` 의존성 분리

> 이 문서는 소셜 로그인(카카오/구글) 기능 구현 시 발생하는 `Activity` 의존성 문제를 해결하고, 앱 아키텍처 및 멀티모듈 환경 대비에 적합하도록 구조를 리팩토링한 내용을 정리합니다.

## 1. 문제 상황

- 카카오, 구글 등 대부분의 소셜 로그인 SDK는 로그인 UI(웹뷰 또는 전용 `Activity`)를 실행하기 위해 `Activity` 또는 `Context`를 파라미터로 요구합니다.
- 클린 아키텍처상 `DataSource`는 `Repository`의 하위 레이어이며, Hilt를 통해 `@Singleton` 스코프로 관리되고 있었습니다.
- `@Singleton` 스코프(Application 생명주기)의 객체가 `Activity` 스코프(Activity 생명주기)의 객체를 참조하면 심각한 **메모리 누수**가 발생하며, Hilt 스코프 규칙에도 위배됩니다.

## 2. 기존 설계의 치명적인 문제점

`DataSource`가 `Activity`에 직접 의존하는 설계는 다음과 같은 심각한 문제를 야기합니다.

### A. 안드로이드 아키텍처 가이드 위반 (안티패턴)

- **관심사 분리(SoC) 위반**: `DataSource`는 데이터 통신에만 관심이 있어야 하나, UI(`Activity`)의 존재를 알게 됩니다.
- **종속성 규칙 위반**: 안드로이드 공식 아키텍처는 **상위 레이어(UI)가 하위 레이어(Data)에 의존**해야 한다고 규정합니다. 하지만 `DataSource`가 `Activity`를 참조하면 **하위 레이어가 상위 레이어에 의존**하는 '역방향 종속성'이 발생합니다.

### B. 추후 멀티모듈 빌드 실패 (순환 종속성)

- 멀티모듈 환경에서 `:data` 모듈은 `:app` 모듈을 알지 못합니다. (의존성: `:app` → `:data`)
- `DataSource`가 `:app` 모듈에 있는 `Activity`를 참조하려면, `:data` 모듈이 `:app` 모듈에 의존해야 합니다. (`:data` → `:app`)
- 이는 `:app` → `:data` → `:app`... 형태의 **순환 종속성(Circular Dependency)**을 만들며, Gradle은 이 구조를 허용하지 않아 **빌드가 실패**합니다.

## 3. 해결 원칙: SDK 책임 분리

이 문제의 근본 원인은 소셜 로그인 SDK가 **두 가지 책임**을 동시에 갖기 때문입니다.

1. **UI 책임**: 로그인 화면을 띄우고 사용자 입력을 받습니다. (`Activity` 필요)
2. **데이터 책임**: 인증 성공 시 `AuthCode`나 `AccessToken`을 반환합니다.

이 두 책임을 아키텍처 레이어에 맞게 분리합니다.

- **UI 책임 (View Layer)**: `Activity`에 접근 가능한 Composable(View)에서 SDK의 로그인 UI 실행을 전담합니다.
- **데이터 책임 (Data Layer)**: `DataSource`는 View로부터 전달받은 **결과물(토큰)**만을 사용하여 백엔드 서버와 통신합니다.

## 4. 리팩토링된 아키텍처

### A. Presentation Layer (Composable)

- `Activity`의 `Context`가 필요한 **SDK 로그인 실행**을 담당합니다. (e.g., `UserApiClient.instance.loginWithKakaoTalk(context, ...)` )
- `rememberLauncherForActivityResult` 또는 SDK가 제공하는 콜백을 통해 **SDK의 인증 결과(토큰 또는 오류)**를 수신합니다.
- 수신한 **결과(토큰)만** `ViewModel`에 전달합니다. (e.g., `viewModel.onSocialLoginSuccess(token)`)

### B. ViewModel Layer

- `Activity`에 대해 전혀 알지 못합니다.
- View로부터 `token`을 전달받아 `Repository`를 호출합니다. (e.g., `repository.signInToServer(token)`)
- `Repository`로부터 받은 **'최종 서버 인증 결과'**를 바탕으로 `UiState`를 업데이트합니다.

### C. Data Layer (Repository / DataSource)

- `Activity` 의존성이 **완전히 제거**됩니다.
- `@Singleton` 스코프를 유지하는 데 아무런 문제가 없습니다.
- 메서드 시그니처가 `login(activity)`에서 `signInToServer(token: String)`과 같이 변경됩니다.
- `ViewModel`에서 전달받은 `token`을 사용하여 **'자체체 백엔드 서버'**와 최종 인증 통신을 수행합니다.

## 5. 새로운 상호작용 (Sequence Diagram)

```mermaid
sequenceDiagram
participant C as Composable (View)
participant SDK as [Kakao/Google SDK]
participant VM as LoginViewModel
participant Repo as AuthRepository
participant DS as AuthDataSource
participant Server as [Backend Server]

Note over C: (1. 로그인 버튼 클릭)
C->>SDK: loginWithKakaoTalk(context)

Note over SDK: (SDK 로그인 UI 실행)
SDK-->>C: onLoginResult(token)

Note over C: (2. SDK 콜백 수신 - 'SDK 레벨 성공')
C->>VM: onSocialLoginSuccess(token)

Note over VM: (3. '비즈니스 레벨' 로직 시작)
VM->>Repo: signInToServer(token)
Repo->>DS: requestServerLogin(token)
DS->>Server: POST /auth/social (token)
Server-->>DS: (LoginResponse - JWT)
DS-->>Repo: (LoginResponse)
Repo-->>VM: (LoginResponse)

Note over VM: (4. '최종 로그인' 성공)
VM-->>C: (UiState = LoginSuccess)
```
Loading
Loading