회원정보 수정

hyuko·2023년 5월 21일
2

팀 프로젝트

목록 보기
7/8

회원 정보 수정

회원 정보 수정은 modifyUser 메서드와 modifyUser API 엔드포인트를 통해 이루어집니다.
클라이언트에서는 MyPage 컴포넌트를 통해 회원 정보 수정 페이지로 이동하고,
사용자가 수정할 정보를 입력한 후 수정을 완료할 수 있습니다.

클라이언트 코드 (MyPage.js)

import React, { useState } from 'react';
import axios from 'axios';
import { useQuery } from 'react-query';
import { useNavigate } from 'react-router-dom';

const MyPage = () => {
  const navigate = useNavigate();
  // 변경 부분<< 처음에는 authState를 useRecoilState를 통해서 true false값만 주었지만
// atom을 만들 당시 바로 default값에 변수를 추가해서 isAuthenticated를 선언하고 
// useRecoilValue를 통해 boolean변수 하나만 선언하여 상태를 관리한다.
  // 인증을 하여 개인정보를 가지고 오는 principal 관련 get요청은 모두 enabled로 잡아주면된다.
  const authState = useRecoilValue(authenticationState);
  const [checkType,setCheckType] = useState("myplan");
  const [userInfo, setUserInfo] = useState({
    email: '',
    userId: '',
    profileImg: ''
  })

  const principal = useQuery(["principal"], async () => {
    const accessToken = localStorage.getItem("accessToken");
    const response = await axios.get('http://localhost:8080/api/v1/auth/principal', { params: { accessToken } });
    return response;
  }, {
    enabled: authState.isAuthenticated,
    onSuccess : (response) => {
      setUserInfo({
        email: response.data.email,
        userId: response.data.userId,
        name: response.data.name,
        profileImg: response.data.postsImgUrl
      })
    }
  });

  const myPlanChangeHandler = (type) => {
    setCheckType(type);
  }

  return (
    <div css={container}>
      <main css={main}>
        <div css={imgContainer}>
          <img css={imgStyle} src={userInfo.profileImg} alt=""/>
        </div>
        <div>{userInfo.email || '이메일 로딩 중...'}</div>
        <div css={modifyButtons}>
          <ModifyButton onClick={() => navigate(`/user/modify/${principal?.data?.data?.userId || ''}`)}>수정하기</ModifyButton>
          <ModifyButton onClick={() => navigate(`/user/modify/password/${principal?.data?.data?.userId || ''}`)}>비밀번호 변경</ModifyButton>
        </div>
        <div css={mainContents}>
          <div css={myPlanAndReview} onClick={() => myPlanChangeHandler('myPlan')}>
            <span>나의 일정</span>
            <span>0</span>
          </div>
          <div css={myPlanAndReview} onClick={() => myPlanChangeHandler('myReview')}>
            <span>나의 리뷰</span>
            <span>0</span>
          </div>
        </div>
        <div css={planAndReviewContainer}>
          {checkType === 'myPlan'?(<TravelList/>):(<MyReviewList/>)}
        </div>
      </main>
    </div>
  );
};

export default MyPage;

서버 코드 (UserController.java)

@RestController
@RequestMapping("/api/v1/user")
public class UserController {
  
// user 정보 수정
    @PutMapping("/{userId}")
    public ResponseEntity<?> modifyUser(@PathVariable int userId, UserModifyReqDto userModifyReqDto) {

        return ResponseEntity.ok(DataRespDto.of(userService.modifyUser(userId, userModifyReqDto)));
    }
  
  // 기타 코드 생략...
}

MyPage 컴포넌트에서는 현재 사용자의 이메일을 표시하고,
회원 정보 수정 버튼과 비밀번호 변경 버튼을 제공합니다.
각 버튼을 클릭하면 해당 기능을 수행하기 위해 해당 페이지로 이동합니다.

클라이언트 코드 (ModifyUser.js)

import React, { useState } from 'react';
import axios from 'axios';

