username 수정 방법: useEffect로 수정할 경우의 username 변경 시 username을 다시 set하는 함수를 호출하여 useState로 관리하여야 함.

백아름·2023년 9월 21일
0

프론트엔드

목록 보기
77/80

마주한 문제점

  • username을 edit할 경우 tempUsername 값을 생성하여 임시로 저장할 수 있는 값을 만들어놓음.
    const [tempUsername, setTempUsername] = useState(username)
  • 이렇게 useState로 관리하게 하였으며, 초기값은 redux의 useSelector로 불러오는 username을 설정하였음.
  • username은 최상위 컴포넌트인 App 컴포넌트에서 useEffect의 mount시점을 빈 배열로 설정하여 서버를 통해서 매번 화면이 렌더링될 때마다 get하여 redex의 username으로 저장할 수 있도록 dispatch하였음.
    (하단 코드 참고)
  • 근데도 username이 불러와지지 않아서 Header값에는 잘 들어갈 때에도 반영이 잘 되는지 보기 위하여 테스트해본 결과, 브라우저의 헤더상에는 username이 잘 불러와지기는 하는데 console.log(username)을 찍었을 때에는 곧바로 useSelector로 반영이 안되는 것을 볼 수 있었음.

username 렌더링 부분 코드

              <Username>
                {isEditing ? (
                  <>
                    <div style={{ display: 'flex', flexDirection: 'column' }}>
                      <input
                        style={{ fontSize: '16px', fontWeight: '700', textAlign: 'center', padding: 0, margin: 0 }}
                        value={tempUsername}
                        onChange={handleInputChange}
                        onBlur={handleInputBlur} // (옵션) input 바깥을 클릭했을 때 변경 적용
                        onKeyUp={handleInputKeyUp}
                        autoFocus // input이 활성화될 때 자동으로 포커스
                      />

                      <span style={{ fontSize: '12px', fontWeight: '500', textAlign: 'center' }}>
                        {byte}byte / 20byte
                      </span>
                    </div>
                  </>
                ) : (
                  <>
                    {username}
                    <FiEdit2
                      onClick={handleEditClick}
                      style={{ paddingTop: '5px', color: '#a1a1a1', cursor: 'pointer' }}
                    />
                  </>
                )}
              </Username>
  • 문제는 input 부분의 value값이 {tempusername}으로 부여되어있었는데 useState의 초기값인 username이 불러와지지 않으니 죽을 맛이었음.. 그저 userSlice에 세팅된 하드코딩 초기값인 "User"라던지, "", 이런 공백란만 떴으니까..

tempUsername 초기값 세팅을 어떻게 해결하지?

  • tempUsername 초기값을 username을 받아와야만 연필 모양인 edit을 눌렀을 때 해당 유저네임 그대로를 불러온 상태에서 수정할 수 있음.

해결방법: useEffect로 [username]이 바뀔 때마다 다시 tempUsername을 다시 setTempUsername(username)을 해주어야했음

  • 이유: useEffect로 다시 setTempUsername을 해주지 않으면 tempUsername은 useSelector 내의 기본값을 받아올 수밖에 없음. 왜냐? App.tsx의 useEffect를 하던, 해당 앱 내에서 useEffect로 setUsername을 해줘도 setTempUsername을 건드리는 값은 없기 때문에 username이 바뀐 시점에서 다시 tempUsername에 반영할 수 있도록 useEffect로 한 번 더 요청을 해줘서 값을 저장해야만 반영이 되는 거였음.
useEffect(() => {
    setTempUsername(username);
    setByte(getByteLength(username))
  }, [username]);
  • 무튼 상단의 코드를 적어줬더니 해결이 되었다~ 이 말씀.


  • 결론적으로 이렇게 되었다!

username 변경할 수 있는 함수 관련 코드

  const [isEditing, setIsEditing] = useState(false); // username을 수정하는 중인지 상태
  const [byte, setByte] = useState(getByteLength(username));
  const [tempUsername, setTempUsername] = useState(username) // 임시 username 저장

  useEffect(() => {
    setTempUsername(username);
    setByte(getByteLength(username))
  }, [username]);

  //20byte 길이 검사하는 함수 - 한글은 3byte, 영문 및 숫자는 1byte
  function getByteLength(str: any) {
    return str.split('').reduce((byteLength: any, char: any) => {
      const charCode = char.charCodeAt(0);
      if (charCode <= 0x7f) {
        return byteLength + 1;
      } else if (charCode <= 0x7ff) {
        return byteLength + 2;
      } else if (0xd800 <= charCode && charCode <= 0xdbff) {
        // Surrogate pair: These take 4 bytes in UTF-8 and 2 chars in UCS-2
        // (Assume next char is the other [valid] half and just skip it)
        return byteLength + 4;
      } else {
        // Other characters are 3 bytes in UTF-8
        return byteLength + 3;
      }
    }, 0);
  }

  // 정규식을 사용하여 허용된 문자 및 기호만 입력되게 하는 로직
  const isValidInput = (input: any) => {
    const pattern = /^[A-Za-z0-9_.-]*$/;
    return pattern.test(input);
  };

  const handleEditClick = () => {
    setIsEditing(true);    
  };

  const handleInputChange = (e: any) => {
    const inputValue = e.target.value;
    const inputByte = getByteLength(inputValue);

    if (getByteLength(inputValue) <= 20 && isValidInput(inputValue)) {
      setTempUsername(inputValue);
      setByte(inputByte);
    }
    console.log(tempUsername)
    console.log(username)
  };

  const handleInputBlur = async () => {
    // 사용자가 input 바깥을 클릭했을 때 변경을 적용할 경우 아래 코드 추가
    await updateUsername();
    setIsEditing(false);
    // setTempUsername(tempUsername);
    setUsername(tempUsername);
  };

  const handleInputKeyUp = async (e: any) => {
    if (e.key === 'Enter') {
      await updateUsername();
      setIsEditing(false);
      // setTempUsername(tempUsername);
      setUsername(tempUsername);
    }
  };

  //닉네임 변경
  const updateUsername = async () => {
    // tempUsername이 빈 문자열이면 업데이트를 건너뛴다.
    if (!tempUsername.trim()) {
      setTempUsername(username); // 현재 username으로 tempUsername을 리셋한다.
      return;
    }

    // dispatch
    dispatch(setUsername(tempUsername));

    // 서버 업데이트
    try {
      const response = await axios.patch(`${APIURL}/members/username`, tempUsername, {
        headers: {
          Authorization: accessToken,
          'Refresh-Token': refreshToken,
        },
        params: {
          username: tempUsername, // 쿼리 파라미터로 username 추가
        },
      });

      console.log(username);
      console.log(tempUsername)

      if (response.status === 200) {
        console.log(response.data);
        dispatch(setUsername(tempUsername));
     }
    } catch (error) {
      console.log(error);
    }
  };
profile
곧 훌륭해질 거에요!

0개의 댓글