Skip to content

Commit

Permalink
Merge pull request #178 from Strong-Potato/177-feat-add-s3-api-apply-…
Browse files Browse the repository at this point in the history
…presigned-url

177 feat add s3 api apply presigned url
  • Loading branch information
HOOOO98 authored Jan 25, 2024
2 parents 0f2a2f8 + a40a6c2 commit 0ee66db
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 54 deletions.
53 changes: 53 additions & 0 deletions src/api/s3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import axios from 'axios';

import {PresignedUrlElement} from '@/types/s3';

export const s3Request = {
uploadImage: async (image: FileList) => {
try {
// presigned url 발급
const res = await axios.post('/api/s3/presigned', [image[0].name]);
const presignedUrl = await res.data.data.elements[0].preSignedUrl;
console.log('api/s3/presigned response', res);

// s3에 이미지 업로드
const uploadRes = await axios.put(presignedUrl, image[0], {
headers: {
'Content-Type': 'image/*',
},
});
console.log('s3 upload response: ', uploadRes);

return presignedUrl;
} catch (error) {
console.log(error);
}
},

uploadImages: async (images: FileList[]) => {
try {
// 이미지 당 presigned url 발급
const res = await axios.post(
'/api/s3/presigned',
images.map((image) => image[0].name),
);
const presignedUrls = await res.data.data.elements.map((element: PresignedUrlElement) => element.preSignedUrl);
console.log('api/s3/presigned response', res);

// s3에 이미지들 업로드
const uploadRes = presignedUrls.map(
async (url: string, index: number) =>
await axios.put(url, images[index], {
headers: {
'Content-Type': 'image/*',
},
}),
);
console.log('s3 upload response: ', uploadRes);

return presignedUrls;
} catch (error) {
console.log(error);
}
},
};
6 changes: 3 additions & 3 deletions src/components/Auth/Input/InputImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import Camera from '@/assets/icons/camera.svg?react';

import {InputImageProps} from '@/types/auth';

