내 프로젝트에서 이미지 업로드 창이 필요한 이유는 프로필 사진 설정을 위해서다.
따라서 이미지를 업로드함과 동시에 백엔드 서버로 보내주어야 한다.
(여기선 프론트가 해야 할 일만 적어보겠음)
해야 할 작업의 큰 틀을 잡아봤다.
이때 미리보기하는 부분에서는 아무리 가로로 긴 사진이든 세로로 긴 사진이든
정해진 틀 안에 꽉 차게 나오도록 구현해야 한다.
<input ref={setProfileImage}
className={styles.imgInput}
type={'file'}
id={"profile"}
accept={'image/*'}
name={'file'}
onChange={imgHandler} />
input
태그에 type={'file']
속성을 지정해 파일을 입력받는다.accept={'image/*}
속성으로 업로드 유형을 이미지 파일로 제한한다.useRef()
메소드를 사용하기 위해 ref
속성을 지정해 input 태그를 빼내올거다.visibility: hidden
/ display: none
속성을 달아주어 감춰주자.visibility 만 hidden 으로 빼면 눈에는 보이지만 여전히 공간을 차지한다.
display 까지 none으로 설정해서 완전히 감춰주자.
업로드 된 이미지의 정보를 동적으로 감시해야 하므로 useState
훅을 사용했다.
나는 이미지의 경로(src)를 state로 관리했다.
const [profile, setProfile] = useState(기본으로 띄울 이미지 경로);
setProfile을 onChange Handler인 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()
메소드로 키값을 추가해줄 수 있다.
자세한 내용은 문서 참고.
server
는 미리 생성해둔 axios 객체를 불러온 것이다.대충 자바스크립트에서의 getElementByID
처럼
리액트에서 특정 DOM에 접근할 수 있게 해주는 셀렉터다.
const setProfileImage = useRef(null);
const imgBtnClick = e => {
e.preventDefault();
setProfileImage.current.click();
}
imgBtnClick
함수는 새로 만든 버튼에 달아줄 온클릭 핸들러다.
아까 input
의 ref
속성에 setProfileImage
를 달아줬었다.
input 태그의 역할을 setProfileImage 라는 변수에게 넘겨주고,
새로 만든 버튼이 setProfileImage 를 수행하도록 한 것이라고 생각하면 될 듯.
<button onClick={imgBtnClick}>사진 업로드</button>
이러면 업로드 기능 자체는 구현이 됐다.
앞에서 말했듯 미리보기에 보이는 사진은 정해진 틀 안에 꽉 차야 한다.
CSS 요소를 잘 써야 하는 것이 관건.
시도해본 방법은 세 개인데,
어느 지점부터 잘라낼 것인지를 정확한 수치로 정해줘야 한다. 불가능.
구글링하면 제일 먼저 나오는 방법인데,
세로로 긴 사진은 되는데 가로로 긴 사진은 안 됐다.
3번으로 해결했다.
img
태그를 div
태그로 감싸주면서 각각에 CSS를 적용해주었다.
div 태그를 틀로 놓고 overflow
와 object-fit
속성으로 이미지를 채워준다고 생각하면 될 듯!
<div className={styles.imageBox}>
<img alt={"미리보기"}
src={profile}
className={styles.imageThumbnail}/>
</div>
.imageBox {
display: inline-flex;
width: 15rem;
height: 15rem;
overflow: hidden;
margin: 0 auto;
border-radius: 50%;
}
.imageThumbnail {
width:100%;
height: 100%;
object-fit: cover;
}