Skip to content
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

[23] 포스트 작성 페이지에서 이미지를 S3에 올리는 기능 및 유동적인 UI 구현 #62

Merged
merged 34 commits into from
Nov 28, 2019

Conversation

leehwarang
Copy link
Contributor

@leehwarang leehwarang commented Nov 22, 2019

구현사항

  • 사용자가 1개~5개 사이의 이미지를 선택하고 포스트 업로드 페이지의 제출 버튼 누를시 AWS S3에 업로드 되는 기능 구현 -> useS3 hooks 생성
  • 5개 이상의 이미지 업로드시 alert 기능 구현
  • 이미지 왼쪽 상단의 별 버튼을 누르면 대표 이미지로 선택되는 기능 구현
  • 이미지 오른쪽 상단의 닫기 버튼을 누르면 삭제되는 기능 구현
  • 아무 이미지도 올리지 않았을 시의 input type="file" 태그와, 1개 이상의 이미지를 올렸을 시의 input type="file" 태그의 디자인을 다른 스타일 적용

참고사항

image

image

image

  • 용량이 커서 시연 영상이 안올라가네요. feat/23-upload-image-s3-redesign 브랜치에서 실행 후 테스트해주세요. S3 로그인하면 업로드 되는 것 까지 확인할 수 있어요.
  • 기획 당시 drag and drop 기능이 포함될 예정이였으나, input 태그와 drag and drop 을 같이 구현하는데 무리가 있어서 다음 피쳐로 미뤄두었습니다. 추후 advanced 피쳐로 추가하는게 좋을 것 같아요.
  • 제출 버튼을 클릭하면 S3에 이미지 업로드 후, 사용자가 입력한 데이터를 서버에 POST 요청을 보냅니다. 따라서 S3에 이미지 업로드 실패시 제출이 안되는 분기 처리가 필요한데 이 부분은 post upload page 마무리 시 같이 진행할게요.
  • prettier singleQuote를 rebase 안하고 했더니, '가 "로 바뀌었네요. 지금 rebase하니까 충돌 나서 엘런 PR 머지후 합치면서 수정할게요.

해결된 이슈 번호

- AWS S3 인스턴스 생성
- 버킷 내에 디렉토리 생성하는 createAlbum 함수 구현
- 특정 디렉토리에 이미지를 업로드하는 addImage 함수 구현
- image의 왼쪽 상단에 버튼 추가
- 버튼 클릭 시, 대표 이미지 인덱스가 정해지고 렌더시 해당 인덱스를 가진 이미지에 스타일 적용
- button 태그에서 input type="image"로 변경
Copy link
Collaborator

@dev-allenk dev-allenk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉헉.. 코드 리뷰하는 것도 힘드네요.
구현하느라 정말 많이 노력했겠어요. 저도 aws문서도 가끔 들여다봤지만 정말 알수없는 말 투성이더라구요 😅
발번역과 적은 레퍼런스, 여러 시행착오를 뚫고 구현해낸 미셸 정말 많이 칭찬합니다! 대단해요!😄👏👏👏

AWS객체 노출되는 것 관련해서 얘기해봐야 할 것도 있고 작은 버그도 있어서 일단 체인지 리퀘스트해요.
코멘트 참고하고 몇가지 질문 답변 부탁해요!

bug:

  1. 첫 이미지가 대표이미지일 때, 첫 이미지를 삭제하면 대표이미지 설정이 사라져요.(representativeIndex가 -1이 돼요)

@@ -14,6 +14,7 @@
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.2",
"aws-sdk": "^2.574.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

링크 에 따르면 aws-sdk 패키지는 node.js를 위한 패키지라고 나와있네요. 브라우저 환경에서는 script 태그를 이용해야하나봐요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요! 근데 왜 지금 aws-sdk 패키지를 사용했는데 정상적으로 동작할까요?
이 글에 따르면 aws-sdk패키지를 브라우저에도 사용하게 하려면 Browserify를 사용해야 한다고 하는데요. 뭔가 잘 돌아가니 스크립트 방식으로 변경할 필요가 잘 안 느껴져요. 😅

