Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 6주차 과제 #10

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
7 changes: 5 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.dagger.hilt)
id("org.jetbrains.kotlin.kapt")

Choose a reason for hiding this comment

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

ksp 써보쉴?
kapt와 ksp의 차이점, ksp의 장점은 아실테니 한번 해보시면 좋을거같아요~

Choose a reason for hiding this comment

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

나도 kapt썻다가 ksp썻다가...ksp로 나중에 오류 잡아서 고쳐보자!

}

val properties = Properties().apply {
load(project.rootProject.file("local.properties").inputStream())
}
Expand Down Expand Up @@ -55,7 +56,6 @@ android {


dependencies {

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand Down Expand Up @@ -90,4 +90,7 @@ dependencies {
implementation(libs.converter.gson)
implementation(libs.coil.compose)

// Hilt
implementation(libs.bundles.hilt)
kapt(libs.hilt.compiler)
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".WavveApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/org/sopt/and/WavveApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.and

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class WavveApp : Application()

Choose a reason for hiding this comment

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

Hilt를 쓰니까 @HiltAndroidApp 어노테이션이 없으면 빌드 에러가 나더라구요...? 힐트를 써줄때면 이렇게 어플리케이션을 늘 하나 따로 만들어서 androidmanifest.xml 파일에도 등록해줘야 하는 건가요?! 뭔가 프로젝트 안에 동떨어진 WaveApp.kt 파일이 하나 생기는 게 조금 이질적으로 느껴져서 여쭤봅니다 ㅜㅜ

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.sopt.and.data.datalocal.datasource

interface UserInfoLocalDataSource {
interface UserLocalDataSource {
var accessToken: String
var nickname: String
fun clear()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package org.sopt.and.data.datalocal.datasourceimpl


Comment on lines +2 to +3
Copy link
Contributor

Choose a reason for hiding this comment

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

2줄 ~

import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import org.sopt.and.data.datalocal.datasource.UserInfoLocalDataSource
import dagger.hilt.android.qualifiers.ApplicationContext
import org.sopt.and.data.datalocal.datasource.UserLocalDataSource
import javax.inject.Inject

class UserInfoLocalDataSourceImpl(context: Context) : UserInfoLocalDataSource {
class UserLocalDataSourceImpl @Inject constructor(

Choose a reason for hiding this comment

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

LocalDataSource에 대해서 찾아봤는데 실제로 개발할 때 사용하기 엄청 유용할 것 같더라고요..! 길지 않은 시간 동안 무겁지 않은 정보를 저장하고 싶을 때 유용하게 쓸 것 같아요!!

@ApplicationContext context: Context
) : UserLocalDataSource {

private val sharedPreferences: SharedPreferences =
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.and.data.dataremote.datasource

import org.sopt.and.data.dataremote.model.request.RequestLoginDto
import org.sopt.and.data.dataremote.model.request.RequestSignUpDto
import org.sopt.and.data.dataremote.model.response.ResponseLoginDto
import org.sopt.and.data.dataremote.model.response.ResponseMyHobbyDto
import org.sopt.and.data.dataremote.model.response.ResponseSignUpDto
import retrofit2.Response


Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

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

요기두

interface UserRemoteDataSource {
suspend fun postSignup(requestSignUpDto: RequestSignUpDto): Response<ResponseSignUpDto>

suspend fun postLogin(requestLoginDto: RequestLoginDto): Response<ResponseLoginDto>

suspend fun getUserHobby(token: String): Response<ResponseMyHobbyDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.sopt.and.data.dataremote.datasourceimpl

import org.sopt.and.data.dataremote.datasource.UserRemoteDataSource
import org.sopt.and.data.dataremote.model.request.RequestLoginDto
import org.sopt.and.data.dataremote.model.request.RequestSignUpDto
import org.sopt.and.data.dataremote.model.response.ResponseLoginDto
import org.sopt.and.data.dataremote.model.response.ResponseMyHobbyDto
import org.sopt.and.data.dataremote.model.response.ResponseSignUpDto
import org.sopt.and.data.dataremote.service.UserService
import retrofit2.Response
import javax.inject.Inject

class UserRemoteDataSourceImpl @Inject constructor(
private val service: UserService
) : UserRemoteDataSource {
override suspend fun postSignup(requestSignUpDto: RequestSignUpDto): Response<ResponseSignUpDto> =
service.postSignup(requestSignUpDto)

override suspend fun postLogin(requestLoginDto: RequestLoginDto): Response<ResponseLoginDto> =
service.postLogin(requestLoginDto)

override suspend fun getUserHobby(token: String): Response<ResponseMyHobbyDto> =
service.getUserHobby(token)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.data.model.request
package org.sopt.and.data.dataremote.model.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.data.model.request
package org.sopt.and.data.dataremote.model.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.data.model.response
package org.sopt.and.data.dataremote.model.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.data.model.response
package org.sopt.and.data.dataremote.model.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.data.model.response
package org.sopt.and.data.dataremote.model.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.sopt.and.data.dataremote.service

import org.sopt.and.data.dataremote.model.request.RequestLoginDto
import org.sopt.and.data.dataremote.model.request.RequestSignUpDto
import org.sopt.and.data.dataremote.model.response.ResponseLoginDto
import org.sopt.and.data.dataremote.model.response.ResponseMyHobbyDto
import org.sopt.and.data.dataremote.model.response.ResponseSignUpDto
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST


interface UserService {
@POST("/user")
suspend fun postSignup(@Body requestSignUpDto: RequestSignUpDto): Response<ResponseSignUpDto>

@POST("/login")
suspend fun postLogin(@Body requestLoginDto: RequestLoginDto): Response<ResponseLoginDto>

@GET("/user/my-hobby")
suspend fun getUserHobby(@Header("token") token: String): Response<ResponseMyHobbyDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.sopt.and.data.repositoryimpl

import jakarta.inject.Inject
import org.sopt.and.data.datalocal.datasource.UserLocalDataSource
import org.sopt.and.data.dataremote.datasource.UserRemoteDataSource
import org.sopt.and.data.dataremote.model.request.RequestLoginDto
import org.sopt.and.data.dataremote.model.request.RequestSignUpDto
import org.sopt.and.data.dataremote.model.response.ResponseLoginDto
import org.sopt.and.data.dataremote.model.response.ResponseMyHobbyDto
import org.sopt.and.data.dataremote.model.response.ResponseSignUpDto
import org.sopt.and.domain.repository.UserRepository
import retrofit2.Response

class UserRepositoryImpl @Inject constructor(
private val userRemoteDataSource: UserRemoteDataSource,
private val userLocalDataSource: UserLocalDataSource
) : UserRepository {
override suspend fun postSignup(requestSignUpDto: RequestSignUpDto): Response<ResponseSignUpDto> =
userRemoteDataSource.postSignup(requestSignUpDto)

override suspend fun postLogin(requestLoginDto: RequestLoginDto): Response<ResponseLoginDto> =
userRemoteDataSource.postLogin(requestLoginDto)

Choose a reason for hiding this comment

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

Response 타입을 그대로 반환하는데, 도메인 레이어에서 더 사용하기 좋은 형태(Result나 sealed class)로 매핑하는게 더 좋다고 들었는데 어떤가요?


override suspend fun getUserHobby(token: String): Response<ResponseMyHobbyDto> =
userRemoteDataSource.getUserHobby(token)

override fun saveAccessToken(token: String) {
userLocalDataSource.accessToken = token
}

override fun getAccessToken(): String {
return userLocalDataSource.accessToken
}

override fun saveNickname(nickname: String) {
userLocalDataSource.nickname = nickname
}

override fun getNickname(): String {
return userLocalDataSource.nickname
}

override fun clearUserData() {
userLocalDataSource.clear()
}
}
21 changes: 0 additions & 21 deletions app/src/main/java/org/sopt/and/data/service/RetrofitInstance.kt

This file was deleted.

28 changes: 0 additions & 28 deletions app/src/main/java/org/sopt/and/data/service/UserService.kt

This file was deleted.

23 changes: 23 additions & 0 deletions app/src/main/java/org/sopt/and/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.sopt.and.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.and.data.datalocal.datasource.UserLocalDataSource
import org.sopt.and.data.datalocal.datasourceimpl.UserLocalDataSourceImpl
import org.sopt.and.data.dataremote.datasource.UserRemoteDataSource
import org.sopt.and.data.dataremote.datasourceimpl.UserRemoteDataSourceImpl
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class DataSourceModule {
@Binds
@Singleton
abstract fun bindsUserRemoteDataSource(userRemoteDataSourceImpl: UserRemoteDataSourceImpl): UserRemoteDataSource

@Binds
@Singleton
abstract fun bindsUserLocalDataSource(userLocalDataSourceImpl: UserLocalDataSourceImpl): UserLocalDataSource
}
71 changes: 71 additions & 0 deletions app/src/main/java/org/sopt/and/di/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.sopt.and.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.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.sopt.and.BuildConfig
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@OptIn(ExperimentalSerializationApi::class)
@Provides
@Singleton
fun providesJson(): Json =
Json {
isLenient = true
prettyPrint = true
explicitNulls = false
ignoreUnknownKeys = true
}

@Provides
@Singleton
fun providesOkHttpClient(
loggingInterceptor: HttpLoggingInterceptor
): OkHttpClient =
OkHttpClient.Builder().apply {
connectTimeout(10, TimeUnit.SECONDS)

Choose a reason for hiding this comment

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

타임아웃 디테일👍 이부분도 나중에는 상수화 하는게 좋겠네요

writeTimeout(10, TimeUnit.SECONDS)
readTimeout(10, TimeUnit.SECONDS)
addInterceptor(loggingInterceptor)
addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Accept", "*/*")
.build()
chain.proceed(request)
}
Comment on lines +42 to +47
Copy link
Contributor

Choose a reason for hiding this comment

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

이 친구 꼭 필요한가요?

}.build()

@Provides
@Singleton
fun providesLoggingInterceptor(): HttpLoggingInterceptor =
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

@ExperimentalSerializationApi
@Provides
@Singleton
fun providesRetrofit(
okHttpClient: OkHttpClient,
json: Json
): Retrofit =
Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(
json.asConverterFactory(requireNotNull("application/json".toMediaTypeOrNull()))
)
.build()
}
17 changes: 17 additions & 0 deletions app/src/main/java/org/sopt/and/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.and.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.and.data.repositoryimpl.UserRepositoryImpl
import org.sopt.and.domain.repository.UserRepository
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindUserRepository(userRepositoryImpl: UserRepositoryImpl): UserRepository
}
Loading