이번 프로젝트에서 프로필 관리의 기능 중에 프로필 이미지를 입력하면 해당 이미지가 어떻게 적용되는지 미리보는 기능이 필요했다. 해당 기능은 글 가장 하단의 블로그 내용을 참고하여 구현하였다.
일단 이미지 파일을 받기 위해서 input 태그의 type을 file로 하여 이미지를 받는다.
이때, label도 같이 작성해주는데 input 태그의 기본 스타일을 안보이게 하고 내가 원하는 스타일의 버튼을 label에 적용해주기 위해서이다. 그렇게에 label의 htmlFor의 값과 input의 id 값을 동일하게 작성해줘야 한다. 그래야 label을 클릭했을 때, input 태그를 누르는 것과 동일한 액션을 취한다.
<label htmlFor="mypage-profile-img">
프로필 수정
</label>
<input
type="file"
accept="image/*"
id="mypage-profile-img"
input 태그의 속성으로 accept을 작성해준다. accept 속성은 input 태그의 type이 file인 경우에만 작성이 가능하며, 해당 속성은 파일의 타입을 명시한다.
위의 코드에서 작성한 accept의 값인 image/*
는 모든 타입의 이미지 파일의 업로드가 허용됨을 뜻한다.
스타일 수정
input 태그를 보이지 않게 하고, label에 원하는 스타일을 적용시킨다.
input 태그에 display:none;
를 작성하여 보이지 않게 하였다.
-
프로필 관리 (이미지 업로드 전)

미리보기 기능 구현
saveImgFile
함수는 FileReader API를 사용하여 이미지를 업로드하는 input 태그의 onChange 이벤트로 넣을 함수이다.
이때, readAsDataURL
은 파일을 URL로 만들어준다(FileReader API).
FileReader API?
웹 API로, 비동기적으로 데이터를 읽기 위하여 File(읽을 파일을 가리킴) 혹은 Blob 객체를 이용해 파일의 내용을 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 함
즉, 클라이언트단에서 file이랑 blob 사용할 수 있게 해주는 것
onChange?
input 태그의 포커스에서 벗어났을 때 (즉, 입력이 종료했을 때) 발생하는 이벤트
-
saveImgFile
imgRef에서 가장 최근의 파일을 불러와 file에 저장
reader은 FileReader 객체 생성
reader에 readAsDataURL 함수를 통해 file을 URL로 반환(즉, 해당 이미지 파일의 링크가 생성됨)
읽기 성공, 실패 여부 상관없이 동작이 끝났을때 마다(reader.onloadend), 파일의 내용을 반환(reader.result)
import { useState, useRef } from 'react';
import defaultImg from 'default 이미지 경로';
const [imgFile, setImgFile] = useState('');
const imgRef = useRef();
...
// 이미지 업로드 input 태그에 넣을 onChange
const saveImgFile = () => {
const file = imgRef.current.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgFile(reader.result);
};
};
...
// 이미지 미리보기
<img
src={imgFile ? imgFile : defaultImg}
alt="프로필 이미지"
/>
...
// 이미지 업로드
<label htmlFor="mypage-profile-img">
프로필 수정
</label>
<input
type="file"
accept="image/*"
id="mypage-profile-img"
onChange="{saveImgFile}"
ref="{imgRef}"
/>
이미지 미리보기
img 태그의 src 속성 값으로 삼항연산자를 사용했다. imgFile의 값이 true일 경우 imgFile
을 src 속성의 값으로 한다. 그러나 false일 경우에는 defaultImg
를 src 속성의 값으로 한다.
이 말인 즉, 이미지를 업로드 하면 해당 이미지를 보여주고 이미지를 업로드 하지 않았다면 기본 이미지(defaultImg)를 보여준다는 뜻이다.
이미지 업로드
input 태그에 위의 코드에 새로운 속성을 더 추가를 해준다. onChange 속성을 추가하여 input 태그의 입력이 종료했을 경우 saveImgFile
함수를 실행한다. ref 속성의 값은 imgRef
를 작성한다.
-
이미지 업로드

-
이미지 미리보기 구현 완료

마치며
다른 분의 코드를 참고하여 구현하긴 했지만, 정상적으로 기능이 작동하도록 만들어서 뿌듯했다.
구현을 완료하고 이미지가 이쁘게 잘 들어가는 모습을 보니 매우 기쁘달까..! 이렇게 새로운 것을 알게되어서 재밌다.
개발 공부는 힘들고 어렵지만 완벽히 습득을 하고 나면 정말 무궁무진하게 활용할 수 있어서 질리지 않고 재밌는 것 같다.
참고
React : 이미지 업로드하고 미리보기
FileReader로 브라우저에서 file 다루기
파일API : FileReader() 객체
이번에도 어김없이 프로젝트를 진행하며 pull을 하려던 중, 다음과 같은 에러(?)가 발생했다…
저번에 작성했던 글의 방식대로 시도해봤으나 실패..^^..
그래서 열심히 서칭한 끝에 찾아낸 방법은 다음과 같다.
git reset –hard [원격저장소]
pull을 하려는 브랜치로 이동한 뒤, 다음과 같이 명령어를 작성한다.
git fetch --all
git reset --hard [원격저장소]
# git reset --hard origin/main
해당 명령어는, 모든 원격 저장소를 fetch한 후, –hard 옵션을 주어 원격저장소의 내용으로 reset 시키는 것이다.
즉, main을 origin/main의 내용으로 강제 리셋시키는 것이다.

해당 방법을 사용하면 웬만한 pull 에러는 다 해결할 수 있다. 지난번의 에러도 해당 방법으로 해결이 가능하다.
단, 문제점은 로컬 저장소의 내용이 날아갈 수 있다..^^.. (갈 수 있는게 아니라 날아간다.)
나의 main 브랜치의 경우에는 로컬 main에 작성하는 내용이 없어서 원격 저장소의 main 내용을 reset을 통해 로컬 저장소로 가져와도 됐다. (main 브랜치는 각자 개발한 내용을 모으는 브랜치로, 따로 로컬에서 작성하는 내용이 없음)
그러니 해당 방법을 사용할 경우, 로컬의 내용이 날아가도 되거나… 어딘가에 따로 저장을 해두고 하자.. (아님 다른 방법을 찾자..)
참고
해결방법 참고 블로그
Git도구(head,index,워킹디렉토리)와 reset
프로젝트를 진행하던 중, main 브랜치의 내용을 soha 브랜치로 가져오려고 pull을 하던 중 다음과 같은 에러가 발생했다.
fatal: Not possible to fast-forward, aborting
뜻은 ff-only를 사용 불가능해서 중단됐다.. 뭐 이런 소리 같다.

해결 방법을 엄청 서칭한 결과 rebase를 해서 confilct를 해결하고 브랜치로 다시 push를 하면 된다~~ 라고 해서 열심히 해봤지만..? 안됐다.. 그래서 stack overflow의 다른 해결 방법을 통해 해결했다!
git pull --no-ff
해당 명령어를 입력해주고 다시 add와 commit을 하니 정상적으로 잘 pull 되었다..ㅜ

매번 느끼는거지만 git은 참.. 어렵다.. 알다가도 모르게 만들어.. 열심히.. 공부해야겠다..
참고
해결방법: stack overflow
그림으로 이해하는 merge –no-ff, squash, rebase 그리고 pull request(PR)
내가 styled-reset을 알기 전까지는 cdn 링크나 css 코드로 직접 속성을 초기화했었다. 하지만 이번에 react를 공부하면서 간단하게 할 수 있는 방법이 없을까? 찾던 중에 styled-reset
을 알게되었다.
해당 방법을 통해 쓸모없는 코드 길이를 대폭 줄일 수 있었다.
styled-rest
-
설치
npm i styled-reset
명령어를 작성하여 styled-reset을 설치
-
적용
App.js 파일에 다음과 같이 작성하여 사용한다.
import { Reset } from 'styled-reset';
const App = () => (
<React.Fragment>
<Reset />
<div>Hello, Soha!</div>
</React.Fragment>
);
import를 통해 Reset을 불러오고, 작성할 내용들의 상단 (즉, 루트 태그 바로 아래)에 Reset을 불러오면 된다.
styled-reset 공식 링크
react-hook-form 패키지에서 useForm()
훅을 불러와, 컴포넌트 함수 안에서 훅을 호출한다.

해당 결과로 register()
과 handleSubmit()
를 얻을 수 있다. (즉, 사용할 함수 적으라는 것)
register()
로 입력란을 등록, handleSubmit()
으로 submit 이벤트를 처리한다.

입력한 내용을 alert로 브라우저에 띄운다. (잘 받았는지 확인하기 위해서)
중복 제출 방지
form에서 이벤트 처리가 종료되기 전에 다시 버튼을 클릭할 경우 양식이 중복 제출되는 문제를 막기 위한 코드이다.
작성한 코드는 처음 제출 버튼을 클릭했을 때, 버튼을 비활성화 시킨다. 이후 이벤트가 종료되면 제출 버튼을 다시 활성화시키는 방식으로 중복 제출을 방지한다.

formState
속성은 양식이 현재 어떤 상태인지를 담는다. formState
에 isSubmitting
속성을 읽어서 양식이 제출중인 상태인지 아닌지를 알아낸다.
제출 버튼의 disabled
속성에 isSubmitting
값을 설정하여, 회원가입 버튼의 양식 제출이 끝날때 까지 비활성화시킨다.
유효성 검사
이메일, 비밀번호와 같은 입력란은 필수 입력으로 하고, 이메일과 핸드폰번호는 유효한 형식이 정해져있기 때문에 해당 형식을 맞춰야 제출이 가능하도록 한다.
register()
의 두번째 인자로 옵션을 넘겨주면 유효성 검증이 가능하다.
검증 타입은 required
, pattern
, minLength
등이 있다.
message
에는 유효하지 않은 형식일 경우 출력되는 오류 메세지를 담는다.
입력한 값이 유효하지 않을 경우 form이 제출되지 않고, formState 속성의 errors 객체에 오류 내용이 저장된다. 이때, error.phone&&<Warning>~
코드로 저장된 에러 메세지를 출력하도록 했다.
aria-invalid
는 스크린리더 사용자를 위한 것으로 일단은…? 굳이 작성하지 않아도 된다.
MUI
공식문서를 참고하여 TextField와 Button 컴포넌트를 불러와 사용했다.


참고자료
React Hook Form 라이브러리 사용법
종합소감
react-hook-form을 사용하여 form을 만들어 봤다. 이전에는 html로만 작업했다면 이번에는 해당 라이브러리 사용하여 작업했더니 좀 더 상세한 기능을 쉽게 작업할 수 있어서 좋았다.
그리고 MUI를 사용하여 디자인을 작업했는데, 디자인을 어떻게 할 지 크게 신경을 쓰지 않아서 좋았다. 다음번에 프로젝트 할 때에도 유용하게 사용이 가능 할 듯 싶다.
UMC, Web 스터디를 마치며
해당 기록들은 UMC 동아리 활동 중 Web 스터디의 과제이다.
동아리가 신설되고 입부하면서 Web 스터디장을 맡게 되었다. 내가 적극적으로 스터디장을 하고 싶다는 어필을 통해 하게 되었다. 처음에는 원대한 꿈을 가지고.. 파트장을 했으나, 이후에 갈 수록 생각한 것 처럼 못한게 끝이 나니 아쉽게 느껴진다.
스터디를 진행하면서 본격적으로 React를 사용해보았다. 이전에는 강의를 보면서 따라만 했다면, 이번에는 직접 어떻게, 어디에 사용해야 하는지 고민을 하며 공부하고 적용하였다. 정말 쉽지 않았고 때려치고 싶었던 적이 한 두번이 아니지만, 파트장이라는 위치와 앞으로의 프로젝트들을 위해 대충하지 않고 정말 열심히 진행했다.
스터디를 진행하면서 새롭게 알게 된 지식도 있었고, 이름만 알았던 개념들의 상세 내용도 알 수 있었다. 단순히 글을 읽는 것이 아닌 직접 배우고 활용하면서 몸소 깨달을 수 있었다.
또한, 스터디를 통해 다른 사람에게 나의 지식을 공유하고, 질문하고, 같이 문제를 해결해 나가면서 역시 혼자 공부할 때보다 누군가 나의 문제를 듣고 같이 고심해줄 사람이 있다는 것에서 공부에 대한 불안감이 줄어들고 안정감을 얻을 수 있었다. 물론, 모두 해결할 수는 없었지만 같이 할 사람이 있다는 것에서 오는 안정감이 나를 편안하게 해주었다.
매주 과제를 하면서 힘들기도 많이 힘들었고, 짜증도 났지만 지금 그 시간들로 인해 내가 더 성장했다는 것은 부정할 수 없는 사실이다. 그리고 여름방학동안 진행해야 할 프로젝트 2개 모두, 이번 스터디의 과제들을 수행하면서 배운 지식들을 적극 활용하게 될 것이다. 대략 2달동안 매주 과제를 수행하느라 힘들기도 했지만..! 이로 인해 배운점도, 많았기 후회없는 정말 참여하기 잘 했다는 생각이 들었다.