리액트에서 스크립트 동적으로 삽입하는 걸 한 번도 안해봐서 이 번 기회에 한 번 시도해 보는 건 의미가 있을 것 같고요.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좀 헷갈리는데 AWS사용방법을 이해한대로 정리해보자면, 아래 2가지 방법이 있겠네요.

1. 브라우저에서 AWS를 사용

스크립트를 삽입해서 전역에 AWS객체를 추가하고, 해당 객체를 사용한다.

2. Node.js 환경에서 AWS를 사용

aws-sdk 패키지를 설치하고 const AWS = require('aws-sdk') 를 통해 패키지에서 export한 AWS객체를 사용한다.

언급된 Browserify는 대충 보니까 require문을 해석할 수 있는 번들러네요.
그러니까 결국, 우리가 작성한 js 파일에서 AWS객체가 접근가능하면 되는것이라고 이해했어요.
우리는 지금 웹팩을 사용하고 있기 때문에 aws-sdk 패키지에 있는 AWS객체가 우리가 작성한 js파일에 번들링되어서 포함되겠죠. 마치 AWS 스크립트를 삽입한 것과 동일한 상태가 되는 거에요. 그러니까 문제없이 잘 돌아가나봐요.

스크립트와 패키지의 차이에 대해서는 공식 문서 상으론 이정도로 언급되어 있어요.

Node.js에 SDK를 사용하는 것은 웹 브라우저에서 JavaScript에 SDK를 사용하는 방법과 다릅니다. SDK를 로드하고 특정 웹 서비스에 액세스하는 데 필요한 자격 증명을 얻는 방법에서 차이가 비롯됩니다. 특정 API 사용이 Node.js와 브라우저 간에 다른 경우 해당 차이점이 표시됩니다. 출처

요약하자면 1) SDK를 로드하는 방법, 2) 자격증명 얻는 방법, 3) 특정 api간의 차이, 이 3가지 차이점이 있다는 말인가봐요.
그런데 1) SDK를 로드하는 방법은 웹팩을 사용하기 때문에 aws-sdk패키지를 사용해도 문제가 없다고 생각돼요. 2) 자격증명 얻는 방법은 브라우저 방식을 사용하고 있는 듯한데 이 부분은 잘 모르겠고, 3) api간의 차이는 아마 거의 없겠지만 혹시 aws-sdk의 AWS객체가 브라우저에서 지원하지 않는 api로 구현되어있다면 에러가 발생하겠네요. (근데 차이가 있긴한가 잘 모르겠어요. node.js도 js 스펙에 맞게 똑같이 구현하지 않나요 😅)

제 결론은, 그냥 써도 되겠다! 입니다.😃

import './ImageUploader.scss';
import React, { useState } from "react";
import "./ImageUploader.scss";
import { PRE_SIGNED_URL } from "../../configs";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 변수는 이제 없어도 되는거겠죠?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 삭제했어요.

const { selectedImages, previewUrls } = images;

