[React] 이미지 업로드 모달창 구현

susu·2022년 8월 19일
2

미리보기

구현 목적

내 프로젝트에서 이미지 업로드 창이 필요한 이유는 프로필 사진 설정을 위해서다.
따라서 이미지를 업로드함과 동시에 백엔드 서버로 보내주어야 한다.
(여기선 프론트가 해야 할 일만 적어보겠음)

구현 과정

해야 할 작업의 큰 틀을 잡아봤다.

  1. 사용자로부터 파일을 업로드 받아서
  2. 업로드 된 파일을 백으로 보내주기
  3. 동시에 업로드 된 파일을 미리보기로 보여주기

이때 미리보기하는 부분에서는 아무리 가로로 긴 사진이든 세로로 긴 사진이든
정해진 틀 안에 꽉 차게 나오도록 구현해야 한다.

⚡️ 파일 업로드 구현

input 태그

<input ref={setProfileImage}
       className={styles.imgInput}
       type={'file'}
       id={"profile"}
       accept={'image/*'}
       name={'file'}
       onChange={imgHandler} />
  • input 태그에 type={'file'] 속성을 지정해 파일을 입력받는다.
  • accept={'image/*} 속성으로 업로드 유형을 이미지 파일로 제한한다.
  • 기본 업로드 포맷은 못생겼기 때문에 새로 버튼을 달아줄거다.
    useRef() 메소드를 사용하기 위해 ref 속성을 지정해 input 태그를 빼내올거다.
  • 물론 파일을 업로드하는 본체는 이 input 태그이므로 CSS로 visibility: hidden / display: none 속성을 달아주어 감춰주자.

    visibility 만 hidden 으로 빼면 눈에는 보이지만 여전히 공간을 차지한다.
    display 까지 none으로 설정해서 완전히 감춰주자.

📌 useState

업로드 된 이미지의 정보를 동적으로 감시해야 하므로 useState 훅을 사용했다.
나는 이미지의 경로(src)를 state로 관리했다.

const [profile, setProfile] = useState(기본으로 띄울 이미지 경로);

setProfile을 onChange Handler인 imgHandler에 달아줄 것이다.

imgHandler

const imgHandler = async (e:any) => {
    setProfile(URL.createObjectURL(e.target.files[0]));
    const formData = new FormData();
    formData.append('file', e.target.files[0]);
    const response = await server.post(엔드포인트, formData);
    ...
    }
  • setProfile 로 업로드한 이미지 파일의 경로를 profile state에 업데이트해주고
  • 서버와 이미지 파일을 가지고 통신하려면 FormData 객체를 사용해야 한다.

    FormData 객체는 key-value 쌍으로 이루어져 있고,
    append() 메소드로 키값을 추가해줄 수 있다.
    자세한 내용은 문서 참고.

  • 요청에 대한 response에 FormData를 담아 서버와 합의한 엔드포인트로 보내주면 된다.
    참고로 server 는 미리 생성해둔 axios 객체를 불러온 것이다.

📌 useRef

대충 자바스크립트에서의 getElementByID 처럼
리액트에서 특정 DOM에 접근할 수 있게 해주는 셀렉터다.

const setProfileImage = useRef(null);
const imgBtnClick = e => {
    e.preventDefault();
    setProfileImage.current.click();
}

imgBtnClick 함수는 새로 만든 버튼에 달아줄 온클릭 핸들러다.

아까 inputref 속성에 setProfileImage 를 달아줬었다.
input 태그의 역할을 setProfileImage 라는 변수에게 넘겨주고,
새로 만든 버튼이 setProfileImage 를 수행하도록 한 것이라고 생각하면 될 듯.

새로 만든 버튼에 달아주기

<button onClick={imgBtnClick}>사진 업로드</button>

이러면 업로드 기능 자체는 구현이 됐다.

⚡️ 미리보기 구현

앞에서 말했듯 미리보기에 보이는 사진은 정해진 틀 안에 꽉 차야 한다.
CSS 요소를 잘 써야 하는 것이 관건.

시도해본 방법은 세 개인데,

1. clip 속성 지정하기

어느 지점부터 잘라낼 것인지를 정확한 수치로 정해줘야 한다. 불가능.

2. background / background-size 지정하기

구글링하면 제일 먼저 나오는 방법인데,
세로로 긴 사진은 되는데 가로로 긴 사진은 안 됐다.

3. div 태그에 img 채우기 👍

3번으로 해결했다.
img 태그를 div 태그로 감싸주면서 각각에 CSS를 적용해주었다.
div 태그를 틀로 놓고 overflowobject-fit 속성으로 이미지를 채워준다고 생각하면 될 듯!

JSX

<div className={styles.imageBox}>
    <img alt={"미리보기"}
         src={profile}
         className={styles.imageThumbnail}/>
</div>

CSS

.imageBox {
    display: inline-flex;
    width: 15rem;
    height: 15rem;
    overflow: hidden;
    margin: 0 auto;
    border-radius: 50%;
}

.imageThumbnail {
    width:100%;
    height: 100%;
    object-fit: cover;
}

*

  1. 노션에 적어가면서 하다가 공개된 곳(?)에 기록하고 싶어서 시작했다.
    남에게 설명한다고 생각하니까 정리가 잘 되는 것 같다.
  2. 서버의 산물(?)을 가시화하기 위해 프론트가 필요해서 이것저것 하다 보니 스프링보다 리액트가 더 익숙해지고 있다.
  3. 기본으로 띄우는 이미지도 직접 포토샵으로 만들었다. 엠앤엠즈같고 귀여움 ㅎㅎㅎ
profile
블로그 이사했습니다 ✈ https://jennairlines.tistory.com

0개의 댓글