본문 바로가기
개발일지/트친소 생성기 사이트

[원신 X친소표 사이트 제작기] 2. 이미지를 불러오기/미리보기 구현 #React

by 박기린 2024. 11. 28.

 

 


프로필(바이오) 이미지 표시 기능

어떤 SNS든지 각 계정마다 프사가 존재하고, X역시 마찬가지입니다.

X친소표를 만든다면 프사(바이오)역시 꼭 들어가야 하기에, 이미지를 받아올 수 있는 기능이 필요합니다.

 

 

 

 


Bio 컴포넌트

// src/app/page.js
const [bio, setBio] = useState(null);

우선 Home 컴포넌트(Next.js app router 기준)에, 프사 이미지를 담을 bio state를 생성합니다.

초기값은 null로 지정합니다.

 

 

 

 

그리고 Bio 이미지를 받아오고, 미리보기 이미지를 출력할 컴포넌트를 제작합니다.

// src/components/Bio.js

import { FormText } from "@/styles/Texts";
import { useState, useRef, useEffect } from "react";
import {
  AddPhotoBox,
  AddPhotoInput,
  WatchPhotoBox,
  PhotoImage,
} from "@/styles/BioBox";

const Bio = (props) => {
  const { imageUpload, onUpload, placeholder = "프로필 사진 첨부하기" } = props;
  const [previewUrl, setPreviewUrl] = useState(null);
  const fileRef = useRef(null);
  const handleClick = () => {
    fileRef?.current?.click();
  };

  useEffect(() => {
    if (imageUpload) {
      setPreviewUrl(imageUpload);

      return () => URL.revokeObjectURL(imageUpload);
    }
  }, [imageUpload]);

  return (
    <AddPhotoBox onClick={handleClick}>
      <AddPhotoInput ref={fileRef} type="file" onChange={onUpload} />
      {!imageUpload ? (
        <FormText>{placeholder}</FormText>
      ) : (
        <WatchPhotoBox>
          <PhotoImage src={previewUrl} alt="미리보기 이미지" />
        </WatchPhotoBox>
      )}
    </AddPhotoBox>
  );
};

export default Bio;

 

 

 

props 설정

const { imageUpload, onUpload, placeholder = "프로필 사진 첨부하기" } = props;

부모 컴포넌트로부터 3가지 props를 전달받습니다.

 

imageUpload : 업로드된 이미지 파일이 담긴 State입니다. 아까 생성한 bio State를 받습니다.
onUpload : '파일 업로드' 이벤트를 처리하는 함수입니다.
placeholder : 업로드된 이미지가 없을 때 표시할 텍스트로, 기본값은 "프로필 사진 첨부하기"입니다.

 

 

 

 

state와 ref 설정

const [previewUrl, setPreviewUrl] = useState(null);
const fileRef = useRef(null);

 

previewUrl : 미리보기에 사용할 이미지 파일 경로를 담습니다.
fileRef : <input type="file" /> 요소에 직접 접근하기 위해 useRef를 사용합니다. 이를 통해, 파일 업로드 창을 열 수 있습니다.

 

 

 

handleClick() : 파일 선택창 열기

const handleClick = () => {
  fileRef?.current?.click();
};

사용자가 '프로필 사진 첨부하기' 컴포넌트를 누르면, fileRef가 가리키는 <input> 요소의 click() 메서드를 호출합니다. 이를 통해, 파일 선택창을 엽니다.

 

 

 

 

 

useEffect로 미리보기 이미지 업데이트

useEffect(() => {
  if (imageUpload) {
    setPreviewUrl(imageUpload);

    return () => URL.revokeObjectURL(imageUpload);
  }
}, [imageUpload]);

의존성 배열에 imageUpload를 지정합니다. imageUpload prop이 변경될 때마다(이미지 파일이 새로 업로드 될 때마다) 실행됩니다.

 

previewUrl에 imageUpload의 경로를 저장.
컴포넌트가 언마운트되거나 imageUpload가 바뀔 때, URL.revokeObjectURL을 호출해 메모리에서 URL 객체를 해제합니다. (메모리 누수 방지)

 

 

 

 

JSX 분석

return (
  <AddPhotoBox onClick={handleClick}>
    <AddPhotoInput ref={fileRef} type="file" onChange={onUpload} />
    {!imageUpload ? (
      <FormText>{placeholder}</FormText>
    ) : (
      <WatchPhotoBox>
        <PhotoImage src={previewUrl} alt="미리보기 이미지" />
      </WatchPhotoBox>
    )}
  </AddPhotoBox>
);

 

AddPhotoBox : 업로드 창을 열기 위한 클릭 영역입니다. 클릭 시 handleClick() 함수를 호출해서 파일 업로드 창을 엽니다.
AddPhotoInput: <input type="file" />입니다. 파일 선택 시 부모로부터 전달받은 onUpload 함수가 호출됩니다.
Preview or Placeholder : imageUpload가 없으면 placeholder를 표시, imageUpload가 있으면 업로드된 이미지의 미리보기를 표시합니다.

 

 

AddPhotoBox, AddPhotoInput, WatchPhotoBox, PhotoImage 모두 특별한 기능이 추가된 컴포넌트가 아니라, styled-components로 스타일만 입힌 컴포넌트입니다.

스타일 컴포넌트의 코드 전문을 보고 싶으면, 접은 글을 펼치세요.

더보기
import styled from "styled-components";

const AddPhotoBox = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  height: 250px;
  width: 250px;
  background-color: #d9d9d9;
  border-radius: 180px;
  overflow: hidden;
  margin-bottom: 40px;
`;

const AddPhotoInput = styled.input`
  display: none;
`;

const WatchPhotoBox = styled.div`
  height: 100%;
  width: 100%;
`;

const PhotoImage = styled.img`
  width: 100%;
  height: 100%;
  object-fit: cover;
`;

 

 

 

 

 


작동 구조

'프로필 사진 첨부하기' 영역을 터치하면

 

 

 

 

 

 

이미지를 업로드할 수 있는 파일 선택기가 켜집니다.

이미지 파일을 선택하면,

 

 

 

이미지가 웹앱에 업로드되고, 미리보기 이미지가 출력됩니다.

 

 

 

 

업로드를 하면, X친소표의 프사 구역에 이미지가 적용됩니다.

 

 

 

 

 

반응형