이번 포스팅에서는 저번 포스팅에서 만든 MyPageFragment에서 닉네임 변경 기능을 완성해보도록 하겠습니다.
fragment_my_page.xml를 아래와 같이 수정한다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
tools:context=".MyPageFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="@drawable/mypage" />
<Button
android:id="@+id/nickNameBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:padding="10dp"
android:text="닉네임 변경"
android:textColor="@color/black"
android:gravity="center"
android:background="@color/skyBlue"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="35dp"
android:textSize="25sp"
android:textStyle="bold"/>
<Button
android:id="@+id/passwordBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:padding="10dp"
android:text="비밀번호 변경"
android:textColor="@color/black"
android:gravity="center"
android:background="@color/skyBlue"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="35dp"
android:textSize="25sp"
android:textStyle="bold"/>
<Button
android:id="@+id/profileBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:padding="10dp"
android:text="프로필 변경"
android:textColor="@color/black"
android:gravity="center"
android:background="@color/skyBlue"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="35dp"
android:textSize="25sp"
android:textStyle="bold"/>
<Button
android:id="@+id/logoutBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:padding="10dp"
android:text="로그아웃"
android:textColor="@color/black"
android:gravity="center"
android:background="@color/skyBlue"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="35dp"
android:textSize="25sp"
android:textStyle="bold"/>
<Button
android:id="@+id/quitBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:padding="10dp"
android:text="회원 탈퇴"
android:textColor="@color/black"
android:gravity="center"
android:background="@color/skyBlue"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="35dp"
android:textSize="25sp"
android:textStyle="bold"/>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="@+id/freind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="1dp"
android:gravity="center"
android:layout_weight="1"
android:src="@drawable/friend" />
<ImageView
android:id="@+id/chat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="1dp"
android:gravity="center"
android:layout_weight="1"
android:src="@drawable/chat" />
<ImageView
android:id="@+id/mypage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/main_border"
android:layout_margin="1dp"
android:gravity="center"
android:layout_weight="1"
android:src="@drawable/mypage" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
① user > dto 패키지 하위로 GetUserRes를 추가한다.
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class GetUserRes {
private String imgUrl;
private String nickName;
}
② UserController에 아래의 API를 추가한다.
/**
* 유저 정보 반환
*/
@GetMapping("")
public BaseResponse<GetUserRes> getUserInfo(@RequestParam String uid) {
try {
return new BaseResponse<>(userService.getUserInfo(uid));
} catch (BaseException exception) {
return new BaseResponse<>(exception.getStatus());
}
}
/**
* 닉네임 변경
*/
@PatchMapping("/nickname")
public BaseResponse<String> modifyUserName(@RequestParam String nickName) {
try {
Long userId = jwtService.getUserIdx();
return new BaseResponse<>(userService.modifyUserNickName(userId, nickName));
} catch (BaseException exception) {
return new BaseResponse<>(exception.getStatus());
}
}
③ UserRepsitory에 아래의 쿼리를 추가한다.
@Query("select u from User u where u.uid = :uid")
Optional<User> findUserByUid(@Param("uid") String uid);
④ UtilService에 아래의 메서드를 추가한다.
public User findByUserUidWithValidation(String uid) throws BaseException {
return userRepository.findUserByUid(uid)
.orElseThrow(() -> new BaseException(NONE_EXIST_USER));
}
⑤ UserService에 아래의 메서드를 추가한다.
public GetUserRes getUserInfo(String uid) throws BaseException {
User user = utilService.findByUserUidWithValidation(uid);
String profileUrl = (user.getProfile() != null) ? user.getProfile().getProfileUrl() : null;
String nickName = user.getNickName();
return new GetUserRes(profileUrl, nickName);
}
@Transactional
public String modifyUserNickName(Long userId, String nickName) throws BaseException {
User user = utilService.findByUserIdWithValidation(userId);
user.setNickName(nickName);
return "회원정보가 수정되었습니다.";
}
① mypage 패키지를 새로 생성하고 그 하위로 NickNameActivity를 추가한다.
② activity_nick_name.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"
tools:context=".mypage.NickNameActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="@drawable/mypage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginTop="200dp"
android:text="현재 닉네임: "
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/oldNickName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="200dp"
android:text="User Nickname "
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="40dp"
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/newNickName"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@drawable/main_border"
android:hint="변경할 닉네임"
android:padding="5dp"
android:textColorHint="#808080"
android:textSize="25sp" />
<Button
android:id="@+id/newNickNameBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:text="변경"
android:background="@color/skyBlue"
android:textSize="35sp"
android:layout_marginVertical="60dp"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
③ 마이페이지에서 닉네임 변경 버튼을 클릭했을 때, 닉네임 변경페이지로 전환되도록 MyPageFragment의 onCreateView를 아래와 같이 수정한다.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_my_page, container, false)
val nickName = view.findViewById<Button>(R.id.nickNameBtn)
nickName.setOnClickListener {
val intent = Intent(requireActivity(), NickNameActivity::class.java)
startActivity(intent)
}
val freind = view.findViewById<ImageView>(R.id.freind)
freind.setOnClickListener {
it.findNavController().navigate(R.id.action_myPageFragment_to_userListFragment)
}
val chat = view.findViewById<ImageView>(R.id.chat)
chat.setOnClickListener {
it.findNavController().navigate(R.id.action_myPageFragment_to_chatListFragment)
}
return view
}
④ api > dto 패키지 하위로 GetUserRes data class를 추가한다.
data class GetUserRes(
@SerializedName("imgUrl")
val imgUrl : String,
@SerializedName("nickName")
val nickName : String
)
⑤ api 패키지 하위로, MyPageApi 인터페이스를 추가한다.
interface MyPageApi {
@GET("/users")
suspend fun getUserInfo(@Query("uid") uid : String): BaseResponse<GetUserRes>
@PATCH("/users/nickname")
suspend fun modifyUserName(
@Header("Authorization") accessToken : String,
@Query("nickName") nickName : String
) : BaseResponse<String>
}
⑥ RetrofitInstance를 아래와 같이 수정한다.
companion object {
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(ApiRepository.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val userApi = retrofit.create(UserApi::class.java)
val myPageApi = retrofit.create(MyPageApi::class.java)
}
⑦ NickNameActivity를 아래와 같이 수정한다.
class NickNameActivity : AppCompatActivity() {
lateinit var nickName : String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nick_name)
val oldNickName = findViewById<TextView>(R.id.oldNickName)
CoroutineScope(Dispatchers.IO).launch {
val response = getUserInfo(FirebaseAuthUtils.getUid())
if (response.isSuccess) {
this@NickNameActivity.nickName = response.result?.nickName.toString()
runOnUiThread {
oldNickName.text = nickName
}
} else {
Log.d("NickNameActivity", "유저의 정보를 불러오지 못함")
}
}
val newNickNameBtn = findViewById<Button>(R.id.newNickNameBtn)
newNickNameBtn.setOnClickListener {
val newNickName = findViewById<TextInputEditText>(R.id.newNickName)
val newNickNameStr = newNickName.text.toString()
if(newNickNameStr.isEmpty()) {
Toast.makeText(this, "닉네임을 입력해주세요", Toast.LENGTH_SHORT).show()
}
else {
getAccessToken { accessToken ->
if (accessToken.isNotEmpty()) {
CoroutineScope(Dispatchers.IO).launch {
val response = modifyUserName(accessToken, newNickNameStr)
if (response.isSuccess) {
withContext(Dispatchers.Main) {
Toast.makeText(this@NickNameActivity, "닉네임 변경이 완료되었습니다", Toast.LENGTH_SHORT).show()
val intent = Intent(this@NickNameActivity, MainActivity::class.java)
startActivity(intent)
}
}
else {
Log.d("NickNameActivity", "닉네임 변경 실패")
val message = response.message
Log.d("NickNameActivity", message)
withContext(Dispatchers.Main) {
Toast.makeText(this@NickNameActivity, message, Toast.LENGTH_SHORT).show()
}
}
}
} else {
Log.e("NickNameActivity", "Invalid Token")
}
}
}
}
}
private suspend fun getUserInfo(uid: String): BaseResponse<GetUserRes> {
return RetrofitInstance.myPageApi.getUserInfo(uid)
}
private suspend fun modifyUserName(accessToken : String, nickName : String) : BaseResponse<String> {
return RetrofitInstance.myPageApi.modifyUserName(accessToken, nickName)
}
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("NickNameActivity", "onCancelled", databaseError.toException())
}
}
FirebaseRef.userInfo.child(FirebaseAuthUtils.getUid()).addListenerForSingleValueEvent(postListener)
}
}
코드를 실행해보면, 닉네임이 변경되면서 MainActivity로 이동할 것이다.