Skip to content
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ out/
build/generated/
application-local.properties
application-test.properties
application-s3.properties
application-s3.properties
test_img_dir
13 changes: 9 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,19 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'

//FCM 추가
implementation 'com.google.firebase:firebase-admin:9.2.0'

// Health Check
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// AWS 서비스 연동을 쉽게 도와주는 Spring Cloud 통합 패키지
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

compileOnly 'org.projectlombok:lombok'
// runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'

runtimeOnly 'com.mysql:mysql-connector-j'
// runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
Expand All @@ -65,4 +70,4 @@ sourceSets {

tasks.named('test') {
useJUnitPlatform()
}
}
6 changes: 3 additions & 3 deletions deploy/local.init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ INSERT INTO users (user_id, created_date, updated_date, activity_area, birthday,
VALUES (default, null, null, 'GYEONGGI', 'birthday', 'email', 'fcm_token', 'MAN', 180, null, 'BOTH', 'MANAGER', 'name', 'password', 'phone', 'refresh_token12345678910', 'USER', 'GK', 70);

INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id)
VALUES (default, null, null, 'PRESIDENT', '2024-12-11', 2, 2, 1, 1);
VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 1, 1);
---- 3번 동아리에 가입한 유저들
INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id)
VALUES (default, null, null, 'VICE_PRESIDENT', '2024-12-11', 2, 2, 3, 3);
VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 3);
INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id)
VALUES (default, null, null, 'PRESIDENT', '2024-12-11', 2, 2, 3, 4);
VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 4);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.example.moim.club.dto.request;

import com.example.moim.global.util.ValidFile;
import com.example.moim.global.util.file.exception.ValidFile;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.example.moim.club.service;