function InputImage({register, imageBlob, setImageBlob}: InputImageProps) {
function InputImage({register, imageUrl, setImageUrl}: InputImageProps) {
return (
<section className={styles.image}>
<label htmlFor='image' className={styles.image__label} style={{backgroundImage: `url(${imageBlob})`}}>
<label htmlFor='image' className={styles.image__label} style={{backgroundImage: `url(${imageUrl})`}}>
<div className={styles.image__label__camera}>
<Camera width={16} height={16} />
</div>
Expand All @@ -22,7 +22,7 @@ function InputImage({register, imageBlob, setImageBlob}: InputImageProps) {
onChange: (e) => {
const file = e.target.files[0];
const fileUrl = URL.createObjectURL(file);
setImageBlob!(fileUrl);
setImageUrl!(fileUrl);
},
})}
/>
Expand Down
9 changes: 6 additions & 3 deletions src/components/Auth/Signup/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import StepPassword from '@/components/Auth/Signup/Step/StepPassword';
import StepProfile from '@/components/Auth/Signup/Step/StepProfile';
import CustomToast from '@/components/CustomToast/CustomToast';

import {s3Request} from '@/api/s3';

import {AuthForm, SignupFormProps} from '@/types/auth';

function SignupForm({signupStep, setSignupStep}: SignupFormProps) {
Expand All @@ -37,15 +39,16 @@ function SignupForm({signupStep, setSignupStep}: SignupFormProps) {
const navigate = useNavigate();

const onSubmit: SubmitHandler<AuthForm> = async (data) => {
console.log(code);
if (Object.keys(dirtyFields).length < 5) return;
console.log(data);

try {
const {email, password, nickname} = data;
const {email, password, image, nickname} = data;
const profile = dirtyFields.image ? await s3Request.uploadImage(image as FileList) : undefined;

const res = await axios.post('/api/auth/register', {
email,
password,
profile: profile.split('?')[0],
nickname,
token: code,
});
Expand Down
33 changes: 15 additions & 18 deletions src/components/Auth/Signup/Step/StepProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import styles from "./Step.module.scss";
import {useState} from 'react';

import AuthButton from "@/components/Auth/Button/AuthButton";
import styles from './Step.module.scss';

import InputImage from "../../Input/InputImage";
import InputNickname from "../../Input/InputNickname";
import AuthButton from '@/components/Auth/Button/AuthButton';

import { StepProfileProps } from "@/types/auth";
import defaultProfile from '@/assets/profile_default.svg';

import InputImage from '../../Input/InputImage';
import InputNickname from '../../Input/InputNickname';

import {StepProfileProps} from '@/types/auth';

function StepProfile({register, resetField, dirty, error}: StepProfileProps) {
const [imageUrl, setImageUrl] = useState<string | undefined>(defaultProfile);

function StepProfile({ register, resetField, dirty, error }: StepProfileProps) {
return (
<section className={styles.container}>
<h2>프로필을 설정해주세요</h2>

<InputImage register={register} />
<InputImage register={register} imageUrl={imageUrl} setImageUrl={setImageUrl} />

<InputNickname
register={register}
dirty={dirty}
error={error}
resetField={resetField}
/>
<InputNickname register={register} dirty={dirty} error={error} resetField={resetField} />

<AuthButton
content="시작하기"
disabled={!dirty || error ? true : false}
type="submit"
/>
<AuthButton content='시작하기' disabled={!dirty || error ? true : false} type='submit' />
</section>
);
}
Expand Down
39 changes: 20 additions & 19 deletions src/components/User/EditProfileForm/EditProfileForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';
import {useState} from 'react';
import {useForm} from 'react-hook-form';
import {SubmitHandler, useForm} from 'react-hook-form';
import {useNavigate} from 'react-router-dom';

import styles from './EditProfileForm.module.scss';
Expand All @@ -9,6 +9,9 @@ import AuthButton from '@/components/Auth/Button/AuthButton';
import InputImage from '@/components/Auth/Input/InputImage';
import InputNickname from '@/components/Auth/Input/InputNickname';

import {s3Request} from '@/api/s3';
import defaultProfile from '@/assets/profile_default.svg';

import {AuthForm} from '@/types/auth';
import {GetUserProp} from '@/types/sidebar';

Expand All @@ -18,44 +21,42 @@ function EditProfileForm({data}: {data: GetUserProp | undefined}) {
resetField,
formState: {errors, dirtyFields},
handleSubmit,
watch,
} = useForm<AuthForm>({
mode: 'onChange',
defaultValues: {
image: undefined,
nickname: data?.data.nickname,
},
});
const watchFields = watch();
const [imageBlob, setImageBlob] = useState<string | undefined>(data?.data.profile);
const [imageUrl, setImageUrl] = useState<string | undefined>(data?.data.profile || defaultProfile);

const navigate = useNavigate();

const onSubmit = async () => {
const onSubmit: SubmitHandler<AuthForm> = async (formData) => {
try {
const {image, nickname} = watchFields;
const {image, nickname} = formData;

// 프로필 이미지 변경 있을 때
if (dirtyFields.image) {
const s3Res = await axios.post('/api/s3/presigned', [image![0].name]);
const s3Url = await s3Res.data.data.elements[0];
const uploadRes = await axios.put(s3Url, image![0], {
headers: {
'Content-Type': 'image/*',
},
});
console.log('s3 upload state: ', uploadRes, 's3 Url : ', s3Url);
const presignedUrl = await s3Request.uploadImage(image as FileList);

const res = await axios.put('/api/members/my-info', {
nickname,
profile: s3Url,
profile: presignedUrl.split('?')[0],
});

console.log('api response : ', res);
console.log('ModifyProfileImage :', res);
navigate('/user');
return;
}

// 프로필 이미지 변경 X, 닉네임만 변경
const res = await axios.put('/api/members/my-info', {
nickname,
profile: imageBlob,
profile: imageUrl,
});

console.log('OnlyNickname', res);
console.log('OnlyNickname: ', res);
navigate('/user');
} catch (error) {
console.log(error);
Expand All @@ -64,7 +65,7 @@ function EditProfileForm({data}: {data: GetUserProp | undefined}) {

return (
<form className={styles.container} onSubmit={handleSubmit(onSubmit)}>
<InputImage register={register} imageBlob={imageBlob} setImageBlob={setImageBlob} />
<InputImage register={register} imageUrl={imageUrl} setImageUrl={setImageUrl} />

<InputNickname register={register} dirty={dirtyFields.nickname} error={errors.nickname} resetField={resetField} />

Expand Down
21 changes: 13 additions & 8 deletions src/components/User/MypageList/MypageList.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@use "@/sass" as *;
@use '@/sass' as *;

.container {
& > li {
Expand Down Expand Up @@ -34,15 +34,17 @@
.tooltipList {
display: none;
position: absolute;
left: -38px;
width: 20rem;
padding: 21px 8px 8px 16px;
left: -50px;
width: 23rem;
height: 12.8rem;
padding: 20px 8px 8px 20px;
margin-top: 2px;
border-radius: 8px;
word-break: keep-all;

background-image: url("@/assets/icons/tooltipFrame.svg");
background-image: url('@/assets/icons/tooltipFrame.svg');
background-repeat: no-repeat;
background-size: auto;
background-size: 100% 100%;

list-style-type: disc;
list-style-position: inside;
Expand All @@ -53,8 +55,11 @@

@include animate(showTooltip, 0.2s, ease-in, forwards);

& > li > span {
margin-left: -5px;
& > li {
margin-bottom: 4px;
& > span {
margin-left: -5px;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/pages/User/User.module.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
@use "@/sass" as *;
@use '@/sass' as *;

.container {
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
color: $neutral900;
margin-bottom: 120px;

h1 {
padding: 16px 20px;
Expand Down
4 changes: 2 additions & 2 deletions src/types/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export interface InputPasswordConfirmProps {

export interface InputImageProps {
register: UseFormRegister<AuthForm>;
imageBlob?: string;
setImageBlob?: React.Dispatch<React.SetStateAction<string | undefined>>;
imageUrl?: string;
setImageUrl?: React.Dispatch<React.SetStateAction<string | undefined>>;
}

export interface InputNickname {
Expand Down
4 changes: 4 additions & 0 deletions src/types/s3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface PresignedUrlElement {
fileName: string;
preSignedUrl: string;
}

0 comments on commit 0ee66db

Please sign in to comment.