React로 캠핑 웹 만들기3 - 마이페이지

이지영·2025년 1월 29일

📝 구현 목표

  • 기본 프로필 이미지 설정
  • 프로필 이미지 있을 경우 해당 이미지 표시
  • 편집 UI 토글 기능
  • 닉네임 수정 기능
  • 프로필 이미지 수정 및 미리보기 기능

💻 코드 구현

// 기본 폼 구조
<form onSubmit={handleSubmit}>
  <input type="file" onChange={handleFileChange} />
  <input
    type="text"
    value={newName}
    onChange={(e) => setNewName(e.target.value)}/>
  <button type="button" onClick={handleCancel}>
  <button type="submit">수정하기</button>
  </div>
</form>
// 이미지 파일 선택 시 이벤트
const handleFileChange = (e) => {
  const file = e.target.files[0];
  if (file) {
    setAvatarFile(file);
    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
    }
    setPreviewUrl(URL.createObjectURL(file));
  }
};

// 수정하기 버튼 클릭 시 이벤트
const handleSubmit = async (e) => {
  e.preventDefault(); 

    try {
      let avatarUrl = user.user_metadata?.avatar_url;
      if (avatarFile) {
        const fileName = `${user.id}-${Date.now()}`;
        const { data, error } = await supabase.storage
          .from("avatars")
          .upload(fileName, avatarFile);

        if (error) throw error;

        const {
          data: { publicUrl },
        } = supabase.storage.from("avatars").getPublicUrl(fileName);

        avatarUrl = publicUrl;
        console.log("publicUrl", publicUrl);
      }

      // user테이블 업데이트
      const { error } = await supabase
        .from("users")
        .update({
          name: newName,
          avatar_url: avatarUrl,
        })
        .eq("id", user.id);

      if (error) throw error;
      alert("프로필이 업데이트 되었습니다.");
      setIsEditing(false);
      setUser({
        ...user,
        name: newName,
        avatar_url: avatarFile ? avatarUrl : user.avatar_url,
      });
    } catch (error) {
      console.error(error);
      alert("업데이트 실패");
    }
  };

💡 알아두면 좋은 점

e.preventDefault() 사용 이유

form의 기본 동작은 제출 시 페이지를 새로고침하면서 서버로 데이터를 전송한다.
SPA에서는 안에있는 코드가 실행되기 전에 페이지가 새로고침된다.
해서 e.preventDefault()를 해서 새로고침을 막아줘야한다.

🚨 트러블슈팅

트러블1. 파일 객체 접근

[문제]

처음에 file을 e.target.files 형태로 작성했다.
하지만 file을 출력해보면 객체이다.

File {
	name: "profile.jpg",
    lastModified: ..//
    lastModifiedDate: ...
    ...//
}

위에 name, lastModified... 등이 들어있는 객체이고 내가 필요한 것은 files[0]번째이기 때문에 files[0]을 해야한다.

트러블2. 이미지 미리보기

[문제]

프로필 이미지 및 닉네임 수정 버튼을 클릭하면
잘 수정되어 supabase의 users테이블에 잘 적용이 되지만
수정 당시 파일 선택 완료 후 아무런 이미지의 변화가 없어서
잘 적용이 되었는지 확인이 어렵다.

[해결]

supabase의 스토리지에서 .getPublicUrl(fileName)을 사용해서
previewUrl을 사용해서 미리보기 이미지를 생성해서 보여줬더니 잘 확인이 되었다.

URL.createObjectURL의 동작 방식

브라우저 메모리에 있는 파일에 대한 임시 URL을 생성한다.
생성된 URL의 형태는 보통 blob:http://도메인/고유ID형식이다

특징:

  • 브라우저 메모리에만 존재하는 임시 URL이다.
  • 페이지가 닫히면 자동으로 해제된다.
  • 다른 탭이나 브라우저에서는 접근할 수 없다.
  • 실제 파일 데이터는 브라우저 메모리에 저장된다.

주의:

  • 사용이 끝난 URL을 해제해줘야한다.
  • 해제하지 않으면 메모리 누수가 발생할 수 있다.
  • 새 파일이 선택될 때마다 이전 URL을 해제하고 새로운 URL을 생성해야한다.
  • URL.revokeObjectURL()로 해제 하면된다.

다음은 캠핑 데이터를 페칭 해와서 OPEN API캠핑 데이터를 메인에 뿌려보는걸 해보려고한다.

0개의 댓글