diff --git a/app/build.gradle b/app/build.gradle index 91ec757..ea6fc20 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,12 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.0' } +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) + android { namespace 'com.sopt.now' compileSdk 34 @@ -15,6 +19,7 @@ android { versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + buildConfigField "String", "AUTH_BASE_URL", properties["base.url"] } buildTypes { @@ -32,9 +37,8 @@ android { } buildFeatures { viewBinding true - } - buildFeatures { dataBinding true + buildConfig true } } @@ -45,7 +49,23 @@ dependencies { implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.activity:activity:1.8.0' + // 서버통신을 위한 retrofit + retrofit에서 serialization 사용 + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0' + // json 직렬화 + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' + // OkHttp 버전 관리및 로깅 인터셉터 + implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) + implementation("com.squareup.okhttp3:okhttp") + implementation("com.squareup.okhttp3:logging-interceptor") + // navigation 구성 요소에서 fragment / ui 사용 + implementation("androidx.navigation:navigation-fragment-ktx:2.7.7") + implementation("androidx.navigation:navigation-ui-ktx:2.7.7") + // 단위 테스트 testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' + // ui 테스트 androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + // coroutine + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3f49b4d..1cd7493 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + @@ -22,10 +24,10 @@ diff --git a/app/src/main/java/com/sopt/now/LoginActivity.kt b/app/src/main/java/com/sopt/now/LoginActivity.kt deleted file mode 100644 index b9dfa25..0000000 --- a/app/src/main/java/com/sopt/now/LoginActivity.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.sopt.now - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.google.android.material.snackbar.Snackbar -import com.sopt.now.databinding.ActivityLoginBinding - -class LoginActivity : AppCompatActivity() { - private lateinit var binding: ActivityLoginBinding - - // 회원가입 시 받은 id랑 pw를 저장할 (임시) 변수 - private var registeredId: String? = null - private var registeredPassword: String? = null - private var registeredNickname: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityLoginBinding.inflate(layoutInflater) - setContentView(binding.root) - - binding.btnLogin.setOnClickListener { - // 임시 변수에 받은 id랑 pw를 저장 - val inputId = binding.txtfieldId.text.toString() - val inputPassword = binding.txtfieldPw.text.toString() - - if (inputId == registeredId && inputPassword == registeredPassword) { - val intent = Intent(this, MainActivity::class.java) - intent.putExtra("id", inputId) - intent.putExtra("password", inputPassword) - intent.putExtra("nickname", registeredNickname) - startActivity(intent) - } - else if (inputId != registeredId){ - Snackbar.make( - binding.root, - "아이디가 일치하지 않습니다", - Snackbar.LENGTH_LONG - ).show() - } - else if (inputPassword != registeredPassword){ - Snackbar.make( - binding.root, - "비밀번호가 일치하지 않습니다", - Snackbar.LENGTH_LONG - ).show() - } - } - binding.btnSignup.setOnClickListener { - val intent = Intent(this, SignupActivity::class.java) - startActivityForResult(intent, REQUEST_SIGNUP) - } - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == 123 && resultCode == Activity.RESULT_OK) { - registeredId = data?.getStringExtra("id") - registeredPassword = data?.getStringExtra("password") - registeredNickname = data?.getStringExtra("nickname") - } - } - companion object { - const val REQUEST_SIGNUP = 123 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/SignupActivity.kt b/app/src/main/java/com/sopt/now/SignupActivity.kt deleted file mode 100644 index 37fd6cb..0000000 --- a/app/src/main/java/com/sopt/now/SignupActivity.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.sopt.now - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.widget.Toast -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.google.android.material.snackbar.Snackbar -import com.sopt.now.databinding.ActivitySignupBinding - -class SignupActivity : AppCompatActivity() { - private lateinit var binding: ActivitySignupBinding - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivitySignupBinding.inflate(layoutInflater) - setContentView(binding.root) - - binding.btnSignup.setOnClickListener { - val id = binding.txtfieldId.text.toString() - val password = binding.txtfieldPw.text.toString() - val nickname = binding.txtfieldName.text.toString() - val mbti = binding.txtfieldMbti.text.toString() - - if (id.isEmpty() || password.isEmpty() || nickname.isEmpty() || mbti.isEmpty()) { - Snackbar.make( - binding.root, - "모든 정보를 입력하세요", - Snackbar.LENGTH_LONG - ).show() - } else if (binding.txtfieldId.length() < 6 || binding.txtfieldId.length() > 10) { - Snackbar.make( - binding.root, - "아이디를 6~10자로 설정해주세요", - Snackbar.LENGTH_LONG - ).show() - } else if (binding.txtfieldPw.length() < 8 || binding.txtfieldPw.length() > 12) { - Snackbar.make( - binding.root, - "비밀번호를 8~12자로 설정해주세요", - Snackbar.LENGTH_LONG - ).show() - } else if (binding.txtfieldName.length() <= 0) { - Snackbar.make( - binding.root, - "닉네임은 한 글자 이상으로 설정해주세요", - Snackbar.LENGTH_LONG - ).show() - } else { - val resultIntent = Intent() - resultIntent.putExtra("id", id) - resultIntent.putExtra("password", password) - resultIntent.putExtra("nickname", nickname) - resultIntent.putExtra("mbti", mbti) - setResult(Activity.RESULT_OK, resultIntent) - Toast.makeText(this, "회원가입 성공!", Toast.LENGTH_SHORT).show() - finish() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/auth/login/LoginActivity.kt b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginActivity.kt new file mode 100644 index 0000000..3b9d979 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginActivity.kt @@ -0,0 +1,45 @@ +package com.sopt.now.presentation.auth.login + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.sopt.now.databinding.ActivityLoginBinding +import com.sopt.now.presentation.data.model.dto.RequestLoginDto +import com.sopt.now.presentation.auth.signup.SignupActivity + +class LoginActivity : AppCompatActivity() { + private val binding by lazy { ActivityLoginBinding.inflate(layoutInflater) } + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + clickButtonListener() + } + + private fun clickButtonListener() { + // 로그인 버튼 클릭 + binding.btnLogin.setOnClickListener { + viewModel.login(getLoginRequestDto()) + } + // 회원가입 버튼 클릭 + binding.btnSignup.setOnClickListener { + Intent(this@LoginActivity, SignupActivity::class.java).let { + startActivity(it) + } + } + } + + private fun getLoginRequestDto(): RequestLoginDto { + val id = binding.edtLoginId.text.toString() + val password = binding.edtLoginPassword.text.toString() + return RequestLoginDto( + authenticationId = id, + password = password, + ) + } + companion object { + var memberId = "1" + } +} diff --git a/app/src/main/java/com/sopt/now/presentation/auth/login/LoginState.kt b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginState.kt new file mode 100644 index 0000000..9d269bc --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginState.kt @@ -0,0 +1,7 @@ +package com.sopt.now.presentation.auth.login + +data class LoginState( + val isSuccess: Boolean, + val message: String, + val memberId: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/auth/login/LoginViewModel.kt b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginViewModel.kt new file mode 100644 index 0000000..2f0027f --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/login/LoginViewModel.kt @@ -0,0 +1,27 @@ +package com.sopt.now.presentation.auth.login + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.sopt.now.presentation.data.model.dto.RequestLoginDto +import com.sopt.now.presentation.data.model.dto.ResponseUserInfoDto +import com.sopt.now.presentation.data.ServicePool +import kotlinx.coroutines.launch +import retrofit2.HttpException + +class LoginViewModel : ViewModel() { + private val authService by lazy { ServicePool.authService } + private val _loginStateLiveData = MutableLiveData() + + fun login(request: RequestLoginDto) { + viewModelScope.launch { + try { + val response = authService.login(request) + _loginStateLiveData.value = LoginState(true, "로그인 성공") + + } catch (e: HttpException) { + _loginStateLiveData.value = LoginState(false, "로그인 실패 ${e.code()}") + } + } + } +} diff --git a/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpActivity.kt b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpActivity.kt new file mode 100644 index 0000000..106d4fc --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpActivity.kt @@ -0,0 +1,53 @@ +package com.sopt.now.presentation.auth.signup + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.sopt.now.databinding.ActivitySignupBinding +import com.sopt.now.presentation.data.model.dto.RequestSignUpDto +import com.sopt.now.presentation.auth.login.LoginActivity + +class SignupActivity : AppCompatActivity() { + private val binding by lazy { ActivitySignupBinding.inflate(layoutInflater) } + private val viewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + SignupButtonClickListener() + observeSignupState() + } + + private fun SignupButtonClickListener() { + binding.btnSignup.setOnClickListener { + viewModel.signUp(getSignUpRequestDto()) + } + } + + private fun observeSignupState() { + viewModel.liveData.observe(this) { SignUpState -> + if (SignUpState.isSuccess) { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + Toast.makeText( + this@SignupActivity, + SignUpState.message, + Toast.LENGTH_SHORT + ).show() + } + } + } + private fun getSignUpRequestDto(): RequestSignUpDto { + val id = binding.edtSignupId.text.toString() + val password = binding.edtSignupPassword.text.toString() + val nickname = binding.edtSignupNickname.text.toString() + val phoneNumber = binding.edtSignupPhonenumber.text.toString() + return RequestSignUpDto( + authenticationId = id, + password = password, + nickname = nickname, + phone = phoneNumber + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpState.kt b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpState.kt new file mode 100644 index 0000000..4da9f19 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpState.kt @@ -0,0 +1,6 @@ +package com.sopt.now.presentation.auth.signup + +data class SignUpState( + val isSuccess: Boolean, + val message: String +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpViewModel.kt b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpViewModel.kt new file mode 100644 index 0000000..291f096 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignUpViewModel.kt @@ -0,0 +1,24 @@ +package com.sopt.now.presentation.auth.signup + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.sopt.now.presentation.data.model.dto.RequestSignUpDto +import com.sopt.now.presentation.data.ServicePool +import kotlinx.coroutines.launch +import retrofit2.HttpException + +class SignUpViewModel : ViewModel() { + private val authService by lazy { ServicePool.authService } + val liveData = MutableLiveData() + fun signUp(request: RequestSignUpDto) { + viewModelScope.launch { + try { + val response = authService.signUp(request) + liveData.value = SignUpState(true, "회원가입 성공") + } catch (e: HttpException) { + liveData.value = SignUpState(false, "회원가입 실패 ${e.code()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/auth/signup/SignupActivity.kt b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignupActivity.kt new file mode 100644 index 0000000..106d4fc --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/auth/signup/SignupActivity.kt @@ -0,0 +1,53 @@ +package com.sopt.now.presentation.auth.signup + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.sopt.now.databinding.ActivitySignupBinding +import com.sopt.now.presentation.data.model.dto.RequestSignUpDto +import com.sopt.now.presentation.auth.login.LoginActivity + +class SignupActivity : AppCompatActivity() { + private val binding by lazy { ActivitySignupBinding.inflate(layoutInflater) } + private val viewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + SignupButtonClickListener() + observeSignupState() + } + + private fun SignupButtonClickListener() { + binding.btnSignup.setOnClickListener { + viewModel.signUp(getSignUpRequestDto()) + } + } + + private fun observeSignupState() { + viewModel.liveData.observe(this) { SignUpState -> + if (SignUpState.isSuccess) { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + Toast.makeText( + this@SignupActivity, + SignUpState.message, + Toast.LENGTH_SHORT + ).show() + } + } + } + private fun getSignUpRequestDto(): RequestSignUpDto { + val id = binding.edtSignupId.text.toString() + val password = binding.edtSignupPassword.text.toString() + val nickname = binding.edtSignupNickname.text.toString() + val phoneNumber = binding.edtSignupPhonenumber.text.toString() + return RequestSignUpDto( + authenticationId = id, + password = password, + nickname = nickname, + phone = phoneNumber + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/ApiFactory.kt b/app/src/main/java/com/sopt/now/presentation/data/ApiFactory.kt new file mode 100644 index 0000000..c8c8b4c --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/ApiFactory.kt @@ -0,0 +1,43 @@ +package com.sopt.now.presentation.data + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import com.sopt.now.BuildConfig +import com.sopt.now.presentation.data.interceptor.AuthInterceptor +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import java.util.concurrent.TimeUnit + +object ApiFactory { + private const val BASE_URL: String = BuildConfig.AUTH_BASE_URL + + // HTTP 로깅 인터셉터 설정 + private val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + // OkHttpClient 설정 + private val okHttpClient = OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) // 로깅 인터셉터 추가 + .addInterceptor(AuthInterceptor()) + .connectTimeout(30, TimeUnit.SECONDS) // 연결 타임아웃 + .readTimeout(30, TimeUnit.SECONDS) // 읽기 타임아웃 + .writeTimeout(30, TimeUnit.SECONDS) // 쓰기 타임아웃 + .build() + + // Retrofit 인스턴스 생성 + val retrofit: Retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okHttpClient) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + + inline fun create(): T = retrofit.create(T::class.java) +} + + +object ServicePool { + val authService = ApiFactory.create() +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/AuthService.kt b/app/src/main/java/com/sopt/now/presentation/data/AuthService.kt new file mode 100644 index 0000000..74ccb42 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/AuthService.kt @@ -0,0 +1,26 @@ +package com.sopt.now.presentation.data + +import com.sopt.now.presentation.data.model.dto.RequestLoginDto +import com.sopt.now.presentation.data.model.dto.RequestSignUpDto +import com.sopt.now.presentation.data.model.dto.ResponseLoginDto +import com.sopt.now.presentation.data.model.dto.ResponseSignUpDto +import com.sopt.now.presentation.data.model.dto.ResponseUserInfoDto +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.POST + +interface AuthService { + @POST("member/join") + suspend fun signUp( + @Body request: RequestSignUpDto, + ): ResponseSignUpDto + + @POST("member/login") + suspend fun login( + @Body request: RequestLoginDto + ): ResponseLoginDto + + @GET("member/info") + suspend fun getUserInfo(): ResponseUserInfoDto +} diff --git a/app/src/main/java/com/sopt/now/presentation/data/interceptor/AuthInterceptor.kt b/app/src/main/java/com/sopt/now/presentation/data/interceptor/AuthInterceptor.kt new file mode 100644 index 0000000..23d64d3 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/interceptor/AuthInterceptor.kt @@ -0,0 +1,14 @@ +package com.sopt.now.presentation.data.interceptor + +import com.sopt.now.presentation.auth.login.LoginActivity.Companion.memberId +import okhttp3.Interceptor +import okhttp3.Response + +class AuthInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val authResponse = chain.proceed(chain.request().newBuilder().addHeader("memberId", memberId).build()) + memberId = authResponse.header("location").toString() + + return chain.proceed(chain.request().newBuilder().addHeader("memberId", memberId).build()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestLoginDto.kt b/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestLoginDto.kt new file mode 100644 index 0000000..84a3b52 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestLoginDto.kt @@ -0,0 +1,12 @@ +package com.sopt.now.presentation.data.model.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestLoginDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("password") + val password: String +) diff --git a/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestSignUpDto.kt b/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestSignUpDto.kt new file mode 100644 index 0000000..ea2abd5 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/model/dto/RequestSignUpDto.kt @@ -0,0 +1,16 @@ +package com.sopt.now.presentation.data.model.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestSignUpDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("password") + val password: String, + @SerialName("nickname") + val nickname: String, + @SerialName("phone") + val phone: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseLoginDto.kt b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseLoginDto.kt new file mode 100644 index 0000000..e9aea7c --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseLoginDto.kt @@ -0,0 +1,12 @@ +package com.sopt.now.presentation.data.model.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseLoginDto( + @SerialName("code") + val code: Int, + @SerialName("message") + val message: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseSignUpDto.kt b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseSignUpDto.kt new file mode 100644 index 0000000..0a89264 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseSignUpDto.kt @@ -0,0 +1,12 @@ +package com.sopt.now.presentation.data.model.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseSignUpDto( + @SerialName("code") + val code: Int, + @SerialName("message") + val message: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseUserInfoDto.kt b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseUserInfoDto.kt new file mode 100644 index 0000000..b090d44 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/data/model/dto/ResponseUserInfoDto.kt @@ -0,0 +1,24 @@ +package com.sopt.now.presentation.data.model.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseUserInfoDto( + @SerialName("code") + val code: Int, + @SerialName("message") + val message: String, + @SerialName("data") + val data: UserData, +) + +@Serializable +data class UserData( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("nickname") + val nickname: String, + @SerialName("phone") + val phone: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/MainActivity.kt b/app/src/main/java/com/sopt/now/presentation/main/MainActivity.kt similarity index 54% rename from app/src/main/java/com/sopt/now/MainActivity.kt rename to app/src/main/java/com/sopt/now/presentation/main/MainActivity.kt index 12b7239..bc87507 100644 --- a/app/src/main/java/com/sopt/now/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/MainActivity.kt @@ -1,12 +1,14 @@ -package com.sopt.now +package com.sopt.now.presentation.main import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.util.Log import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment +import com.sopt.now.presentation.main.mypage.MyPageFragment +import com.sopt.now.R import com.sopt.now.databinding.ActivityMainBinding +import com.sopt.now.presentation.main.home.HomeFragment +import com.sopt.now.presentation.main.search.SearchFragment class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding @@ -16,43 +18,32 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + val memberId = intent.getStringExtra("memberId") val currentFragment = supportFragmentManager.findFragmentById(binding.fcvHome.id) if (currentFragment == null) { supportFragmentManager.beginTransaction() .add(binding.fcvHome.id, HomeFragment()) .commit() } - setBottomNavigation() + setBottomNavigation(memberId) } - private fun setBottomNavigation() { - binding.bnvHome.setOnItemSelectedListener{ + + private fun setBottomNavigation(memberId: String?) { + binding.bnvHome.setOnItemSelectedListener { when (it.itemId) { - R.id.menu_home-> { + R.id.menu_home -> { replaceFragment(HomeFragment()) true } - R.id.menu_search-> { + R.id.menu_search -> { replaceFragment(SearchFragment()) true } R.id.menu_mypage -> { - val id = intent.getStringExtra("id") - val password = intent.getStringExtra("password") - val nickname = intent.getStringExtra("nickname") - - // MyPageFragment로 데이터 전달 - val bundle = Bundle().apply { - putString("id", id) - putString("password", password) - putString("nickname", nickname) - } - - val myPageFragment = MyPageFragment() - myPageFragment.arguments = bundle - - replaceFragment(myPageFragment) // 수정된 부분 + val myPageFragment = MyPageFragment.newInstance(memberId) + replaceFragment(myPageFragment) true } @@ -60,10 +51,10 @@ class MainActivity : AppCompatActivity() { } } } + private fun replaceFragment(fragment: Fragment) { supportFragmentManager.beginTransaction() - .replace(R.id.fcv_home, fragment) + .replace(binding.fcvHome.id, fragment) .commit() } } - diff --git a/app/src/main/java/com/sopt/now/Friend.kt b/app/src/main/java/com/sopt/now/presentation/main/home/Friend.kt similarity index 77% rename from app/src/main/java/com/sopt/now/Friend.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/Friend.kt index 53f871d..53d977c 100644 --- a/app/src/main/java/com/sopt/now/Friend.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/Friend.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import androidx.annotation.DrawableRes data class Friend( diff --git a/app/src/main/java/com/sopt/now/FriendListAdapter.kt b/app/src/main/java/com/sopt/now/presentation/main/home/FriendListAdapter.kt similarity index 92% rename from app/src/main/java/com/sopt/now/FriendListAdapter.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/FriendListAdapter.kt index 2a15801..9c428c5 100644 --- a/app/src/main/java/com/sopt/now/FriendListAdapter.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/FriendListAdapter.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import android.view.LayoutInflater import android.view.ViewGroup @@ -42,9 +42,7 @@ class FriendListAdapter(private val myProfile: MyProfile) : RecyclerView.Adapter } } - override fun getItemCount(): Int { - return friendList.size + 1 // 내 프로필을 포함하기 위해 +1을 함 - } + override fun getItemCount(): Int = friendList.size + 1 override fun getItemViewType(position: Int): Int { return if (position == 0) { diff --git a/app/src/main/java/com/sopt/now/FriendViewHolder.kt b/app/src/main/java/com/sopt/now/presentation/main/home/FriendViewHolder.kt similarity index 91% rename from app/src/main/java/com/sopt/now/FriendViewHolder.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/FriendViewHolder.kt index 3c5d042..bc5ccba 100644 --- a/app/src/main/java/com/sopt/now/FriendViewHolder.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/FriendViewHolder.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import androidx.recyclerview.widget.RecyclerView import com.sopt.now.databinding.ItemFriendBinding diff --git a/app/src/main/java/com/sopt/now/HomeFragment.kt b/app/src/main/java/com/sopt/now/presentation/main/home/HomeFragment.kt similarity index 93% rename from app/src/main/java/com/sopt/now/HomeFragment.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/HomeFragment.kt index 038ac63..6289c67 100644 --- a/app/src/main/java/com/sopt/now/HomeFragment.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/HomeFragment.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import android.os.Bundle import android.view.LayoutInflater @@ -6,14 +6,13 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import com.sopt.now.R import com.sopt.now.databinding.FragmentHomeBinding -import com.sopt.now.databinding.ItemFriendBinding class HomeFragment: Fragment() { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! - private lateinit var friendListAdapter: FriendListAdapter + private var friendListAdapter: FriendListAdapter? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -87,10 +86,11 @@ class HomeFragment: Fragment() { binding.rvFriends.adapter = friendListAdapter binding.rvFriends.layoutManager = LinearLayoutManager(requireContext()) - friendListAdapter.setFriendList(mockFriendList) + friendListAdapter?.setFriendList(mockFriendList) } override fun onDestroyView() { - super.onDestroyView() _binding = null + friendListAdapter = null + super.onDestroyView() } } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/MyProfile.kt b/app/src/main/java/com/sopt/now/presentation/main/home/MyProfile.kt similarity index 77% rename from app/src/main/java/com/sopt/now/MyProfile.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/MyProfile.kt index d5d23f8..be35804 100644 --- a/app/src/main/java/com/sopt/now/MyProfile.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/MyProfile.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import androidx.annotation.DrawableRes data class MyProfile( diff --git a/app/src/main/java/com/sopt/now/MyProfileViewHolder.kt b/app/src/main/java/com/sopt/now/presentation/main/home/MyProfileViewHolder.kt similarity index 91% rename from app/src/main/java/com/sopt/now/MyProfileViewHolder.kt rename to app/src/main/java/com/sopt/now/presentation/main/home/MyProfileViewHolder.kt index cc7553c..d0d3eec 100644 --- a/app/src/main/java/com/sopt/now/MyProfileViewHolder.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/home/MyProfileViewHolder.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.home import androidx.recyclerview.widget.RecyclerView import com.sopt.now.databinding.ItemMyprofileBinding diff --git a/app/src/main/java/com/sopt/now/MyPageFragment.kt b/app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageFragment.kt similarity index 58% rename from app/src/main/java/com/sopt/now/MyPageFragment.kt rename to app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageFragment.kt index 54fdac1..b8fa6fa 100644 --- a/app/src/main/java/com/sopt/now/MyPageFragment.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageFragment.kt @@ -1,18 +1,29 @@ -package com.sopt.now +package com.sopt.now.presentation.main.mypage import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.LinearLayoutManager import com.sopt.now.databinding.FragmentMypageBinding -class MyPageFragment: Fragment() { + +class MyPageFragment : Fragment() { private var _binding: FragmentMypageBinding? = null private val binding get() = _binding!! - private lateinit var friendListAdapter: FriendListAdapter // + + private var memberId: String? = null + + companion object { + fun newInstance(memberId: String?): MyPageFragment { + val fragment = MyPageFragment() + val args = Bundle().apply { + putString("memberId", memberId) + } + fragment.arguments = args + return fragment + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -21,19 +32,12 @@ class MyPageFragment: Fragment() { _binding = FragmentMypageBinding.inflate(inflater, container, false) return binding.root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val id = arguments?.getString("id") - val password = arguments?.getString("password") - val nickname = arguments?.getString("nickname") - - with(binding) { - tvId.text = "아이디: $id" - tvPassword.text = "비밀번호: $password" - tvNickname.text = "닉네임: $nickname" - } } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageViewModel.kt b/app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageViewModel.kt new file mode 100644 index 0000000..6c7bfa1 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/main/mypage/MyPageViewModel.kt @@ -0,0 +1,36 @@ +package com.sopt.now.presentation.main.mypage + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.sopt.now.presentation.data.ServicePool +import kotlinx.coroutines.launch +import retrofit2.HttpException + +class MyPageViewModel : ViewModel() { + private val _authService by lazy { ServicePool.authService } + private val _userinfo = MutableLiveData() + + fun getUserInfo() { + viewModelScope.launch { + runCatching { + _authService.getUserInfo() + }.onSuccess { + _userinfo.value = UserInfo( + isSuccess = true, + message = it.message, + id = it.data.authenticationId, + nickname = it.data.nickname, + phone = it.data.phone + ) + }.onFailure { + if(it is HttpException) { + _userinfo.value = UserInfo( + isSuccess = false, + message = "실패입니당" + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/presentation/main/mypage/UserInfo.kt b/app/src/main/java/com/sopt/now/presentation/main/mypage/UserInfo.kt new file mode 100644 index 0000000..ca71867 --- /dev/null +++ b/app/src/main/java/com/sopt/now/presentation/main/mypage/UserInfo.kt @@ -0,0 +1,9 @@ +package com.sopt.now.presentation.main.mypage + +data class UserInfo ( + val isSuccess : Boolean, + val message : String, + val id : String ="", + val nickname : String ="", + val phone : String="" +) diff --git a/app/src/main/java/com/sopt/now/SearchFragment.kt b/app/src/main/java/com/sopt/now/presentation/main/search/SearchFragment.kt similarity index 91% rename from app/src/main/java/com/sopt/now/SearchFragment.kt rename to app/src/main/java/com/sopt/now/presentation/main/search/SearchFragment.kt index 5cef32f..3e828db 100644 --- a/app/src/main/java/com/sopt/now/SearchFragment.kt +++ b/app/src/main/java/com/sopt/now/presentation/main/search/SearchFragment.kt @@ -1,4 +1,4 @@ -package com.sopt.now +package com.sopt.now.presentation.main.search import android.os.Bundle import android.view.LayoutInflater diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index b6f598a..d3397d6 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -5,63 +5,63 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".LoginActivity"> + tools:context=".presentation.auth.login.LoginActivity"> + app:layout_constraintTop_toTopOf="parent" /> + + app:layout_constraintTop_toTopOf="parent" /> + + app:layout_constraintTop_toTopOf="parent" /> + + app:layout_constraintTop_toTopOf="parent" /> + + app:layout_constraintTop_toTopOf="parent" /> +