import com.example.moim.club.dto.request.*;
import com.example.moim.club.dto.response.ClubOutput;
import com.example.moim.club.dto.response.ClubSaveOutput;
import com.example.moim.club.dto.response.ClubUpdateOutput;
import com.example.moim.club.dto.response.UserClubOutput;
Expand All @@ -14,8 +13,8 @@
import com.example.moim.club.repository.UserClubRepository;
import com.example.moim.global.enums.ClubRole;
import com.example.moim.global.exception.ResponseCode;
import com.example.moim.global.util.FileStore;
import com.example.moim.global.util.TextUtils;
import com.example.moim.global.util.file.service.FileService;
import com.example.moim.notification.dto.ClubJoinEvent;
import com.example.moim.user.entity.User;
import com.example.moim.user.repository.UserRepository;
Expand All @@ -36,13 +35,13 @@ public class ClubCommandServiceImpl implements ClubCommandService {
private final ClubRepository clubRepository;
private final UserClubRepository userClubRepository;
private final UserRepository userRepository;
private final FileStore fileStore;
private final FileService fileService;
private final ClubSearchRepository clubSearchRepository;
private final ApplicationEventPublisher eventPublisher;

@Transactional
public ClubSaveOutput saveClub(User user, ClubInput clubInput) throws IOException {
Club club = clubRepository.save(Club.createClub(clubInput, fileStore.storeFile(clubInput.getProfileImg())));
Club club = clubRepository.save(Club.createClub(clubInput, fileService.upload(clubInput.getProfileImg(), "/club-profile")));
// 검색을 위한 저장
saveClubSearch(club);

Expand All @@ -65,7 +64,7 @@ public ClubUpdateOutput updateClub(User user, ClubUpdateInput clubUpdateInput, L
throw new ClubControllerAdvice(ResponseCode.CLUB_PASSWORD_INCORRECT);
}

club.updateClub(clubUpdateInput, fileStore.storeFile(clubUpdateInput.getProfileImg()));
club.updateClub(clubUpdateInput, fileService.upload(clubUpdateInput.getProfileImg(), "/club-profile"));
// 검색 정보 동기화를 위한 처리
club.getClubSearch().updateFrom(club);
List<UserClubOutput> userList = userClubRepository.findAllByClub(club).stream().map(UserClubOutput::new).toList();
Expand Down
41 changes: 0 additions & 41 deletions src/main/java/com/example/moim/global/util/FileStore.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.moim.global.util.file.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Slf4j
@Profile("!test")
@Configuration
public class AwsS3Config {

@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public BasicAWSCredentials basicAWSCredentials() {
return new BasicAWSCredentials(accessKey, secretKey);
}

@Bean
public AmazonS3 amazonS3(BasicAWSCredentials basicAWSCredentials) {
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.moim.global.util.file.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import java.io.File;

@Slf4j
@Profile("test")
@Configuration
public class LocalFileConfig {

@Value("${local.upload.directory}")
private String fileUploadDir;

// @Bean
// public File file() {
//
// File directory = new File(fileUploadDir);
//
// if (!directory.exists()) {
// directory.mkdir();
// }
//
// return directory;
// }
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.example.moim.global.util;
package com.example.moim.global.util.file.exception;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.web.multipart.MultipartFile;

public class ValidFileValidator implements ConstraintValidator<ValidFile, MultipartFile> {
public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {

@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.moim.global.util;
package com.example.moim.global.util.file.exception;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
Expand All @@ -10,7 +10,7 @@

@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidFileValidator.class)
@Constraint(validatedBy = FileValidator.class)
public @interface ValidFile {
String message() default "유효하지 않은 파일입니다.";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.moim.global.util.file.exception.advice;

import com.example.moim.global.exception.GeneralException;
import com.example.moim.global.exception.ResponseCode;

public class AwsS3ControllerAdvice extends GeneralException {
public AwsS3ControllerAdvice(ResponseCode responseCode) {
super(responseCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.moim.global.util.file.exception.advice;

import com.example.moim.global.exception.GeneralException;
import com.example.moim.global.exception.ResponseCode;

public class LocalFileControllerAdvice extends GeneralException {
public LocalFileControllerAdvice(ResponseCode responseCode) {
super(responseCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.moim.global.util.file.model;

import lombok.*;

@Builder
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class AwsS3 {

private String key;
private String path;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.example.moim.global.util.file.service;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.example.moim.global.exception.ResponseCode;
import com.example.moim.global.util.file.model.AwsS3;
import com.example.moim.global.util.file.exception.advice.AwsS3ControllerAdvice;
import com.example.moim.global.util.uuid.UuidHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Slf4j
//@Service
//@Profile("!test")
@RequiredArgsConstructor
public class AwsS3FileService implements FileService {

private final AmazonS3 amazonS3;
private final UuidHolder uuidHolder;

@Value("${cloud.aws.s3.bucket}")
private String bucket;

public String upload(MultipartFile multipartFile, String directoryName) {

// 확장자 가져오기
String originalFilename = multipartFile.getOriginalFilename();
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));

String fileName = uuidHolder.randomUuid() + extension;
String key = directoryName + "/" + fileName;
log.info("upload key : {}", key);

// 메타 데이터 설정
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(multipartFile.getSize());
metadata.setContentType(multipartFile.getContentType());

try {
// 파일 업로드
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, key, multipartFile.getInputStream(), metadata)
.withCannedAcl(CannedAccessControlList.PublicRead); // Public Read 권한 설정
amazonS3.putObject(putObjectRequest);
} catch (IOException e) {
throw new AwsS3ControllerAdvice(ResponseCode.S3_UPLOAD_FAIL);
}

String path = amazonS3.getUrl(bucket, key).toString();
log.info("upload path : {}", path);

AwsS3 result = AwsS3
.builder()
.key(key)
.path(path)
.build();

return result.getPath();
}

public void remove(String path) {

if (!amazonS3.doesObjectExist(bucket, path)) {
log.error("{} 이 존재하지 않습니다.", path);
throw new AwsS3ControllerAdvice(ResponseCode.S3_PATH_NOT_FOUND);
}
amazonS3.deleteObject(bucket, path);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.moim.global.util.file.service;

import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

public interface FileService {
String upload(MultipartFile multipartFile, String directoryName) throws IOException;
void remove(String path);
}
Loading