프로젝트 진행 중 회원가입시 특별히 본인의 닉네임을 설정하지 않은 유저들에게 랜덤으로 닉네임을 부여하는 기능을 추가했다.
이는 그리 어렵지 않은 작업이었지만, 다음에 재사용할 상황이 찾아올 수 있으니, 구현 과정을 기록하고자 한다.
랜덤 닉네임의 경우 우선 닉네임을 어떤 형식으로 부여해둘 것인지 생각해야한다. 이번 프로젝트의 경우 에브리타임
과 비슷한 대학생들을 타겟으로 한 커뮤니티 사이트가 주제인 만큼, ex) 서울대123
과 같이 학교 이름과 숫자를 조합해 부여하면 좋을 것 같다고 생각했다.
이를 위해 학교 부분에 들어갈 텍스트를 배열로 만들어준다.
const names = [
'서울대',
'한양대',
'고려대',
'서강대',
'연세대',
'중앙대',
'부산대',
'경북대',
'충남대',
'전북대',
'Harvard',
'Yale',
'Stanford',
'MIT',
'Princet',
'Oxford',
'Camb',
'Caltech',
'UCLA',
'Berkeley',
];
세상에 존재하는 학교의 이름은 무수히 많지만, 국내외 대학교 중 생각나는 학교들의 이름을 가져왔다. 양이 적다고 생각될 수 있는데, 실제로 서비스하지 않는 사이드 프로젝트이기에 유저의 수가 적으며 뒤에 랜덤한 숫자를 부여한다고 했을 때 이 정도의 학교명으로도 충분히 많은 수의 닉네임을 뽑아낼 수 있다.
텍스트가 담긴 배열을 만들었다면, 이제 이 배열에서 랜덤으로 하나의 텍스트를 뽑아 닉네임으로 사용해줘야한다.
이를 위해서 자바스크립트의 Math.random
과 Math.floor
메소드를 사용할 수 있다. random
메소드는 0 ~ 1 사이의 부동소수점의 난수를 랜덤으로 생성하며, floor
의 경우 숫자를 내림차순 한다. 참고 : MDN
두 메소드를 이용해 배열 내에서 랜덤한 인덱스를 뽑아 출력해보자.
const getRandomItem = (arr: string[]): string =>
arr[Math.floor(Math.random() * arr.length)];
함수의 이름은 getRandomItem
이라고 지정하고, string 문자열로 이루어진 배열 arr
를 인자로 받는다.
이후 random
메소드를 사용해 랜덤한 수를 생성하고, arr
의 length를 곱해주면, 0 부터 length - 1 까지의 랜덤한 부동소수점 난수를 생성하게 되고, 이를 floor
를 통해 내림차순 하여 정수로 만들어준다.
이를 실행하면 다음과 같이 랜덤한 텍스트를 출력하게 되고, 이를 namePart
라는 변수에 저장해준다.
이제 랜덤한 텍스트를 뽑아냈으니, 숫자를 생성해 텍스트 뒤에 붙여주기만 하면 끝이다.
그 전에, 프로젝트 내에서 닉네임의 validation은 최소 2글자 ~ 최대 8글자만을 허용했기에 텍스트의 길이를 제외하고 남은 길이를 계산해줘야 했다.
const remainingLength = 8 - namePart.length;
8에서 랜덤 텍스트를 저장한 변수 namePart
의 length를 빼주면 간단하게 남은 자릿수를 계산할 수 있다.
텍스트가 '부산대' 라면 8 - 3 = 5 라는 결과값이 나올것이다.
이제 이를 이용해 남는 부분에 랜덤한 숫자를 부여해보자
// 랜덤 숫자 생성하는 함수
const generateRandomNumber = (min: number, max: number): string =>
Math.floor(min + Math.random() * (max - min + 1)).toString();
// 남는 길이가 1 이상일 경우 남는 자릿수를 이용해 랜덤한 숫자 생성
const numberPart =
remainingLength > 0
? generateRandomNumber(Math.pow(10, remainingLength - 1), Math.pow(10, remainingLength) - 1)
: '';
// 최종 결과 값 리턴
return `${namePart}${numberPart}`;
remainingLength
의 길이에 맞게 최소값과 최대값을 계산하고, 그 범위 내에서 랜덤한 정수를 반환해 이를 문자열로 변환한다.
최종적으로 namePart
와 numberPart
를 붙여 리턴해 랜덤한 닉네임을 반환해준다.
// getRandomNick.ts
export const generateRandomNickname = (): string => {
const names = [
'서울대',
'한양대',
'고려대',
'서강대',
'연세대',
'중앙대',
'부산대',
'경북대',
'충남대',
'전북대',
'Harvard',
'Yale',
'Stanford',
'MIT',
'Princet',
'Oxford',
'Camb',
'Caltech',
'UCLA',
'Berkeley',
];
const getRandomItem = (arr: string[]): string => arr[Math.floor(Math.random() * arr.length)];
const generateRandomNumber = (min: number, max: number): string =>
Math.floor(min + Math.random() * (max - min + 1)).toString();
const selectNamePart = (): string => getRandomItem(names);
const namePart = selectNamePart();
const remainingLength = Math.max(0, 8 - namePart.length);
const numberPart =
remainingLength > 0
? generateRandomNumber(Math.pow(10, remainingLength - 1), Math.pow(10, remainingLength) - 1)
: '';
return `${namePart}${numberPart}`;
};
이제 만들어진 함수를 닉네임 페이지에서 사용해보자.
import React, { useState, useEffect, useRef, ChangeEvent } from 'react';
import { generateRandomNickname } from '@/utils/randomNick';
// import ...
const SetupProfileInfo = ({ user }: { user?: UserDataInterface | undefined }) => {
const { register: imageUrlRegister, setValue: setImageUrlValue } = useSetupInput('imageUrl');
const {
register: nicknameRegister,
errors,
status: nicknameStatus,
setValue: setNicknameValue,
} = useSetupInput('nickname', nicknameValidation);
const [profileImageUrl, setProfileImageUrl] = useState<string | undefined>(
URL.DEFAULT_PROFILE_IMG,
);
const [nickname, setNickname] = useState<string>('');
useEffect(() => {
if (user) {
setProfileImageUrl(user.imageUrl || URL.DEFAULT_PROFILE_IMG);
setImageUrlValue('imageUrl', user.imageUrl || URL.DEFAULT_PROFILE_IMG);
setNickname(user.nickname);
setNicknameValue('nickname', user.nickname);
} else {
// 기존 user data가 없을 경우 (즉, 새로 가입하는 회원의 경우) 기본 프로필 이미지와 랜덤 닉네임 부여
const defaultImageUrl = URL.DEFAULT_PROFILE_IMG;
const randomNickname = generateRandomNickname();
setProfileImageUrl(defaultImageUrl);
setImageUrlValue('imageUrl', defaultImageUrl);
setNickname(randomNickname);
setNicknameValue('nickname', randomNickname);
}
}, [user, setImageUrlValue, setNicknameValue]);
return (
<>
{!user && (
<MainComment
style={{ fontSize: '20px', textAlign: 'center' }}
comment='프로필을 설정해주세요'
/>
)}
<InputWrapper>
<Input
type='text'
status={nicknameStatus}
{...nicknameRegister('nickname')}
defaultValue={nickname}
errorMessage={
errors.nickname && typeof errors.nickname.message === 'string'
? errors.nickname.message
: undefined
}
placeholder='사용하실 닉네임을 입력해주세요.'
/>
</InputWrapper>
<ButtonContainer>
<Button $isFullWidth type='submit' disabled={nicknameStatus !== 'success'}>
{user ? '변경하기' : '회원가입'}
</Button>
</ButtonContainer>
</>
);
};
export default SetupProfileInfo;
// styled-components ...
SetupProfileInfo
컴포넌트는 react-hook-form을 이용하여 사용자의 프로필 사진과 닉네임을 관리한다. 이 때 해당 컴포넌트는 회원가입과 회원수정 단계에서 중복으로 사용되기에 user
를 props로 전달받아 기존에 가입된 user
의 데이터가 존재할 경우 해당 유저의 이미지와 닉네임을 불러와 회원수정 페이지에 적용하고, 그 외의 경우 (신규 가입) 에는 기본 이미지와 랜덤 닉네임을 부여한다.
useEffect
를 사용해 컴포넌트가 마운트 될 때 마다 새로운 랜덤 닉네임이 적절하게 부여되는 것을 확인할 수 있다.