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/week6 essential #11

Open
wants to merge 16 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
21 changes: 16 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-parcelize'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.20'
}

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
android {
namespace 'org.sopt.dosopttemplate'
compileSdk 33
Expand All @@ -14,12 +16,14 @@ android {
targetSdk 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "REQRES_BASE_URL", properties["reqres.base.url"]
buildConfigField "String", "AUTH_BASE_URL", properties["base.url"]
}
buildFeatures {
viewBinding true
dataBinding true
buildConfig true
}
buildTypes {
release {
Expand All @@ -34,19 +38,26 @@ android {
kotlinOptions {
jvmTarget = '11'
}

}

dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
implementation "androidx.fragment:fragment-ktx:1.6.1"
implementation "androidx.fragment:fragment-ktx:1.6.2"
implementation "androidx.activity:activity-ktx:1.6.1"
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.databinding:databinding-runtime:7.1.2'
implementation 'androidx.databinding:databinding-runtime:8.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1'
implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0'
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0"))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
}
25 changes: 15 additions & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-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:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -11,26 +11,31 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DoSoptTemplate"
tools:targetApi="31">
tools:targetApi="31"
android:usesCleartextTraffic="true">
<activity
android:name=".presentation.login.LoginActivity"
android:exported="false">
android:exported="true"
android:windowSoftInputMode="adjustResize">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".presentation.signup.SingUpActivity"
android:exported="false"></activity>
android:exported="false"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".presentation.home.HomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android:exported="false">
</activity>
<activity
android:name=".presentation.editMyPage.EditMyPageActivity"
android:exported="false">
android:exported="false"
android:windowSoftInputMode="adjustResize">
</activity>
</application>

Expand Down
49 changes: 49 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.sopt.dosopttemplate.api

import android.util.Log
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.sopt.dosopttemplate.BuildConfig
import retrofit2.Retrofit

object ApiFactory {
lateinit var url: String
private fun getLogOkHttpClient(): Interceptor {
val loggingInterceptor = HttpLoggingInterceptor { message ->
Log.d("Retrofit2", "CONNECTION INFO -> $message")
}
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return loggingInterceptor
}

val okHttpClient = OkHttpClient.Builder()
.addInterceptor(getLogOkHttpClient())
.build()

val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(url)
.client(okHttpClient)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
}

inline fun <reified T> create(url: String): T {
this.url = url
return retrofit.create<T>(T::class.java)
}
}

object ServicePool {
private const val BASE_URL = BuildConfig.AUTH_BASE_URL
private const val REQRES_BASE_URL = BuildConfig.REQRES_BASE_URL

val authService = ApiFactory.create<AuthService>(BASE_URL)
val followerService = ApiFactory.create<FollowerService>(REQRES_BASE_URL)
}


20 changes: 20 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.dosopttemplate.api

import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST

interface AuthService {
@POST("api/v1/members/sign-in")
suspend fun login(
@Body request: RequestLoginDto,
): Response<ResponseLoginDto>

@POST("api/v1/members")
suspend fun signUp(
@Body request: RequestSignUpDto,
): Response<Unit>
}


89 changes: 89 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.sopt.dosopttemplate.api// λ³΄μ‹œλ©΄ μ•Œκ² μ§€λ§Œ LiveDataλŠ” lifecycle λΌμ΄λΈŒλŸ¬λ¦¬μ— λ“€μ–΄μžˆμŠ΅λ‹ˆλ‹€!

Choose a reason for hiding this comment

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

주석 μ²˜λ¦¬κ°€ 잘λͺ» 된 것 κ°™μŠ΅λ‹ˆλ‹€..?

// μ„œλΉ„μŠ€ 객체λ₯Ό λ”°λ‘œ λ§Œλ“€μ§€ μ•Šκ³  λ°”λ‘œ λΆˆλŸ¬μ™”μŠ΅λ‹ˆλ‹€.
// μ–Έμ œλ‚˜ κ·Έλ ‡λ“― Callκ³Ό Callback은 Retrofit2의 것을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. okhttp μ•„λ‹˜ 주의!
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.sopt.dosopttemplate.api.ServicePool.authService
import org.sopt.dosopttemplate.util.UtilClass.isIdConditionSatisfied
import org.sopt.dosopttemplate.util.UtilClass.isPasswordConditionSatisfied

class AuthViewModel : ViewModel() {
// MutableLiveDataλ₯Ό μ‚¬μš©ν•˜μ—¬ login result 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

private val _loginResult: MutableLiveData<ResponseLoginDto> = MutableLiveData()
val loginResult: MutableLiveData<ResponseLoginDto> get() = _loginResult

private val _loginSuccess: MutableLiveData<Boolean> = MutableLiveData()
val loginSuccess: MutableLiveData<Boolean> get() = _loginSuccess

private val _signUpResult: MutableLiveData<Unit> = MutableLiveData()
val signUpResult: MutableLiveData<Unit> get() = _signUpResult

private val _signUpSuccess: MutableLiveData<Boolean> = MutableLiveData()
val signUpSuccess: MutableLiveData<Boolean> get() = _signUpSuccess

val isLoginButtonClicked: MutableLiveData<Boolean> = MutableLiveData(false)
val idConditionSatisfied: MutableLiveData<Boolean> = MutableLiveData(false)
val passwordConditionSatisfied: MutableLiveData<Boolean> = MutableLiveData(false)
val loginConditionSatisfied: MutableLiveData<Boolean> = MutableLiveData(idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false)
Comment on lines +28 to +31
Copy link

Choose a reason for hiding this comment

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

μš”λ ‡κ²Œ λΌμ΄λΈŒλ°μ΄ν„° μ‹œμž‘ν•˜λŠ” κ±°κ΅°μš”!!

Comment on lines +28 to +31
Copy link

Choose a reason for hiding this comment

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

μš”λ‘œμ§λ“€ 데이터 바인딩을 ν™œμš©ν•˜μ‹ λ‹€λ©΄ xml둜 μ΄μ „ν•˜λŠ” 방법도 μžˆλŠ”λ° λͺ¨λ₯΄μ…¨λ‹€λ©΄ ν•œλ²ˆ μ‚¬μš©ν•΄λ³΄λŠ” 방법도 μ’‹μšΈκ±° κ°™μŠ΅λ‹ˆλ‹€.


val _id: MutableLiveData<String> = MutableLiveData()
val id: String get() = _id.value ?: ""
val _password: MutableLiveData<String> = MutableLiveData()
val password: String get() = _password.value ?: ""
val _nickName: MutableLiveData<String> = MutableLiveData()
val nickName: String get() = _nickName.value ?: ""
Comment on lines +33 to +38

Choose a reason for hiding this comment

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

λ°˜λ³΅λ˜λŠ” μž‘μ—…μ„ ν•œ λ²ˆμ— μ²˜λ¦¬ν•  μˆ˜λŠ” μ—†μ„κΉŒμš”? ν•œ 번 κ³ λ―Όν•΄λ³΄μ„Έμš”!


fun login() {
viewModelScope.launch {
kotlin.runCatching {
authService.login(RequestLoginDto(id, password))
}.onSuccess {
if (it.isSuccessful) {
loginResult.value = it.body()
loginSuccess.value = true
} else {
loginSuccess.value = false
}
}.onFailure {
// μ—λŸ¬ 처리
}
}
}

fun signUp() {
viewModelScope.launch {
kotlin.runCatching {
authService.signUp(RequestSignUpDto(id, nickName, password))
}.onSuccess {
if (it.isSuccessful) {
signUpResult.value = it.body()
signUpSuccess.value = true
} else {
signUpSuccess.value = false
}
}.onFailure {
// μ—λŸ¬ 처리

Choose a reason for hiding this comment

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

둜그 좜λ ₯도 ν•˜λ‚˜μ˜ λ°©λ²•μž…λ‹ˆλ‹€!

}
}
}
Comment on lines +58 to +72
Copy link
Member

Choose a reason for hiding this comment

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

runCatching으둜 μ„œλ²„ 응닡 ν•Έλ“€λ§ν•˜μ‹€κ±°λΌλ©΄ λ°˜ν™˜κ°’μ΄ Response ꡬ쑰가 μ•„λ‹Œ DTO ꡬ쑰둜 λ³€κ²½ν•΄μ•Όν•  것 κ°™μŠ΅λ‹ˆλ‹€~!!



fun onLoginButtonClick() {
isLoginButtonClicked.value = true
}

fun onIDTextChanged() {
idConditionSatisfied.value = isIdConditionSatisfied(id)
loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false
}

fun onPasswordTextChanged() {
passwordConditionSatisfied.value = isPasswordConditionSatisfied(password)
loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false
Log.v("logincondition", loginConditionSatisfied.value.toString())
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/FollowerService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.sopt.dosopttemplate.api

import org.sopt.dosopttemplate.BuildConfig
import retrofit2.Call
import retrofit2.http.GET

private const val REQURES_BASE_URL = BuildConfig.REQRES_BASE_URL
Copy link

Choose a reason for hiding this comment

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

μš”λΆ€λΆ„ λ ˆνŠΈλ‘œν•μœΌλ‘œ 이동해도 쒋을덧


interface FollowerService {
@GET(REQURES_BASE_URL)
Copy link
Member

Choose a reason for hiding this comment

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

μš” 뢀뢄에 AuthService와 λ‹€λ₯΄κ²Œ BASE_URL을 넣은 μ΄μœ κ°€ λ­”κ°€μš”? BASE_URL은 이 FollowerServiceλ₯Ό μ‚¬μš©ν•˜λŠ” ServicePoolμ—μ„œ λ„£μ–΄μ£ΌλŠ” κ²ƒμœΌλ‘œ λ³΄μ΄λŠ”λ°!

fun getFollowerList(): Call<ResponseGetFollwerDto>
}
12 changes: 12 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/RequestLoginDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.sopt.dosopttemplate.api

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RequestLoginDto(
@SerialName("username")
val username: String,
@SerialName("password")
val password: String,
)
14 changes: 14 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/RequestSignUpDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.dosopttemplate.api

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RequestSignUpDto(
@SerialName("username")
val username: String,
@SerialName("nickname")
val nickname: String,
@SerialName("password")
val password: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.sopt.dosopttemplate.api

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseGetFollwerDto(
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
data class ResponseGetFollwerDto(
@Serializable
data class FollowerResponseDto(
@SerialName("page")
val page: Int,
@SerialName("per_page")
val perPage: Int,
@SerialName("total")
val total: Int,
@SerialName("total_pages")
val totalPages: Int,
@SerialName("data")
val data: List<User>,
@SerialName("support")
val support: Support,
) {
@Serializable
data class User(
@SerialName("id")
val id: Int,
@SerialName("email")
val email: String,
@SerialName("first_name")
val firstName: String,
@SerialName("last_name")
val lastName: String,
@SerialName("avatar")
val avatar: String
)
@Serializable
data class Support(
@SerialName("url")
val url: String,
@SerialName("text")
val text: String
)
}

μ €λŠ” μ΄λŸ°μ‹μœΌλ‘œ data class 계측을 μ£Όμ–΄μ„œ μ’…μ†λ˜λ„λ‘ ν‘œν˜„ν•΄λ‘μ—ˆμ–΄μš”! λ¬Όλ‘  이것은 μ·¨ν–₯μ°¨μ΄μž…λ‹ˆλ‹€ ~~~

@SerialName("page")
val page: Int,
@SerialName("per_page")
val perPage: Int,
@SerialName("total")
val total: Int,
@SerialName("total_pages")
val totalPages: Int,
@SerialName("data")
val data: List<ResponseData>,
@SerialName("support")
val support: ResponseSupport
)

@Serializable
data class ResponseSupport(
@SerialName("url")
val url: String,
@SerialName("text")
val text: String
)

@Serializable
data class ResponseData(
@SerialName("id")
val id: Int,
@SerialName("email")
val email: String,
@SerialName("first_name")
val firstName: String,
@SerialName("last_name")
val lastName: String,
@SerialName("avatar")
val avatar: String,
)
Copy link

Choose a reason for hiding this comment

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

μ΄λ ‡κ²Œ ν•œ λ²ˆμ— μ“°λ‹ˆκΉŒ 더 κΉ”λ”ν•œ 것 κ°™μ•„μš”!

14 changes: 14 additions & 0 deletions app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.dosopttemplate.api

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseLoginDto(
@SerialName("id")
val id: Int,
@SerialName("username")
val username: String,
@SerialName("nickname")
val nickname: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.dosopttemplate.api

sealed class SignupResponse {
data class ResponseSignUpDto(
val location: String
)
}
Loading