const getImage = ({ target }) => {
const files = Array.from(target.files);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배열로 변환해서 사용하니까 좋네요 👍

const representativeClassName =
index === representativeIndex ? "representative" : "non-representative";
return (
<div className="image-uploader-preview-wrapper" key={index}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://ko.reactjs.org/docs/lists-and-keys.html#keys

key값을 index로 두는건 권장할만한 방법은 아니지만 지금 상황에서는 뾰족한 수가 없어보이네요.
image값을 쓸 수도 있겠지만 너무 긴 감이 있어서..
나중에 이미지의 순서를 변경하는 기능이 들어간다면 고려해봐야 할 것 같아요.

Copy link
Contributor Author

@leehwarang leehwarang Nov 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 drag and drop 이랑 이미지 드래그해서 순서 변경하는 걸 이미지 업로더 advanced feature 로 두면 되겠네요.

Comment on lines 54 to 100
<div className="image-uploader">
<input type="file" className="image-uploader-input" />
{previewImages}
{0 < selectedImages.length && selectedImages.length < maximumCnt && (
<div className="image-uploader-second-input-wrapper">
<label
className="image-uploader-second-input-label"
htmlFor="second-input"
>
+
</label>
<input
className="image-uploader-second-input-btn"
id="second-input"
type="file"
accept="image/*"
onChange={getImage}
multiple
name="filename[]"
/>
</div>
)}
<div
className="image-uploader-first-input-wrapper"
style={{
display: !previewUrls.length ? "inline-block" : "none"
}}
>
<img
className="image-uploader-first-input-icon"
src={`${IMAGE_BUCKET_URL}/image-upload-icon.png`}
/>
<label
className="image-uploader-first-input-label"
htmlFor="first-input"
>
이미지 선택
</label>
<input
id="first-input"
type="file"
accept="image/*"
className="image-uploader-first-input-btn"
onChange={getImage}
multiple
name="filename[]"
/>
</div>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return (
  <div className="image-uploader">
    { hasSelectedImages ?
      <PreviewImages />
      <SecondInputButton />  
      :
      <FirstInputButton />
    }
  </div>
)

이런식으로 작성하는 건 어때보여요?
지금은 한눈에 너무 많은 정보가 들어오는 것 같아서 보는데 조금 벅찬 기분이 들어서요.

Copy link
Contributor Author

@leehwarang leehwarang Nov 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋네요 ! 재사용 컴포넌트에 사로잡혀 있어서 지금까지 한 번만 사용하는 컴포넌트들을 분리 할 생각을 안했던 것 같아요. 가독성도 중요한데 말이죠. 의견 준대로 변경할게요. 👍

Comment on lines +75 to +77
const represenTativeImage = e.target.previousSibling.previousSibling.src;
const represenTativeIndex = images.previewUrls.findIndex(
el => el === represenTativeImage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 위와 마찬가지로.. DOM 구조가 변경된다면 이 부분도 같이 수정이 필요할 것 같아요.
위에서 말했듯이 dataset을 활용하는 것도 가능하겠어요!

).then(
uploadedUrl => {
console.log("Successfully uploaded photo.");
return uploadedUrl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 uploadedUrl 배열에서 각 item의 Location 값만 리턴해줘야할 것 같아요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useS3 내에서 reduce 돌리는게 안되서 받는 쪽에서 파싱 작업 했어요. 왜 안되는지는 좀 알아보고 알려줄게요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    ).then(
      uploadedUrl => {
        return uploadedUrl.reduce((acc, cur) => {
          return acc.concat(cur.Location);
        }, []);
      },

받는 쪽 코드를 그대로 then으로 옮겨도 되겠네요 👍


// TODO : 브라우저의 토큰에 저장되어 있는 쿠키에서 user nickname 가져오는 코드 추가
const albumName = await createAlbum("michelle", YYYYMMDDHHMMSS(new Date()));
const uploadedUrl = await addImage(images.selectedImages, albumName);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uploadedUrl을 서버로 전송하면 되는거겠죠?
이부분 async/await 쓰는게 어색하다고 했던 것 같은데 createAlbum함수랑 addImage함수를 콜백함수를 받게끔 수정할 수는 있을 것 같아요. 근데 지금 이대로가 가독성은 더 좋을 것 같은 느낌!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 사용하는게 어색할 뿐 가독성은 훨씬 좋죠 ㅎㅎ 더 익숙해져야 겠어요.

Comment on lines +98 to +101
addImageHandler={addImageHandler}
deleteImageHandler={deleteImageHandler}
representativeImageHandler={selectRepresentativeImage}
representativeIndex={representativeIndex}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

핸들러함수를 PostUploadPage 에 위치한 이유가 궁금해요.
저는 얼마전까지는 별 생각없이 아무데나 두고 작성했는데 요즘 들어서는 이벤트가 발생하는 하위컴포넌트에 두는게 자연스럽다는 생각이 들더라구요. useReducer로 코드 수정하기에도 용이하고요.ㅎ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 일단 PostUploadPage에서 제출 버튼을 클릭하니까, 관련된 데이터와 데이터를 다루는 함수들을 가지고 있어야 한다고 생각 했어요. 저는 일반적으로 단독으로 사용하는 컴포넌트들은 걔가 관리하고, 상위 컴포넌트가 있으면 상위 컴포넌트가 관리하는 것 같아요!

@@ -17,3 +17,17 @@ export const throttle = (callback, delay = 200) => {
}
};
};

export const YYYYMMDDHHMMSS = dateObj => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요 함수 이름 직관적이고 재밌네요 ㅋㅋ
처음 Date() 를 제안했을 땐 ms 단위로 나오는 알아보기 힘든 숫자를 사용할 생각이었는데
이렇게 사용하니까 읽기도 쉽고 좋네요! 👍

@dev-allenk
Copy link
Collaborator

아참! 디자인 이쁘게 잘 나왔네요!😻
별모양 버튼, 삭제 버튼, 아이콘 들어간 버튼 전부 예뻐요!ㅎㅎ

- FirstInputButton, SecondInputButton, PreviewImages 컴포넌트 생성
- image input button에서 name="files[]" 삭제
- PreviewImage의 margin 스타일 수정
- useS3에서 var, let을 const로 변경
- PostUploadPage에서 e.preventDefault() 삭제
Copy link
Contributor Author

@leehwarang leehwarang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 다 확인했어요. 꼼꼼한 피드백 고마워요 ~
일단 남은 건

  1. aws-sdk 를 script 불러 오는 것으로 변경하기
  2. config 변수가 노출되는 부분 해결하기
  3. previewImage에서 이미지가 먼저 보여지고, 대표 이미지 선택과 닫기 버튼 불러올 때 렌더링 시간 차이 생기는 부분 해결하기

이 세 가지인데요.
1번 -> require 로 불러오는 방법과 script 로 불러오는 방법이 어떤 차이가 있는지 좀 더 알아볼게요.
2번 -> 이건 같이 방법을 좀 더 찾아봐야 할 것 같고, 우리 코드 전반적으로 수정해야 하는 부분이니까 피쳐로 따서 진행하는거 어때요?
3번 -> 이것도 아까 이야기 한 것 처럼 버튼 이미지를 컴포넌트로 작성하고 React.memo 활용하는 방법으로 시도해볼게요. 크리티컬한 부분 아니여서 피쳐로 따서 진행할게요.

accept="image/*"
onChange={getImage}
multiple
name="filename[]"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 글 을 참고 했어요~

저는 저 글 보고 input 태그를 이용해 여러개의 데이터를 올리려면 multiple name="filename[]" 이 두 가지 속성을 함께 사용해야 하는 줄 알았는데요. 테스트 해보니 multiple 은 필수적이고 name 은 없어도 되네요. name은 아마 form 태그 안에 있는 input 태그에서 여러개의 데이터를 서버에 보낼 때 사용해야 하는 것 같은데, 이 코드에서 이미지 올리는 부분은 input 태그로 받은 값을 바로 서버에 보내지 않고, 후처리해서 Promise로 S3 에 올리는 거라 name이 없어도 되는 듯 해요.

@@ -14,6 +14,7 @@
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.2",
"aws-sdk": "^2.574.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요! 근데 왜 지금 aws-sdk 패키지를 사용했는데 정상적으로 동작할까요?
이 글에 따르면 aws-sdk패키지를 브라우저에도 사용하게 하려면 Browserify를 사용해야 한다고 하는데요. 뭔가 잘 돌아가니 스크립트 방식으로 변경할 필요가 잘 안 느껴져요. 😅

리액트에서 스크립트 동적으로 삽입하는 걸 한 번도 안해봐서 이 번 기회에 한 번 시도해 보는 건 의미가 있을 것 같고요.

&-wrapper {
padding: 0 1.5rem;
height: 4rem;
line-height: 3.5rem;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

div 안에 있는 텍스트를 수직 정렬할 때 line-height 를 사용해요. 이 글 을 살펴보면 좋을 것 같아요. 폰트 사이즈 보다 조금 더 큰 값을 line-height 으로 주면, line-height - fontsize 만큼의 여백을 폰트의 상하로 나눠 가진다고 이해하고 있어요.

border: 1px solid $main-color;
border-radius: 2rem;
background-color: $main-color-light;
display: flex;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

display: flex;
align-items: center;
justify-content: center;

요 코드 다 삭제할게요.

Comment on lines 13 to 15
& + & {
margin-left: 2rem;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미세한 스타일 버그가 있었군요. margin: 0 1rem 주는 방식으로 변경했어요.
다만 "첫번째 엘리먼트는 그대로인데 두번째부터는 margin-left? 왜 두번째부터 margin-left가 필요한거지?" 이 질문에 대해서는 코드를 작성한 사람의 의도를 좀 더 이해해보려고 했으면 좋을 것 같아요. 정답은 없으니까요.

저는 연속적으로 보여지는 요소들이 있을 때, 첫 번째 요소의 위치는 그대로 두고 두 번째와 세 번째 요소에 margin-left 를 줘서 간격을 띄우는 방법을 꽤 자주 사용하더라구요. 전체적으로 margin: 0 1rem 을 주면 첫 번째 요소도 왼쪽 공간을 가져서 원하지 않는 공간이 생기니까요.

Comment on lines 73 to 80
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

더 간단하게 표현이 가능하네요! 제시해준 걸로 변경했어요. 땡큐 ~

};

const selectRepresentativeImage = e => {
e.preventDefault();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleteImageHandler 함수와 selectRepresentativeImage 함수에서 e.preventDefault() 삭제했어요.
이벤트 발생으로 호출되는 부분에서는 e.preventDefault()를 습관적으로 사용하고 있었네요.

Copy link
Collaborator

@dev-allenk dev-allenk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 다 확인했어요. 꼼꼼한 피드백 고마워요 ~
일단 남은 건

  1. aws-sdk 를 script 불러 오는 것으로 변경하기
  2. config 변수가 노출되는 부분 해결하기
  3. previewImage에서 이미지가 먼저 보여지고, 대표 이미지 선택과 닫기 버튼 불러올 때 렌더링 시간 차이 생기는 부분 해결하기

이 세 가지인데요.
1번 -> require 로 불러오는 방법과 script 로 불러오는 방법이 어떤 차이가 있는지 좀 더 알아볼게요.
2번 -> 이건 같이 방법을 좀 더 찾아봐야 할 것 같고, 우리 코드 전반적으로 수정해야 하는 부분이니까 피쳐로 따서 진행하는거 어때요?
3번 -> 이것도 아까 이야기 한 것 처럼 버튼 이미지를 컴포넌트로 작성하고 React.memo 활용하는 방법으로 시도해볼게요. 크리티컬한 부분 아니여서 피쳐로 따서 진행할게요.

1번은 별도로 코멘트 달았으니 한번 확인해보아요. 결론적으론 그냥 이대로 사용해도 될 것 같아요.
2, 3번에는 이견 없습니당. 일단 이 PR은 이 정도로 마무리해요. 잘 동작하니 ㅎㅎ

@@ -14,6 +14,7 @@
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.2",
"aws-sdk": "^2.574.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좀 헷갈리는데 AWS사용방법을 이해한대로 정리해보자면, 아래 2가지 방법이 있겠네요.

1. 브라우저에서 AWS를 사용

스크립트를 삽입해서 전역에 AWS객체를 추가하고, 해당 객체를 사용한다.

2. Node.js 환경에서 AWS를 사용

aws-sdk 패키지를 설치하고 const AWS = require('aws-sdk') 를 통해 패키지에서 export한 AWS객체를 사용한다.

언급된 Browserify는 대충 보니까 require문을 해석할 수 있는 번들러네요.
그러니까 결국, 우리가 작성한 js 파일에서 AWS객체가 접근가능하면 되는것이라고 이해했어요.
우리는 지금 웹팩을 사용하고 있기 때문에 aws-sdk 패키지에 있는 AWS객체가 우리가 작성한 js파일에 번들링되어서 포함되겠죠. 마치 AWS 스크립트를 삽입한 것과 동일한 상태가 되는 거에요. 그러니까 문제없이 잘 돌아가나봐요.

스크립트와 패키지의 차이에 대해서는 공식 문서 상으론 이정도로 언급되어 있어요.

Node.js에 SDK를 사용하는 것은 웹 브라우저에서 JavaScript에 SDK를 사용하는 방법과 다릅니다. SDK를 로드하고 특정 웹 서비스에 액세스하는 데 필요한 자격 증명을 얻는 방법에서 차이가 비롯됩니다. 특정 API 사용이 Node.js와 브라우저 간에 다른 경우 해당 차이점이 표시됩니다. 출처

요약하자면 1) SDK를 로드하는 방법, 2) 자격증명 얻는 방법, 3) 특정 api간의 차이, 이 3가지 차이점이 있다는 말인가봐요.
그런데 1) SDK를 로드하는 방법은 웹팩을 사용하기 때문에 aws-sdk패키지를 사용해도 문제가 없다고 생각돼요. 2) 자격증명 얻는 방법은 브라우저 방식을 사용하고 있는 듯한데 이 부분은 잘 모르겠고, 3) api간의 차이는 아마 거의 없겠지만 혹시 aws-sdk의 AWS객체가 브라우저에서 지원하지 않는 api로 구현되어있다면 에러가 발생하겠네요. (근데 차이가 있긴한가 잘 모르겠어요. node.js도 js 스펙에 맞게 똑같이 구현하지 않나요 😅)

