안녕하세요
imgur 이미지 URL 업로드 + ReactQuill에서 URL로 이미지 저장하기 본문
목차
1. imgur 회원가입 및 로그인
2. imgur APP 등록하기
3. input으로 이미지파일 선택하기
4. imgur에 이미지 파일 업로드 POST 요청 보내기
++ 어??? 나는 요청이 안되는데요?
5. ReactQuill 에 ref 속성 주입하기
6. ReactQuill에 URL 로 이미지 삽입하기
1. imgur 회원가입 및 로그인
위 링크로 이동하여 회원가입을 하고 로그인도 해줍니다.
2. imgur APP 등록하기
https://api.imgur.com/oauth2/addclient
로그인 하고 위 링크로 이동한 뒤에
이런 식으로 작성해주면 APP을 생성할 수 있습니다.
https://imgur.com/account/settings/apps
APP을 생성한 뒤 위 링크로 접속하면
이렇게 생성한 앱 목록이 있습니다.
Client ID를 API 요청 header에 포함하여 보내야하기 때문에 저장해둡니다.
3. input으로 이미지파일 선택하기
<div className={styles.quillContainer}>
<input type='file' onChange={onFileChange} />
<button onClick={onFileUpload} type='button'>upload</button>
<ReactQuill {...props} />
</div>
저는 이렇게 만들어 보았습니다.
업로드할 파일을 저장할 state와 onFileChange 함수를 만듭니다.
input의 onChange 이벤트를 이용하여 이미지파일을 state에 저장하도록 합니다.
// 이미지파일 state
const [file, setFile] = useState<any>();
// onChange 이벤트에 전달하여 setFile로 업로드할 이미지파일을 state에 저장합니다.
const onFileChange = (e: any) => {
setFile(e.target.files[0]);
};
<input type='file' onChange={onFileChange} />
파일 선택을 하면 e.target.files[0] 에 저장이 되고, 이를 setState로 저장시켜 줄 수 있습니다.
파일이 선택된 모습
4. imgur에 이미지 파일 업로드 POST 요청 보내기
button을 클릭하면 실행할 함수를 만들어 봅시다.
imgur에 이미지등록 API 요청을 보내야 합니다. 아래에 순서대로 설명하겠습니다.
const onFileUpload = async () => {
const formData = new FormData(); // formData 형식으로 body에 전달합니다.
formData.append('image', file); // formDate에 이미지 파일을 추가합니다.
const res = await fetch('https://api.imgur.com/3/image', {
method: 'POST',
headers: {
Authorization: `Client-ID ${process.env.NEXT_PUBLIC_IMGUR_CLIENT_ID}`,
Accept: 'application/json',
},
body: formData,
});
const result = await res.json(); // imgur 업로드 결과 데이터
const { link } = result.data; // 이미지 링크
};
//...
<button onClick={onFileUpload} type='button'>upload</button>
위는 imgur API 레퍼런스입니다. 업로드 이외에도 수정, 삭제 등 다른 기능 확인이 필요하시면 위 링크로 접속.
curl --location 'https://api.imgur.com/3/image' \
--header 'Authorization: Client-ID {{clientId}}' \
--form 'image="R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"'
첫번째,
이미지를 업로드하기 위해서는 binary, base64, image URL 형식으로 보내야 합니다.
여기서는 formData를 이용하여 binary 파일로 요청합니다.
const formData = new FormData(); // formData 형식으로 body에 전달
formData.append('image', file); // formDate에 이미지 파일 추가
두번째,
API 요청 형식에 따라 요청합니다.
--header 'Authorization: Client-ID {{clientId}}' \
header에는 위를 반드시 포함하여야 한답니다.
아까 만들어 둔 Client ID 를 포함시켜줍시다.
const res = await fetch('https://api.imgur.com/3/image', {
method: 'POST',
headers: {
Authorization: `Client-ID ${process.env.NEXT_PUBLIC_IMGUR_CLIENT_ID}`,
Accept: 'application/json',
},
body: formData,
});
body에는 이미지파일이 추가 된 formData를 넣습니다..
{ image: File } 과 같은 형태로 전송이 됩니다.
세번째, 요청 결과를 확인합니다.
{
"data": {
//...
"type": "image/jpeg",
"size": 659939,
"link": "https://i.imgur.com/6R057H2.jpg"
},
"success": true,
"status": 200
}
요청에 성공하면 위와 같은 형식으로 데이터가 반환이 되는데,
속성 중에 있는 link가 바로 선택한 이미지파일의 주소입니다.
const result = await res.json(); // imgur 업로드 결과 데이터
const { link } = result.data; // 이미지 링크
이제 생성된 주소를 <image src="주소" /> 와 같은 형태로 Quill의 본문에 삽입하면 되겠죠.
++ 어??? 나는 요청이 안되는데요?
imgur에서 localhost 에서는 요청을 보내도 403, 429 HTTP/2 protocol error 이런 에러만 반환합니다.
서버를 직접 만들어 운영하고 계시는 분이라면 서버를 통해서 요청을 보내시면 됩니다. (저는 직접 서버 만들 줄 모름)
그렇지 않다면 저처럼 vercel(AWS나 GCP같은 클라우드 서버)에 배포하여 확인해야 합니다.
그런데 애초에 요청이 되는지를 알고 싶다면 POSTMAN으로 확인해봅시다.
워크스페이스를 생성합니다.
collection을 만들고,
> POST 요청 선택,
> 요청 주소 입력,
> Headers 탭에서 key에 Authorization 입력,
> value에 Client-ID 입력
Body 탭 선택,
> image 키 적고
> FIle 선택,
> 밸류에 파일 등록
그리고 Send 보내면 이렇게 요청이 잘 됐을 경우 데이터를 받아볼 수 있습니다.
오른쪽에 코드도 있으니 저 코드를 참고하여 개성있게 코드 짜면 됩니다.
5. ReactQuill 에 ref 속성 주입하기
// dynamic import. ssr: false 옵션으로 클라이언트에서 동적으로 로드하도록 합니다.
// window와 같은 브라우저 API에 의존할 경우 필요합니다.
const ReactQuill = dynamic(
async () => {
const { default: ReactQuillClass } = await import('react-quill');
const RefExtendedQuill = ({ forwardedRef, ...props }) => <ReactQuillClass ref={forwardedRef} {...props} />;
return RefExtendedQuill;
},
{ ssr: false },
);
ReactQuill 컴포넌트에는 ref를 직접 작성할 수 없습니다. ref 속성은 없기 때문입니다.
const quillRef = useRef();
//...
<ReactQuill
ref={quillRef}
/>
하지만 직접 dynamic import한 모듈에 ref를 주입한 상태의 컴포넌트를 반환하도록 할 수 있습니다.
react-quill 모듈을 import하여 출력을 해보면 아래와 같이 { default: 컴포넌트 } 형태를 확인할 수 있습니다.
즉, 컴포넌트를 import한 것이고, 컴포넌트에는 ref 속성을 이용할 수 있습니다.
ref 속성 주입 방법은 다음과 같습니다.
forwardedRef 속성을 이용하여 다음과 같은 방법으로 리액트 컴포넌트에 ref 속성을 주입할 수 있습니다.
// ReactQuillClass 변수에 ReactQuill 컴포넌트 할당
const { default: ReactQuillClass } = await import('react-quill');
// 컴포넌트에 ref 속성 주입
const RefExtendedQuill = ({ forwardedRef, ...props }) => <ReactQuillClass ref={forwardedRef} {...props} />;
return RefExtendedQuill;
{ default: ReactQuillClass }
=> ReactQuillClass에 default 를 할당합니다.
=> RefExtendedQuill 은 <ReactQuillClass /> 태그를 반환하는 컴포넌트의 변수명입니다.
=> forwardedRef 속성을 <ReactQuillClass /> 태그에 ref={forwardedRef} 와 같은 형태로 전달하면
ref 속성을 이용할 수 있게 됩니다.
다른 이름은 아무렇게 지어도 상관 없지만, forwardedRef 는 정해져 있는 속성입니다. 바꾸면 안됩니다.
이제는 useRef()를 이용하여 ReactQuill을 선택할 수 있게 되었습니다.
const quillRef = useRef();
// ...
<ReactQuill
forwardedRef={quillRef}
/>
6. ReactQuill에 URL 로 이미지 삽입하기
const onFileUpload = async () => {
const formData = new FormData(); // formData 형식으로 body에 전달
formData.append('image', file); // formDate에 이미지 파일 추가
const res = await fetch('https://api.imgur.com/3/image', {
method: 'POST',
headers: {
Authorization: `Client-ID ${process.env.NEXT_PUBLIC_IMGUR_CLIENT_ID}`,
Accept: 'application/json',
},
body: formData,
});
const result = await res.json(); // imgur 업로드 결과 데이터
const { link } = result.data; // 이미지 링크
const editor = quillRef.current?.getEditor();
const range = editor?.getSelection();
editor.insertEmbed(range.index, 'image', link);
};
quillRef을 이용하여 quill의 속성을 확인할 수 있게 되었습니다.
문서로 확인하려면 위 링크에 접속하면 됩니다.
커서의 위치를 확인하는 방법은 다음과 같습니다.
직접 접근하는 방법과 editor의 getSelection() 메서드를 이용하는 방법 두가지가 있습니다.
quillRef.current.selection
const editor = quillRef.current?.getEditor();
const range = editor?.getSelection();
태그를 본문에 삽입하는 방법은 다음과 같습니다.
const editor = quillRef.current?.getEditor();
const range = editor?.getSelection();
editor.insertEmbed(range.index, 'image', link);
insertEmbed() 메서드에 세개의 인자를 넣으면 됩니다.
- 이미지를 삽일할 index, (커서 위치)
- type 선택, <image />
- src에 들어갈 이미지 주소 지정
공식 레퍼런스 sample
quill.insertEmbed(10, 'image', 'https://quilljs.com/images/cloud.png');
( n번째 인덱스, 'image' type, 이미지 src )
결과
<p>k</p><p><image src="주소" /></p> 와 같은 형태로 이미지 태그가 삽입된 형태로 저장이 됩니다.
'Next13 블로그 프로젝트' 카테고리의 다른 글
댓글에 답글(대댓글) 구현하기 (0) | 2023.10.30 |
---|---|
ReactQuill + highlight.js(코드 블록)로 게시물의 코드 영역 구분하기 (0) | 2023.10.20 |
eslint error 10가지 원인과 해결 방법 (0) | 2023.10.15 |
티스토리 Loading 컴포넌트 커스텀 훅을 이용하여 구현하는 방법 (0) | 2023.10.11 |
Next.js 프로젝트 Vercel 배포하는 방법 (+마주하는 에러 해결 세가지) (0) | 2023.10.05 |