-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Jamie] TextEditor 컴포넌트 생성 #56
Conversation
✅ Deploy Preview for co-studo ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쩨이미 너무너무 고생 많으셨어요!!!
쨈의 에디터 적용기를 들으면서 외부 라이브러리를 가져와 사용할 때 어떤 고난들을 겪게 되는지...간접 체험했습니다ㅎ....
ReactQuill과 관련한 코드는 해당 라이브러리 사용법에 대해 추가적인 학습을 해야 100% 이해가 갈 것 같아 작성해주신 PR 글을 참고하여 전체적인 흐름만 파악했습니다!
전반적으로 깔끔하게 짜주셔서 흐름을 파악하는데는 큰 무리가 없었어요!
LGTM 이네요! ^_^)b
나중에 시간이 될 때 ReactQuill 에 대해서 저도 찾아보겠습니다ㅎㅎ
코드 잘 봤습니다!
고생하셨어요!!!!
const modules = useMemo( | ||
() => ({ | ||
toolbar: { | ||
container: toolbarOptions, | ||
handlers: { | ||
image: handleImage, | ||
}, | ||
}, | ||
imageResize: { | ||
parchment: Quill.import('parchment'), | ||
modules: ['Resize', 'DisplaySize', 'Toolbar'], | ||
}, | ||
}), | ||
[], | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 여기에 useMemo 를 쓰신 이유를 여쭤봐도 될까요?
순수한 질문입니다!! ^///^)>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
스택오버플로우 글에서는 모듈을 직접 건네주면 키보드를 누를 때마다 모든 모듈을 매번 렌더링되게 만들어서 메모이제이션을 하라고 되어있더라구요! 저같은 경우는 좀 다른 상황이긴 했는데 addRange(): The given range isn't in document
라는 동일한 경고가 떠서 이 부분을 해결하기 위해서 사용했었습니당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 커스텀 사용 어렵다!
고생하셨습니다 제이미 💯
post: <T>(url: string, payload?: object, config?: AxiosRequestConfig) => | ||
axios.post<ServerResponse<T>>(url, payload, config).then((res) => res.data), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/apis/image.ts
Outdated
export const getImageUrl = (image: File) => { | ||
const formData = new FormData(); | ||
formData.append('image', image); | ||
|
||
return http.post<ImageEntity>(`__API_END_POINT__/image`, formData, { | ||
withCredentials: true, | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
결론적으로는 image url 을 얻지만 행위 자체는 이미지를 업로드 하여 그 이미지의 주소를 얻는 것 이므로 getImageUrl 보다는 uploadImage 등의 이름이 어울리지 않을까 생각했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 네이밍이 좀 걸렸었는데 말씀해주신 이름이 더 나은 것 같네요! 그리고 파크께서 get 함수 작성하신 걸 보면 모두 fetchXX라고 작성하셨던데 하나의 컨벤션인 걸로 보면 될까요? post의 경우 아직 사용된 곳이 없어서 임의로 짓긴 했었습니다,,!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전부 GET 요청이어서 가져온다는 의미로 fetch~ 로 짓긴 했었는데, POST method 의 경우 적절한 네이밍으로 지어도 상관없지 않을까요?
GET 같은 경우는 fetch 로 통일해서 사용하는게 좋아보이고, POST 는 적절한 이름 (apply, upload, create...) 로 쓰여도 상관없지 않을까 싶습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 저도 동의합니당 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
노션에 적어놨숩니당
@@ -0,0 +1,165 @@ | |||
import 'react-quill/dist/quill.snow.css'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
css import 위치가 여기에 있음으로 응집도 면에서 이해가 될 수도 있긴 한데,
TextEditor 를 불러와서 사용하는 곳마다 + 한 곳에서 여러번 사용된다면 여러번 호출될까요? (궁금)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
width: '800px', | ||
editAreaHeight: '300px', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추후 rem 기반으로 수정될 필요가 있어보여욥
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제 생각엔 default 값이니 반응형 대비해서 width는 %, height는 rem으로 주는게 어떨까 싶어여!
width도 rem으로 주면 많이 확대 시 가로 스크롤이 생길 수 있을거 같네여
아니면 max-width: 100%를 주던지?
width: '100%',
// html이 10px이니 30rem
editAreaHeight: '30rem'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그리고 에디터영역높이라는 의미로 editAreaHeight인건가요..?? 높이 option 같은데 동사가 앞에 있으니 set 하는 함수라고 생각했어요 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jindonyy react-quill 문서에서 editing area 라는 이름을 사용하고 있어서 가져와 썼던 건데 edit area라고 해서 동사처럼 보였을 수도 있겠네요..! 수정하도록 하겠습니당 감사해요 👍
const contentRef = useRef(''); | ||
|
||
useImperativeHandle(ref, () => ({ | ||
getContents: () => contentRef.current || '', | ||
})); | ||
|
||
const handleEditorChange = (content, delta, source, editor) => { | ||
contentRef.current = editor.getHTML(); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contentRef 는 여기서 current 값을 바꿔주는데, 결론적으로 어디서 쓰이는지가 안보이는 것 같네요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contentRef는 에디터에서 HTML 형식의 컨텐츠를 가져오는 용도로 사용하고 있습니다!
그냥 단순 텍스트만 가져온다고 하면 ReactQuill에 넘겨주고 있는 editorRef를 사용해 텍스트를 가져오면 되는데, 컨텐츠를 HTML 형식으로 가져오려면 getHTML() 함수를 사용해야 하는데 onChange의 props인 editor에서만 사용할 수가 있더라구요. 그래서 본문에 작성했던 것처럼 에디터가 새로 렌더링되면 사라지는 문제를 해결하기 위해서, 그리고 상태를 사용해 값을 실시간으로 가져올 필요가 없다고 생각해서 contentRef에 onChange 함수에서 가져온 값을 저장하도록 했고 useImperativeHandle에 getContents 함수를 만들어서 ref를 건네주고 있는 상위 컴포넌트가 contentRef 값을 가져갈 수 있도록 작성했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아하 useImperativeHandle hook 에 대해서 잘 인지하지 못하고 있어서 제대로 이해하지 못한 거였군영
설명 감사합니다~!
const handleImage = () => { | ||
const input = document.createElement('input'); | ||
input.setAttribute('type', 'file'); | ||
input.setAttribute('accept', 'image/*'); | ||
|
||
input.click(); | ||
|
||
input.addEventListener('change', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
event target 이런 것은 사용 불가능하고 createElement 를 해야 하나 보네요??!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
질문) 혹시 이전에 사용하려던 toast 에디터는 이런 기능이 구현되있던건가여??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jindonyy toast-ui 에디터도 이미지 URL을 새로 받아와서 넣어주려면 이미지 핸들러를 커스텀해서 넣어줘야 했어요! 근데 이제 훅을 등록할 때 들어오는 blob이란 값에 이미지 파일이 담겨있는데, 그걸 가지고 URL을 요청해서 콜백에 넘겨주면 되는 좀 더 간단한 구조였어요
useEffect(() => {
if (editorRef.current) {
editorRef.current.getInstance().removeHook('addImageBlobHook');
editorRef.current
.getInstance()
.addHook('addImageBlobHook', (blob, callback) => {
(async () => {
const {
results: { imageUrl },
} = await getImageUrl(blob);
callback(imageUrl, blob.name);
})();
return false;
});
}
}, []);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에디터 어렵다 어려워.. 보면서 저도 ref 공부했네요^_ㅠ
라이브러리인데 생각보다 구현해야할게 많네요ㅠㅠ 이전 라이브러리 버전 이슈도 그렇고 고생했어요 제이미! 👍
|
||
export const getImageUrl = (image: File) => { | ||
const formData = new FormData(); | ||
formData.append('image', image); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormData..ㅇ0ㅇ)!
src/apis/image.ts
Outdated
export const getImageUrl = (image: File) => { | ||
const formData = new FormData(); | ||
formData.append('image', image); | ||
|
||
return http.post<ImageEntity>(`__API_END_POINT__/image`, formData, { | ||
withCredentials: true, | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
노션에 적어놨숩니당
width: '800px', | ||
editAreaHeight: '300px', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제 생각엔 default 값이니 반응형 대비해서 width는 %, height는 rem으로 주는게 어떨까 싶어여!
width도 rem으로 주면 많이 확대 시 가로 스크롤이 생길 수 있을거 같네여
아니면 max-width: 100%를 주던지?
width: '100%',
// html이 10px이니 30rem
editAreaHeight: '30rem'
width: '800px', | ||
editAreaHeight: '300px', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그리고 에디터영역높이라는 의미로 editAreaHeight인건가요..?? 높이 option 같은데 동사가 앞에 있으니 set 하는 함수라고 생각했어요 🤔
const handleImage = () => { | ||
const input = document.createElement('input'); | ||
input.setAttribute('type', 'file'); | ||
input.setAttribute('accept', 'image/*'); | ||
|
||
input.click(); | ||
|
||
input.addEventListener('change', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
질문) 혹시 이전에 사용하려던 toast 에디터는 이런 기능이 구현되있던건가여??
} | ||
} | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 여기 eventListener 안에 함수 좀 쪼갤수 있을까여..?? editor 학습이 안되더라도 뭔가 로직은 좀 보였으면 하는데 지금 안에 함수 이름들의 기능을 검색해보면서 봐야 추론이 되는거 같아요. 에디터 기능을 몰라도 함수명으로 로직이 보이면 유지보수할 때 좋을거 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
말씀해주신 걸 보고 함수로 나눌 수 있을지 좀 생각해봤는데 함수 하나당 하나의 동작을 하는,,? 그런 식이라 분리하는 게 크게 의미가 없을 것 같더라구요. 길어봤자 두줄 정도,,? 어떤 동작을 하는 건지 주석을 추가하면 좀 나을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
분리하기 좀 애매하긴 하네여..ㅎㅎ 라이브러리를 활용한거니 주석을 사용해도 좋은거 같아요! 👍
- Renaming function and props - Change default editor options - Add comment
7f9a552
to
28df30d
Compare
close #31
TextEditor
현재 이미지를 첨부했을 때 msw로 지정된 이미지를 보내주고 있어 스토리북에서는 확인이 불가해 gif를 첨부했습니다!
구현 & 고민사항
에디터 비교
toast-ui 내 다른 에디터의 이슈를 보니 리액트 18 버전을 언제 지원할지 알 수 없다고 해서 라이브러리를 변경하기로 했습니다.
구현 내용