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

[#52] - 업로드 API 연동 #58

Merged
merged 6 commits into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ on:
branches:
- main


jobs:
build:
name: react build & deploy
runs-on: ubuntu-latest


steps:
- name: checkout the code
uses: actions/checkout@v3


- name: Get npm cache directory
id: npm-cache-dir
Expand All @@ -29,40 +26,33 @@ jobs:
restore-keys: |
${{ runner.os }}-node-


- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22'


- name: Install pnpm
run: npm install -g pnpm


- name: Install dependencies
run: pnpm install


- name: Build the project
run: pnpm run build
env:
VITE_API_BASE_URL: ${{ vars.API_BASE_URL }}


- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2



- name: Upload to S3
run: |
aws s3 sync ./dist s3://${{ secrets.AWS_BUCKET_NAME }} --region ap-northeast-2



- name: Invalidate CloudFront Cache
run: |
aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_ID }} --paths "/*"

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-slot": "^1.1.2",
"@tanstack/react-query": "^5.66.7",
"axios": "^1.7.9",
"dompurify": "^3.2.4",
"path": "^0.12.7",
"react": "^18.3.1",
Expand Down
48 changes: 48 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import { RouterProvider } from 'react-router';

import { globalStyles } from '@assets/styles/global-styles';
import { Global } from '@emotion/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { router } from './route';

const queryClient = new QueryClient();

function App() {
return (
<div>
<Global styles={globalStyles} />
<RouterProvider router={router} />
</div>
<QueryClientProvider client={queryClient}>
<div>
<Global styles={globalStyles} />
<RouterProvider router={router} />
</div>
</QueryClientProvider>
);
}

Expand Down
5 changes: 5 additions & 0 deletions src/common/services/service-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import axios from 'axios';

export const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
});
42 changes: 17 additions & 25 deletions src/features/upload/components/file-upload/file-upload.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { useEffect } from 'react';
import { useDropzone } from 'react-dropzone';
import {
Controller,
FieldValues,
SubmitErrorHandler,
SubmitHandler,
useForm,
} from 'react-hook-form';
import { Controller, FieldValues, SubmitErrorHandler, useForm } from 'react-hook-form';

import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
Expand All @@ -24,40 +17,39 @@ const schema = z.object({
.refine((file) => file?.type?.includes('pdf'), 'PDF 파일을 업로드해주세요.'),
});

export default function FileUpload({ ...props }) {
interface FileUploadProps {
onSubmit: (data: FieldValues) => unknown;
}

// 파일 업로드용 컴포넌트
export default function FileUpload({ onSubmit }: FileUploadProps) {
const {
handleSubmit,
control,
watch,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
mode: 'onChange',
});

const onSubmit: SubmitHandler<FieldValues> = (data) => {
// TODO: PDF 업로드 API 호출
console.log(data);
};

const onSubmitError: SubmitErrorHandler<FieldValues> = (errors) => {
console.log('errors', errors);
};

const file = watch('file');

useEffect(() => {
if (!file) return;

handleSubmit(onSubmit, onSubmitError)();
}, [file, handleSubmit]);

return (
<form css={styles.form}>
<form css={styles.form} onSubmit={handleSubmit(onSubmit, onSubmitError)}>
<Controller
name="file"
control={control}
render={({ field }) => <Dropzone {...props} onChange={field.onChange} />}
render={({ field }) => (
<Dropzone
onChange={(files) => {
field.onChange(files);

handleSubmit(onSubmit, onSubmitError)();
}}
/>
)}
/>
{errors.file && <div css={styles.errorText}>{errors.file.message?.toString()}</div>}
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { usePostPortfolioMutation } from '../../services/mutations';
import FileUpload from '../file-upload/file-upload';

export default function PortfolioUpload() {
const { mutateAsync: postPortfolio } = usePostPortfolioMutation();

return (
<FileUpload
onSubmit={async ({ file }) => {
try {
const formData = new FormData();
formData.append('file', file);

await postPortfolio(
{
file,
},
{
onSuccess: (data) => {
// TODO: 업로드 성공 시 액션
console.log('success', data);
},
},
);
} catch (error) {
// TODO: 업로드 실패 시 액션
console.log('error', error);
}
}}
/>
);
}
15 changes: 15 additions & 0 deletions src/features/upload/services/mutations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useMutation } from '@tanstack/react-query';

import { postPortfolio } from './post-portfolio';
import { PortfolioRequest } from '../types/portfolio-types';

export const usePostPortfolioMutation = () => {
return useMutation({
mutationKey: ['postPortfolio'],
mutationFn: async ({ file }: PortfolioRequest) => {
const data = await postPortfolio({ file });

return data;
},
});
};
19 changes: 19 additions & 0 deletions src/features/upload/services/post-portfolio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { axiosInstance } from '@/common/services/service-config';

import { PortfolioRequest, PortfolioResponse } from '../types/portfolio-types';

export const postPortfolio = async ({ file }: PortfolioRequest) => {
const { data } = await axiosInstance.post<PortfolioResponse>(
'/api/v1/files/portfolio',
{
file,
},
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
);

return data;
};
11 changes: 7 additions & 4 deletions src/features/upload/stories/file-upload.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import FileUpload from '@/features/upload/components/file-upload/file-upload';
const meta = {
title: 'Components/FileUpload',
component: FileUpload,
argTypes: {
maxFiles: { control: 'number', description: '업로드 가능한 파일 개수' },
},
} satisfies Meta<typeof FileUpload>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};
export const Default: Story = {
args: {
onSubmit: () => {
console.log('submitted');
},
},
};
9 changes: 9 additions & 0 deletions src/features/upload/types/portfolio-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type PortfolioRequest = {
file: File;
};

export type PortfolioResponse = {
id: string;
logicalName: string;
url: string;
};
4 changes: 2 additions & 2 deletions src/features/upload/upload-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import FileUpload from '@/features/upload/components/file-upload/file-upload';
import PortfolioUpload from './components/portfolio-upload/portfolio-upload';

import * as styles from './upload-page.styles';

Expand All @@ -7,7 +7,7 @@ export default function UploadPage() {
<div css={styles.container}>
<div css={styles.logo}>Logo</div>
<h1 css={styles.title}>포트폴리오를 업그레이드 해볼까요?</h1>
<FileUpload />
<PortfolioUpload />
</div>
);
}
Loading