닉네임 변경에 이어 비밀번호 변경 기능도 추가해보도록 하겠습니다.
① user > dto 패키지 하위로 PatchPasswordReq를 추가한다.
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class PatchPasswordReq {
private String exPassword; // 이전 비밀번호
private String newPassword; // 새 비밀번호
private String newPasswordChk; // 새 비밀번호 확인
}
② UserController에 아래의 API를 추가한다.
/**
* 유저 비밀번호 변경
*/
@PatchMapping("/password")
public BaseResponse<String> modifyPassword(@RequestBody PatchPasswordReq patchPasswordReq) {
try {
Long userId = jwtService.getUserIdx();
return new BaseResponse<>(userService.modifyPassword(userId, patchPasswordReq));
} catch (BaseException exception) {
return new BaseResponse<>(exception.getStatus());
}
}
③ UserService에 아래의 메서드를 추가한다.
/**
* 유저 비밀번호 변경
*/
@Transactional
public String modifyPassword(Long userId, PatchPasswordReq patchPasswordReq) throws BaseException {
try {
User user = utilService.findByUserIdWithValidation(userId);
String password;
try {
password = new AES128(Secret.USER_INFO_PASSWORD_KEY).decrypt(user.getPassword());
} catch (Exception ignored) {
throw new BaseException(BaseResponseStatus.PASSWORD_DECRYPTION_ERROR);
}
// 이전 비밀번호가 일치하지 않는 경우
if (!patchPasswordReq.getExPassword().equals(password)) {
throw new BaseException(BaseResponseStatus.EX_PASSWORD_MISSMATCH);
}
// 이전 비밀번호와 새 비밀번호가 일치하는 경우
if(patchPasswordReq.getNewPassword().equals(patchPasswordReq.getExPassword())) {
throw new BaseException(BaseResponseStatus.CANNOT_UPDATE_PASSWORD);
}
// 새 비밀번호와 새 비밀번호 확인이 일치하지 않는 경우
if(!patchPasswordReq.getNewPassword().equals(patchPasswordReq.getNewPasswordChk())) {
throw new BaseException(BaseResponseStatus.PASSWORD_MISSMATCH);
}
String pwd;
try{
pwd = new AES128(Secret.USER_INFO_PASSWORD_KEY).encrypt(patchPasswordReq.getNewPassword()); // 암호화코드
}
catch (Exception ignored) { // 암호화가 실패하였을 경우 에러 발생
throw new BaseException(BaseResponseStatus.PASSWORD_ENCRYPTION_ERROR);
}
user.setPassword(pwd);
return "비밀번호 변경이 완료되었습니다.";
} catch (BaseException exception) {
throw new BaseException(exception.getStatus());
}
}
① mypage 패키지 하위로, PasswordActivity를 생성한다.
② activity_password.xml 파일에 아래의 내용을 입력한다.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/main_border"
android:orientation="vertical"
tools:context=".mypage.PasswordActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginBottom="50dp"
android:src="@drawable/mypage"/>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterMaxLength="12"
app:counterEnabled="true"
android:layout_marginHorizontal="40dp"
android:layout_marginVertical="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/oldPassword"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@drawable/main_border"
android:hint="현재 비밀번호"
android:inputType="textPassword"
android:padding="5dp"
android:textColorHint="#808080"
android:textSize="25sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterMaxLength="12"
app:counterEnabled="true"
android:layout_marginHorizontal="40dp"
android:layout_marginVertical="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newPassword"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@drawable/main_border"
android:hint="새 비밀번호"
android:inputType="textPassword"
android:padding="5dp"
android:textColorHint="#808080"
android:textSize="25sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterMaxLength="12"
app:counterEnabled="true"
android:layout_marginHorizontal="40dp"
android:layout_marginVertical="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newPasswordChk"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@drawable/main_border"
android:hint="새 비밀번호 확인"
android:inputType="textPassword"
android:padding="5dp"
android:textColorHint="#808080"
android:textSize="25sp" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/newPasswordBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_marginVertical="50dp"
android:layout_marginHorizontal="40dp"
android:background="@color/skyBlue"
android:text="변경"
android:textSize="35sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</LinearLayout>
</ScrollView>
③ 마이페이지에서 비밀번호 변경 버튼을 클릭했을 때, 비밀번호 변경 페이지로 전환되도록 MyPageFragment의 onCreateView에 아래의 내용을 추가한다.
val password = view.findViewById<Button>(R.id.passwordBtn)
password.setOnClickListener {
val intent = Intent(requireActivity(), PasswordActivity::class.java)
startActivity(intent)
}
④ api > dto 패키지 하위로 PatchPasswordReq data class를 추가한다.
data class PatchPasswordReq(
@SerializedName("exPassword")
val exPassword : String,
@SerializedName("newPassword")
val newPassword : String,
@SerializedName("newPasswordChk")
val newPasswordChk : String
)
⑤ MyPageApi 인터페이스에 아래의 API를 추가한다.
@PATCH("/users/password")
suspend fun modifyPassword(
@Header("Authorization") accessToken : String,
@Body patchPasswordReq : PatchPasswordReq
): BaseResponse<String>
⑥ PasswordActivity를 아래와 같이 수정한다.
class PasswordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_password)
val newPasswordBtn = findViewById<Button>(R.id.newPasswordBtn)
newPasswordBtn.setOnClickListener {
val oldPassword = findViewById<TextInputEditText>(R.id.oldPassword)
val oldPasswordStr = oldPassword.text.toString()
val newPassword = findViewById<TextInputEditText>(R.id.newPassword)
val newPasswordStr = newPassword.text.toString()
val newPasswordChk = findViewById<TextInputEditText>(R.id.newPasswordChk)
val newPasswordChkStr = newPasswordChk.text.toString()
if(oldPasswordStr.isEmpty()) {
Toast.makeText(this, "현재 비밀번호를 입력해주세요", Toast.LENGTH_SHORT).show()
}
else if(newPasswordStr.isEmpty()) {
Toast.makeText(this, "새 비밀번호를 입력해주세요", Toast.LENGTH_SHORT).show()
}
else if(newPasswordStr != newPasswordChkStr) {
Toast.makeText(this, "비밀번호와 비밀번호 확인의 입력 값이 다릅니다.", Toast.LENGTH_SHORT).show()
}
else {
val patchPasswordReq = PatchPasswordReq(oldPasswordStr, newPasswordStr, newPasswordChkStr)
getAccessToken { accessToken ->
if (accessToken.isNotEmpty()) {
CoroutineScope(Dispatchers.IO).launch {
val response = modifyPassword(accessToken, patchPasswordReq)
if (response.isSuccess) {
withContext(Dispatchers.Main) {
Toast.makeText(this@PasswordActivity, "비밀번호 변경이 완료되었습니다", Toast.LENGTH_SHORT).show()
val intent = Intent(this@PasswordActivity, MainActivity::class.java)
startActivity(intent)
}
}
else {
Log.d("PasswordActivity", "비밀번호 변경 실패")
val message = response.message
Log.d("PasswordActivity", message)
withContext(Dispatchers.Main) {
Toast.makeText(this@PasswordActivity, message, Toast.LENGTH_SHORT).show()
}
}
}
} else {
Log.e("PasswordActivity", "Invalid Token")
}
}
}
}
}
private suspend fun modifyPassword(accessToken : String, patchPasswordReq : PatchPasswordReq): BaseResponse<String> {
return RetrofitInstance.myPageApi.modifyPassword(accessToken, patchPasswordReq)
}
private fun getAccessToken(callback: (String) -> Unit) {
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data = dataSnapshot.getValue(com.chrome.chattingapp.authentication.UserInfo::class.java)
val accessToken = data?.accessToken ?: ""
callback(accessToken)
}
override fun onCancelled(databaseError: DatabaseError) {
Log.w("PasswordActivity", "onCancelled", databaseError.toException())
}
}
FirebaseRef.userInfo.child(FirebaseAuthUtils.getUid()).addListenerForSingleValueEvent(postListener)
}
}
코드를 실행시켜보면, 현재 비밀번호와 새비밀번호를 입력했을 때 비밀번호 변경이 잘 이루어지는 것을 확인할 수 있을 것이다.