제 결론은, 그냥 써도 되겠다! 입니다.😃

<div
className="image-uploader-first-input-wrapper"
style={{
display: !previewUrls.length ? "inline-block" : "none"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드는 이제 없어도 될 것 같아요! FirstInputButton을 사용하는 쪽에 조건이 들어있으니.

accept="image/*"
className="image-uploader-first-input-btn"
onChange={onChangeHandler}
multiple
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name속성은 지웠군요.
name 속성에 대괄호 사용하는 링크 글 잘 읽었어요. 덕분에 또 새로운 사실을 알게 되었네요. 고마워요 ㅎㅎ
아직 어떻게 사용되는지는 잘 모르겠지만 필요할 때 생각날듯!

const getImage = ({ target }) => {
const files = Array.from(target.files);

if (files.length + selectedImages.length > 5) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에 작성한 maximumCnt 변수를 여기 사용해도 괜찮겠네요.

representativeIndex={representativeIndex}
representativeImageHandler={representativeImageHandler}
/>
{selectedImages.length < 5 && (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 maximumCnt를 사용할 수 있을 것 같아요.

).then(
uploadedUrl => {
console.log("Successfully uploaded photo.");
return uploadedUrl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    ).then(
      uploadedUrl => {
        return uploadedUrl.reduce((acc, cur) => {
          return acc.concat(cur.Location);
        }, []);
      },

받는 쪽 코드를 그대로 then으로 옮겨도 되겠네요 👍

&-wrapper {
padding: 0 1.5rem;
height: 4rem;
line-height: 3.5rem;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line-height 관련 글 잘 읽었어요. 고마워요!
폰트를 세로방향으로 가운데정렬할 때 간단하게 이용할 수 있는 속성이네요. 👍

Comment on lines +53 to +55
const deletedImage = e.target.previousSibling.src;
const targetIndex = images.previewUrls.findIndex(
url => url === deletedImage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 코멘트가 없어서 시무룩..😥
이런 코드가 가능하다는 것도 참고..

const deleteImageHandler = e => {
  const targetIndex = e.target.dataset.idx;
  //나머지 코드
}

files.map(file => {
const fileName = file.name;
const albumPhotosKey =
"post-images/" + encodeURIComponent(albumName) + "//";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 맨 뒤에 "//" 슬래시 2개가 들어가서 그런지 s3에 업로드 될 때 빈 폴더가 하나 더생겨요.
스크린샷 2019-11-27 오후 1 45 13

@leehwarang leehwarang merged commit 35f5e85 into develop Nov 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request refactor/style Refactoring or styling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[23] 포스트 작성 페이지에서 이미지를 S3에 올리는 기능 및 유동적인 UI 구현
2 participants