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

Feature/#156 마일스톤 페이지 내 파일 접근 방식 수정 #159

10 changes: 0 additions & 10 deletions backend/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@ endif::[]

== API 목록

== 파일

=== `GET`: 파일 다운로드

.HTTP Request
include::{snippets}/download-file/http-request.adoc[]

.HTTP Response
include::{snippets}/download-file/http-response.adoc[]

== 회원

=== `GET`: 학생 정보 조회
Expand Down
21 changes: 21 additions & 0 deletions backend/src/main/java/sw_css/config/ResourceConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package sw_css.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class ResourceConfig implements WebMvcConfigurer {

private String connectPath = "/files/**";

@Value("${data.file-storage-path}")
private String resourcePath;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(connectPath)
.addResourceLocations("file:" + resourcePath);
}
}
8 changes: 7 additions & 1 deletion backend/src/main/java/sw_css/file/api/FileController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
import org.springframework.web.bind.annotation.RestController;
import sw_css.file.application.FileService;

@RequestMapping("/files")
@RequestMapping
@RestController
@RequiredArgsConstructor
public class FileController {
private final FileService fileService;

@Deprecated
@GetMapping("/{fileName}")
public ResponseEntity<byte[]> downloadFile(@PathVariable("fileName") final String fileName) throws IOException {
byte[] downloadFile = fileService.downloadFileFromFileSystem(fileName);
return ResponseEntity.ok(downloadFile);
}

@GetMapping("/favicon.ico")
public ResponseEntity<Void> ignoreFaviconRequest() {
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Deprecated
@Service
@RequiredArgsConstructor
@Transactional
Expand Down
1 change: 1 addition & 0 deletions backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ spring:
data:
client-url: http://localhost:3000
file-path-prefix: /backend/src/main/resources/static/files
file-storage-path: /절대 경로/
39 changes: 0 additions & 39 deletions backend/src/test/java/sw_css/restdocs/docs/FileApiDocsTest.java

This file was deleted.

3 changes: 3 additions & 0 deletions frontend/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const nextConfig = {
compiler: {
styledComponents: true,
},
images: {
domains: ['localhost'],
},
};

export default nextConfig;
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
'use client';

import Image from 'next/image';
import { useMemo } from 'react';

import { HistoryFileType } from '@/data/milestone';
import { useFileQuery } from '@/lib/hooks/useApi';
import { getFileType } from '@/lib/utils/utils';

interface FilePreviewProps {
fileName: string | null;
}

const FilePreview = ({ fileName }: FilePreviewProps) => {
const { data: file } = useFileQuery(fileName);
const fileUrl = useMemo(() => {
if (file) {
return URL.createObjectURL(file);
}
return '';
}, [file]);
switch (getFileType(fileName)) {
case HistoryFileType.PDF:
return (
<>
<a className="w-full rounded-sm bg-admin-primary-main text-white" href={fileUrl} download>
<a
className="w-full rounded-sm bg-admin-primary-main text-white"
href={process.env.NEXT_PUBLIC_FILE_URL + '/' + fileName}
download
>
다운로드
</a>
<embed
src={fileUrl}
src={process.env.NEXT_PUBLIC_FILE_URL + '/' + fileName}
type="application/pdf"
className="h-full w-full"
style={{ height: '100%', minHeight: '800px' }}
Expand All @@ -37,10 +30,21 @@ const FilePreview = ({ fileName }: FilePreviewProps) => {
case HistoryFileType.IMAGE:
return (
<>
<a className="w-full rounded-sm bg-admin-primary-main text-white" href={fileUrl} download>
<a
className="w-full rounded-sm bg-admin-primary-main text-white"
href={process.env.NEXT_PUBLIC_FILE_URL + '/' + fileName}
download
>
다운로드
</a>
<Image src={fileUrl} priority={false} layout="responsive" alt={fileName ?? ''} width={532} height={532} />
<Image
src={process.env.NEXT_PUBLIC_FILE_URL + '/' + fileName}
priority={false}
layout="responsive"
alt={fileName ?? ''}
width={532}
height={532}
/>
</>
);
case HistoryFileType.EMPTY:
Expand Down
30 changes: 10 additions & 20 deletions frontend/src/app/admin/milestone/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
import { Form, Formik } from 'formik';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useMemo } from 'react';
import * as Yup from 'yup';
import { toast } from 'react-toastify';

import { FileUploader } from '@/app/components/Formik/FileUploader';
import { useRegisterHistoryInBatchMutation } from '@/lib/hooks/useAdminApi';
import { useFileQuery } from '@/lib/hooks/useApi';

const validationSchema = Yup.object().shape({
file: Yup.mixed()
Expand All @@ -39,36 +37,28 @@ const initialValues: HistoryRegisterFormProps = {

const Page = () => {
const router = useRouter();
const { data: standardFile } = useFileQuery('history_standard.pdf');
const standardFileUrl = useMemo(() => {
if (standardFile) {
return URL.createObjectURL(standardFile);
}
return '';
}, [standardFile]);

const { data: sampleFile } = useFileQuery('history_register_sample.xlsx');
const sampleFileUrl = useMemo(() => {
if (sampleFile) {
return URL.createObjectURL(sampleFile);
}
return '';
}, [sampleFile]);

const { mutate: registerHistories } = useRegisterHistoryInBatchMutation();

return (
<>
<div className="flex items-center rounded-sm border-[1px] border-admin-border bg-admin-background-light px-5 py-3 text-sm">
<p className="flex flex-1 justify-center gap-1">
점수 산정 기준 표 - <Image src="/images/admin/pdf_icon.svg" alt="pdf" width="16" height="16" />
<a className="pl-[0.5px] text-red-500 underline underline-offset-4" href={standardFileUrl} download>
<a
className="pl-[0.5px] text-red-500 underline underline-offset-4"
href={process.env.NEXT_PUBLIC_FILE_URL + '/history_standard.pdf'}
download
>
점수산정파일.pdf
</a>
</p>
<p className="flex flex-1 justify-center gap-1">
일괄등록 파일 예시 - <Image src="/images/admin/xlsx_icon.svg" alt="xlsx" width="16" height="16" />
<a className="pl-[0.5px] text-green-500 underline underline-offset-4" href={sampleFileUrl} download>
<a
className="pl-[0.5px] text-green-500 underline underline-offset-4"
href={process.env.NEXT_PUBLIC_FILE_URL + '/history_register_sample.xlsx'}
download
>
sample.xlsx
</a>
</p>
Expand Down
7 changes: 0 additions & 7 deletions frontend/src/lib/api/server.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,3 @@ export async function getMilestoneHistory(historyId: number) {
const response = await server.get<MilestoneHistoryDto>(`/admin/milestones/histories/${historyId}`);
return response?.data;
}

export async function getFile(fileName: string | null) {
const response = await server.get<Blob>(`/files/${fileName}`, {
responseType: 'blob',
});
return response.data;
}
13 changes: 0 additions & 13 deletions frontend/src/lib/hooks/useApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,6 @@ export function useStudentMembersQuery() {
});
}

export function useFileQuery(fileName: string | null) {
return useAxiosQuery({
queryKey: QueryKeys.FILE(fileName),
queryFn: async (): Promise<Blob | null> => {
const response = await client.get(`/files/${fileName}`, { responseType: 'blob' });
if (response?.status !== 200) {
return null;
}
return response?.data;
},
});
}

export function useMilestoneHistoryCreateMutation() {
return useAxiosMutation({
mutationFn: async ({ milestoneId, description, count, file, activatedAt }: MilestoneHistoryCreateDto) => {
Expand Down