Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1efe18e
feature/#7: okhttp, retrofit 세팅
kangyein9892 May 8, 2025
22f1246
feature/#7: baseReponseDto 설정
kangyein9892 May 8, 2025
acbaefb
feature/#7: signUp response,request dto 및 entity 추가
kangyein9892 May 8, 2025
864129f
feature/#7: AuthService 및 모듈 구현
kangyein9892 May 8, 2025
2196e39
feature/#7: AuthDataSoruce 구현
kangyein9892 May 8, 2025
0d5c56f
feature/#7: AuthRepository 구현 및 모듈 구현
kangyein9892 May 8, 2025
1d9ec2e
feature/#7: 닉네임 화면 구현 및 회원가입 api 연결
kangyein9892 May 8, 2025
e8e0731
feature/#7: signIn response, request dto 및 entity 추가
kangyein9892 May 8, 2025
7accf99
feature/#7: authService에 signIn 함수 추가
kangyein9892 May 8, 2025
399cc15
feature/#7: authDataSource에 signIn 추가
kangyein9892 May 8, 2025
d3a5c1f
feature/#7: authRepository에 signIn 추가
kangyein9892 May 8, 2025
0f0427e
refactor/#7: userLocalDataSource로 수정
kangyein9892 May 8, 2025
97f36de
feature/#7: signIn api 뷰에 연결
kangyein9892 May 8, 2025
eb7fd68
delete/#7: home에서 안쓰는 userRepository 관련 코드 삭제
kangyein9892 May 8, 2025
4c6280a
chore/#7: userId Long 타입으로 수정
kangyein9892 May 9, 2025
296b952
feature/#7: AuthInterceptor okhttp intercept 헤더 추가
kangyein9892 May 9, 2025
7daaeb6
chore/#7: jsonToErrorMessage 패키지 위치 변경
kangyein9892 May 9, 2025
36ad7f3
feature/#7: mynickname response dto, entity 추가
kangyein9892 May 9, 2025
076171e
chore/#7: authService에 헤더 필요 없으므로 @Tag 추가
kangyein9892 May 9, 2025
2d8039c
feature/#7: userService 구현
kangyein9892 May 9, 2025
ae63048
feature/#7: userRemoteDataSource 구현
kangyein9892 May 9, 2025
3fbe91e
feature/#7: UserRepository 구현
kangyein9892 May 9, 2025
35cd3ab
feature/#7: my 화면에 닉네임 반영
kangyein9892 May 9, 2025
b0d38e3
chore/#7: delay 시간 추가
kangyein9892 May 9, 2025
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
5 changes: 4 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:name=".AtSoptApplication"
android:allowBackup="true"
Expand All @@ -12,8 +14,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ATSOPTANDROID"
android:usesCleartextTraffic="true"
tools:targetApi="31">

</application>

</manifest>
</manifest>
17 changes: 17 additions & 0 deletions data/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import java.util.Properties

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
Expand All @@ -7,6 +9,10 @@ plugins {
alias(libs.plugins.dagger.hilt)
}

val properties = Properties().apply {
load(project.rootProject.file("local.properties").inputStream())
}