const ModifyForm = () => {
    const [ authState, setAuthState ] = useRecoilState(authenticationState);
    const [ imgFiles, setImgFiles ] = useState(null);
    const [ preview, setPreview ] = useState('');

    const principal = useQuery(["principal"], async () => {
        const accessToken = localStorage.getItem("accessToken");
        const response = await axios.get('http://localhost:8080/api/v1/auth/principal', {params: {accessToken}});
        return response;
    }, {
        enabled: authState.isAuthenticated,
        onSuccess: (response) => {
            setUpdateUser({
                profileImg: response.data.postsImgUrl,
                email: response.data.email,
                name: response.data.name,
                phone: response.data.phone,
                address: response.data.address
            })
        }
    });

    const [inputDisabled, setInputDisabled] = useState({
        name: true,
        phone: true,
        address: true
    });

    const [buttonText, setButtonText] = useState({
        name: 'Edit',
        phone: 'Edit',
        address: 'Edit'
    });


    const [ updateUser, setUpdateUser ] = useState({
        profileImg: '',
        email: '',
        name:  '',
        phone: '',
        address:  ''

    });

    const [ errorMessages, setErrorMessages ] = useState({
        name: '',
        phone: ''
    });

    const [ isName, setIsName ] = useState(true)
    const [ isPhone, setIsPhone ] = useState(true)

    // 유효성 검사 -> user 정보수정
    const onChangeHandler = (e) => {
        const { name, value } = e.target;

        if(name === 'name') {
            const nameRegex = /^[가-힣]{2,8}$/;
            if(!nameRegex.test(value)) {
                setErrorMessages((errors) => ({
                    ...errors,
                    name: '2글자 이상 8글자 미만으로 입력해주세요.'
                }));
                setIsName(false);
            } else {
                setErrorMessages((errors) => ({
                    ...errors,
                    name: '올바른 이름 형식입니다. :)'
                }));
                setIsName(true);
            }
        } else if (name === 'phone') {
            const phoneRegex = /^\d{3}-\d{3,4}-\d{4}$/;
            if (!phoneRegex.test(value)) {
              setErrorMessages((errors) => ({
                ...errors,
                phone: '올바른 전화번호 형식이 아닙니다.'
              }));
              setIsPhone(false);
            } else {
              setErrorMessages((errors) => ({
                ...errors,
                phone: '올바른 전화번호 형식입니다. :)'
              }));
              setIsPhone(true);
            }
        }

        setUpdateUser(
            {
                ...updateUser,
                [name]: value
            }
        )

    };


    const modifyUser = useMutation(async (modifyData) => {
        try {
            const formData = new FormData();

            formData.append("email", modifyData.email)
            formData.append("name", modifyData.name)
            formData.append("phone", modifyData.phone)
            formData.append("address", modifyData.address)
            formData.append("profileImg", imgFiles)
            console.log(imgFiles);
            console.log(formData.get("profileImg"));

            const option = {
                headers: {
                    'Content-Type': 'multipart/form-data',
                    Authorization: `${localStorage.getItem('accessToken')}`
                }
            }

            const response = await axios.put(`http://localhost:8080/api/v1/user/${principal.data.data.userId}`, formData, option);

            setErrorMessages({
                profileImg: '',
                email: '',
                name: '',
                phone: '',
                address: ''});

            return response
        }catch (error) {


        }
    }, {
        onSuccess: (response) => {
            alert('회원정보 수정 완료');
            window.location.replace('/');
        }
    })

    const updateUserHandleSubmit = () => {
        if(isName && isPhone) {
            modifyUser.mutate(updateUser);
        }
    }

    const toggleEdit = (field) => {
        setInputDisabled((prevState) => ({
            ...prevState,
            [field]: !prevState[field]
        }));

        setButtonText((prevState) => ({
            ...prevState,
            [field]: prevState[field] === "Edit" ? "Modify" : "Edit"
        }));
    };

    const saveImgFileHandle = (e) => {

        setImgFiles(e.target.files[0]);

        const fileReader = new FileReader();
        fileReader.readAsDataURL(e.target.files[0]);
        fileReader.onload = (e) => {
            setPreview(e.target.result);
        };
    }

    const imgClickHandle = (e) => {

    }

    // const removeFileHandle = (e) => {
    //     const idToRemove = parseInt(e.target.value);
    //     setImgFiles(prevImgFiles => prevImgFiles.filter(imgFile => imgFile.id !== idToRemove));
    // }

    if(authState) {
        if (principal.isLoading) {
            return (<Box sx={{ display: 'flex' }}>
                <CircularProgress />
            </Box>)
        }
        return (
            <Grid component="main" maxWidth="xs" css={signupContainer}>

                <Box css={signupBox}>
                    <Typography component="h1" variant="h5" css={signupText}>
                        Edit Member Information
                    </Typography>

                    <div css={profileImgContainer} onClick={imgClickHandle}>
                        <img css={imgStyle} src={preview ? preview : updateUser.profileImg} alt=""/>
                        <input css={hiddenInput} type="file" multiple={false} accept={".jpg, .png, .jpeg"}  onChange={saveImgFileHandle}/>
                        <label>

                        </label>
                    </div>

                    <Box component="form" css={inputContainer}>
                        <StyleInput

                            id="email"
                            label="이메일"
                            name="email"
                            autoComplete="email"
                            onChange={onChangeHandler}
                            autoFocus
                            value={principal.data.data.email}
                            disabled={true}
                        />

                        <div css={editInputStyle}>
                            <StyleInput

                                id="name"
                                label="이름"
                                name="name"
                                autoComplete="name"
                                onChange={onChangeHandler}
                                value={updateUser.name}
                                disabled={inputDisabled.name}
                            />
                            <button type={"button"} css={editButtonStyle} onClick={() => toggleEdit("name")}>
                                {buttonText.name}
                            </button>
                        </div>
                        <div css={errorMsg}>{errorMessages.name}</div>
                        <div css={editInputStyle}>
                            <StyleInput

                                id="phone"
                                label="연락처"
                                placeholder="010-1234-1234"
                                name="phone"
                                autoComplete="tel"
                                value={updateUser.phone}
                                onChange={onChangeHandler}
                                disabled={inputDisabled.phone}
                            />
                            <button type={"button"} css={editButtonStyle} onClick={() => toggleEdit("phone")}>
                                {buttonText.phone}
                            </button>
                        </div>
                        <div css={errorMsg}>{errorMessages.phone}</div>
                        <div css={editInputStyle}>
                            <Box width={"100%"}>
                                <FormControl css={addressForm}>
                                    <InputLabel id="addressSelectLabel">주소</InputLabel>
                                    <Select

                                        labelId="addressSelectLabel"
                                        id="address"
                                        value={updateUser.address}
                                        label="주소"

                                        onChange={(event) => setUpdateUser({...updateUser, address: event.target.value})}

                                        disabled={inputDisabled.address}
                                    >
                                        {address.map((item) => (
                                            <MenuItem key={item} value={item}>
                                                {item}
                                            </MenuItem>
                                        ))}

                                    </Select>
                                </FormControl>
                            </Box>
                            <button type={"button"} css={addressEditButtonStyle} onClick={() => toggleEdit("address")}>
                                {buttonText.address}
                            </button>
                        </div>

                        <Button css={submitButton}
                                type='button'
                                onClick={updateUserHandleSubmit}
                                fullWidth
                                variant="contained"
                                disabled={!isName || !isPhone}
                                sx={{ mt: 3, mb: 2 }}
                        >
                            Modify Member
                        </Button>

                    </Box>
                </Box>

            </Grid>
        );
    }

};

export default ModifyForm;

다음으로..

비밀번호 수정 및 찾기 등을 나타내는 페이지와 백엔드 기능을 알아보도록 하겠습니다.

profile
백엔드 개발자 준비중

0개의 댓글