Skip to content

Commit 5785914

Browse files
authored
Merge pull request #157 from umc-commit/feat/152-s3-upload-request
[FEAT] 커미션 신청폼 이미지 업로드 S3 연결
2 parents f3333a8 + 0c6d29f commit 5785914

File tree

3 files changed

+34
-65
lines changed

3 files changed

+34
-65
lines changed

src/commission/controller/commission.controller.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,5 @@ export const getCommissionReport = async (req, res, next) => {
113113
} catch (err) {
114114
next(err);
115115
}
116-
};
116+
};
117+

src/commission/service/commission.service.js

Lines changed: 29 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import multer from 'multer';
22
import path from 'path';
3-
import fs from 'fs';
3+
import { uploadToS3, deleteFromS3 } from '../../s3.upload.js';
44
import { CommissionRepository } from "../repository/commission.repository.js";
55
import { RequestRepository } from "../../request/repository/request.repository.js";
66
import {
@@ -17,38 +17,14 @@ import {
1717
} from "../../common/errors/commission.errors.js";
1818

1919
export const CommissionService = {
20-
// 업로드 디렉토리 설정
21-
uploadDir: path.join(process.cwd(), 'uploads', 'request-images'),
2220

2321
/**
24-
* 업로드 디렉토리 생성
22+
* S3 업로드를 위한 multer 설정
2523
*/
26-
ensureUploadDir() {
27-
if (!fs.existsSync(this.uploadDir)) {
28-
fs.mkdirSync(this.uploadDir, { recursive: true });
29-
console.log(`업로드 디렉토리 생성: ${this.uploadDir}`);
30-
}
31-
},
32-
33-
/**
34-
* multer 저장소 설정
35-
*/
36-
storage: multer.diskStorage({
37-
destination: function(req, file, cb) {
38-
const uploadDir = path.join(process.cwd(), 'uploads', 'request-images');
39-
cb(null, uploadDir);
40-
},
41-
filename: function(req, file, cb) {
42-
// 파일명: request_현재시간_랜덤값.확장자
43-
const timestamp = Date.now();
44-
const random = Math.round(Math.random() * 1E9);
45-
const ext = path.extname(file.originalname);
46-
cb(null, `request_${timestamp}_${random}${ext}`);
47-
}
48-
}),
24+
storage: multer.memoryStorage(),
4925

5026
/**
51-
* 파일 필터 (이미지만 허용)
27+
* 파일 필터
5228
*/
5329
fileFilter(req, file, cb) {
5430
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png'];
@@ -67,72 +43,64 @@ export const CommissionService = {
6743
* multer 인스턴스 생성
6844
*/
6945
getUploadMiddleware() {
70-
// 업로드 디렉토리 확인
71-
this.ensureUploadDir();
72-
7346
return multer({
7447
storage: this.storage,
7548
fileFilter: this.fileFilter,
7649
limits: {
77-
fileSize: 5 * 1024 * 1024 // 5MB 제한
50+
fileSize: 10 * 1024 * 1024
7851
}
7952
}).single('image');
8053
},
8154

8255
/**
83-
* 이미지 업로드 처리
56+
* S3 이미지 업로드 처리
8457
*/
8558
async uploadRequestImage(file) {
8659
try {
8760
// 1. 파일 존재 여부 확인
8861
if (!file) {
89-
throw new ImageUploadFailedError('파일이 업로드되지 않았습니다');
62+
throw new ImageUploadFailedError({ reason: '파일이 업로드되지 않았습니다' });
9063
}
9164

92-
// 2. 파일 크기 추가 검증
93-
if (file.size > 5 * 1024 * 1024) {
94-
this.deleteFile(file.path);
95-
throw new FileSizeExceededError({
96-
maxSize: '5MB',
97-
receivedSize: `${Math.round(file.size / 1024 / 1024 * 100) / 100}MB`
98-
});
65+
// 2. 파일 크기 검증
66+
if (file.size > 10 * 1024 * 1024) {
67+
throw new FileSizeExceededError({ fileSize: file.size });
68+
}
69+
70+
// 3. 파일 확장자 검증
71+
const ext = path.extname(file.originalname).toLowerCase().replace('.', '');
72+
if (!['jpeg', 'jpg', 'png'].includes(ext)) {
73+
throw new UnsupportedImageFormatError({ fileType: file.mimetype });
9974
}
10075

101-
// 3. 파일 URL 생성
102-
const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
103-
const imageUrl = `${baseUrl}/uploads/request-images/${file.filename}`;
76+
// 4. S3 업로드 (requests 폴더에 저장)
77+
const imageUrl = await uploadToS3(file.buffer, 'requests', ext);
10478

105-
// 4. 성공 응답 반환
10679
return {
10780
image_url: imageUrl,
10881
file_size: file.size,
10982
file_type: file.mimetype
11083
};
11184

11285
} catch (error) {
113-
// 오류 발생 시 업로드된 파일 삭제
114-
if (file && file.path) {
115-
this.deleteFile(file.path);
116-
}
11786
throw error;
11887
}
11988
},
12089

12190
/**
122-
* 파일 삭제 헬퍼 메서드
91+
* S3 이미지 삭제
12392
*/
124-
deleteFile(filePath) {
93+
async deleteS3Image(imageUrl) {
12594
try {
126-
if (fs.existsSync(filePath)) {
127-
fs.unlinkSync(filePath);
128-
console.log(`파일 삭제: ${filePath}`);
95+
if (imageUrl && imageUrl.includes('amazonaws.com')) {
96+
await deleteFromS3(imageUrl);
97+
console.log(`S3 이미지 삭제 완료: ${imageUrl}`);
12998
}
13099
} catch (error) {
131-
console.error('파일 삭제 실패:', error);
100+
console.error('S3 이미지 삭제 실패:', error);
132101
}
133102
},
134103

135-
136104
/**
137105
* 커미션 게시글 상세글 조회
138106
*/
@@ -627,39 +595,39 @@ export const CommissionService = {
627595
// 캐릭터 데이터
628596
CHARACTER_DATA: [
629597
{
630-
image: "https://example.com/character1.png",
598+
image: `https://${process.env.AWS_S3_BUCKET_NAME}.s3.dualstack.${process.env.AWS_REGION}.amazonaws.com/reports/reportType1.png`,
631599
quote: {
632600
title: "커미션계의 VIP",
633601
description: "\"커미션계의 큰 손 등장!\" 덕분에 작가님들의 창작 활동이 풍요로워졌어요."
634602
},
635603
condition: "월 사용 포인트 15만포인트 이상"
636604
},
637605
{
638-
image: "https://example.com/character2.png",
606+
image: `https://${process.env.AWS_S3_BUCKET_NAME}.s3.dualstack.${process.env.AWS_REGION}.amazonaws.com/reports/reportType2.png`,
639607
quote: {
640608
title: "작가 덕후 신청자",
641609
description: "\"이 작가님만큼은 믿고 맡긴다!\" 단골의 미덕을 지닌 당신, 작가님도 감동했을 거예요."
642610
},
643611
condition: "같은 작가에게 3회 이상 신청"
644612
},
645613
{
646-
image: "https://example.com/character3.png",
614+
image: `https://${process.env.AWS_S3_BUCKET_NAME}.s3.dualstack.${process.env.AWS_REGION}.amazonaws.com/reports/reportType3.png`,
647615
quote: {
648616
title: "호기심 대장 신청자",
649617
description: "호기심이 가득해서, 언제나 새로운 작가를 탐색해요."
650618
},
651619
condition: "서로 다른 작가 5명 이상에게 커미션을 신청"
652620
},
653621
{
654-
image: "https://example.com/character4.png",
622+
image: `https://${process.env.AWS_S3_BUCKET_NAME}.s3.dualstack.${process.env.AWS_REGION}.amazonaws.com/reports/reportType4.png`,
655623
quote: {
656624
title: "숨겨진 보석 발굴가",
657625
description: "\"빛나는 원석을 내가 발견했다!\" 성장하는 작가님들의 첫걸음을 함께한 당신, 멋져요."
658626
},
659627
condition: "팔로워 수가 0명인 작가에게 신청 2회 이상"
660628
},
661629
{
662-
image: "https://example.com/character5.png",
630+
image: `https://${process.env.AWS_S3_BUCKET_NAME}.s3.dualstack.${process.env.AWS_REGION}.amazonaws.com/reports/reportType5.png`,
663631
quote: {
664632
title: "빠른 피드백러",
665633
description: "\"작가님, 이번 커미션 최고였어요!\" 정성 가득한 피드백으로 건강한 커미션 문화를 만들어가요."

src/common/swagger/commission.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@
410410
"image": {
411411
"type": "string",
412412
"format": "binary",
413-
"description": "업로드할 이미지 파일 (JPG, PNG, 최대 5MB)"
413+
"description": "업로드할 이미지 파일 (JPG, PNG, 최대 10MB)"
414414
}
415415
},
416416
"required": ["image"]
@@ -460,8 +460,8 @@
460460
"data": {
461461
"type": "object",
462462
"properties": {
463-
"maxSize": { "type": "string", "example": "5MB" },
464-
"receivedSize": { "type": "string", "example": "7.2MB" }
463+
"maxSize": { "type": "string", "example": "10MB" },
464+
"receivedSize": { "type": "string", "example": "15MB" }
465465
}
466466
}
467467
}

0 commit comments

Comments
 (0)