마이페이지 - 닉네임 변경하기

변현섭·2023년 9월 1일
0

이번 포스팅에서는 저번 포스팅에서 만든 MyPageFragment에서 닉네임 변경 기능을 완성해보도록 하겠습니다.

1. 레이아웃 구성하기

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>

2. 닉네임 변경하기

1) 백엔드

① 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 "회원정보가 수정되었습니다.";
}

2) 프론트엔드

① 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를 아래와 같이 수정한다.

  • 참고로, Fragment에서 Intent를 사용할 때에는 this를 쓸 수 없다(Context 또는 Activity만 입력가능하기 때문). 그러므로 requireActivity()를 넣어야한다.
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로 이동할 것이다.

profile
Java Spring, Android Kotlin, Node.js, ML/DL 개발을 공부하는 인하대학교 정보통신공학과 학생입니다.

0개의 댓글