android {
namespace = "org.sopt.at.data"
compileSdk = libs.versions.compileSdk.get().toInt()
Expand All @@ -16,6 +22,7 @@ android {

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
buildConfigField("String", "BASE_URL", properties["base.url"].toString())
}

buildTypes {
Expand All @@ -34,6 +41,9 @@ android {
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
buildConfig = true
}
}

dependencies {
Expand Down Expand Up @@ -62,6 +72,13 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)

implementation(platform(libs.okhttp.bom))
implementation(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit)
implementation(libs.retrofit.kotlin.serialization.converter)
implementation(libs.kotlinx.serialization.json)

implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
Expand Down
14 changes: 14 additions & 0 deletions data/src/main/java/org/sopt/at/data/datasource/AuthDataSource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.at.data.datasource

import org.sopt.at.data.dto.request.SignInRequestDto
import org.sopt.at.data.dto.request.SignUpRequestDto
import org.sopt.at.data.service.AuthService
import javax.inject.Inject

internal class AuthDataSource @Inject constructor(
private val authService: AuthService
) {
suspend fun signUp(signUpRequest: SignUpRequestDto) = authService.signUp(signUpRequest)

suspend fun signIn(signInRequest: SignInRequestDto) = authService.signIn(signInRequest)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.SharedPreferences
import javax.inject.Inject
import androidx.core.content.edit

internal class UserDataSource @Inject constructor(
internal class UserLocalDataSource @Inject constructor(
private val userSharedPreference: SharedPreferences
) {
var id: String
Expand All @@ -15,15 +15,22 @@ internal class UserDataSource @Inject constructor(
get() = userSharedPreference.getString(PASSWORD, "").toString()
set(value) = userSharedPreference.edit { putString(PASSWORD, value) }

var userId: Long
get() = userSharedPreference.getLong(USER_ID, -1)
set(value) = userSharedPreference.edit { putLong("userId", value) }


fun clearUserPreference() {
id = DEFAULT_STRING
password = DEFAULT_STRING
userId = -1
userSharedPreference.edit { clear() }
}

companion object {
private const val ID = "id"
private const val PASSWORD = "password"
private const val USER_ID = "userId"
private const val DEFAULT_STRING = ""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.sopt.at.data.datasource

import org.sopt.at.data.service.UserService
import javax.inject.Inject

internal class UserRemoteDataSource @Inject constructor(
private val userService: UserService
) {
suspend fun getMyNickName() = userService.getMyNickName()
}
66 changes: 66 additions & 0 deletions data/src/main/java/org/sopt/at/data/di/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.sopt.at.data.di

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.sopt.at.data.BuildConfig
import org.sopt.at.data.datasource.UserLocalDataSource
import org.sopt.at.data.service.AuthInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal object NetworkModule {

@Provides
@Singleton
fun providesLoggingInterceptor() = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}

}

@Provides
@Singleton
fun providesOkHttpClient(
loggingInterceptor: HttpLoggingInterceptor,
authInterceptor: AuthInterceptor
): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.addInterceptor(authInterceptor)
.addInterceptor(loggingInterceptor)
.build()

@Provides
@Singleton
fun provideAuthInterceptor(userLocalDataSource: UserLocalDataSource): AuthInterceptor =
AuthInterceptor(userLocalDataSource)

@Provides
@Singleton
fun providesConverterFactory(): Converter.Factory = Json.asConverterFactory("application/json".toMediaType())

@Provides
@Singleton
fun providesRetrofit(
client: OkHttpClient,
converterFactory: Converter.Factory
): Retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(converterFactory)
.client(client)
.build()
}
5 changes: 5 additions & 0 deletions data/src/main/java/org/sopt/at/data/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.at.data.repository.AuthRepositoryImpl
import org.sopt.at.data.repository.UserRepositoryImpl
import org.sopt.at.repository.AuthRepository
import org.sopt.at.repository.UserRepository

@Module
@InstallIn(SingletonComponent::class)
internal interface RepositoryModule {
@Binds
fun bindUserRepository(userRepositoryImpl: UserRepositoryImpl): UserRepository

@Binds
fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository
}
24 changes: 24 additions & 0 deletions data/src/main/java/org/sopt/at/data/di/ServiceModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.sopt.at.data.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.at.data.service.AuthService
import org.sopt.at.data.service.UserService
import retrofit2.Retrofit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal object ServiceModule {
@Provides
@Singleton
fun provideAuthService(retrofit: Retrofit): AuthService =
retrofit.create(AuthService::class.java)

@Provides
@Singleton
fun provideUserService(retrofit: Retrofit): UserService =
retrofit.create(UserService::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sopt.at.data.dto.request

import kotlinx.serialization.Serializable
import org.sopt.at.entity.SignInEntity

@Serializable
internal data class SignInRequestDto(
val loginId: String,
val password: String
)

internal fun SignInEntity.toDto() = SignInRequestDto(
loginId = id,
password = password
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.at.data.dto.request

import kotlinx.serialization.Serializable
import org.sopt.at.entity.SignUpEntity

@Serializable
internal data class SignUpRequestDto(
val loginId: String,
val nickname: String,
val password: String
)

internal fun SignUpEntity.toDto() = SignUpRequestDto(
loginId = loginId,
nickname = nickname,
password = password
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.at.data.dto.response

import kotlinx.serialization.Serializable

@Serializable
data class BaseResponseDto<T>(
val success: Boolean,
val code: String,
val message: String,
val data: T?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.at.data.dto.response

import kotlinx.serialization.Serializable
import org.sopt.at.entity.MyNicknameEntity

@Serializable
data class MyNicknameResponseDto(
val nickname: String
) {
fun toEntity() = MyNicknameEntity(
nickname = nickname
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.at.data.dto.response

import kotlinx.serialization.Serializable
import org.sopt.at.entity.SignInUserEntity

@Serializable
data class SignInResponseDto(
val userId: Long
) {
fun toEntity() = SignInUserEntity(
userId = userId
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sopt.at.data.dto.response

import kotlinx.serialization.Serializable
import org.sopt.at.entity.SignUpUserEntity

@Serializable
data class SignUpResponseDto(
val userId: Long,
val nickname: String
) {
fun toEntity() = SignUpUserEntity(
userId = userId,
nickname = nickname
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.sopt.at.data.repository

import org.sopt.at.data.datasource.AuthDataSource
import org.sopt.at.data.dto.request.toDto
import org.sopt.at.data.service.jsonToErrorMessage
import org.sopt.at.entity.ApiException
import org.sopt.at.entity.SignInEntity
import org.sopt.at.entity.SignInUserEntity
import org.sopt.at.entity.SignUpEntity
import org.sopt.at.entity.SignUpUserEntity
import org.sopt.at.repository.AuthRepository
import retrofit2.HttpException
import javax.inject.Inject

internal class AuthRepositoryImpl @Inject constructor(
private val authDataSource: AuthDataSource
) : AuthRepository {

override suspend fun signUp(signUpData: SignUpEntity): Result<SignUpUserEntity> =
runCatching {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Try catch 문으로 사용했었는데 runCatching으로 체이닝이 깔끔하게 이뤄지네요 하나 배워갑니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

저도 현재 화면에서 try-catch 방식으로 예외 처리를 하고 있는데요, 예인님처럼 runCatching을 쓰면 비동기 체이닝이 더 깔끔해 보이네용 .ᐟ
혹시 팀에서 선호하는 방식이 따로 있나요, 아니면 상황에 따라 자유롭게 쓰면 될까요?

val response = authDataSource.signUp(signUpData.toDto())

if (!response.success || response.data == null) {
throw ApiException(response.message)
}

response.data.toEntity()
}.recoverCatching { throwable ->
when (throwable) {
is HttpException -> {
val errorBodyStr = throwable.response()?.errorBody()?.string()

val message = jsonToErrorMessage(errorBodyStr)
?: "Unknown error"

throw ApiException(message)
}

else -> throw throwable
}
}

override suspend fun signIn(signInEntity: SignInEntity): Result<SignInUserEntity?> =
runCatching {
val response = authDataSource.signIn(signInEntity.toDto())

if (!response.success || response.data == null) {
throw ApiException(response.message)
}

response.data.toEntity()
}.recoverCatching { throwable ->
when (throwable) {
is HttpException -> {
val errorBodyStr = throwable.response()?.errorBody()?.string()

val message = jsonToErrorMessage(errorBodyStr)
?: "Unknown error"

throw ApiException(message)
}

else -> throw throwable
}
}
}
Loading