Skip to content

Commit 30b5d60

Browse files
authored
Merge pull request #24 from hackathon-soa/refactor/#21
refactor : 회원가입 API 수정 및 이미지 업로드 API 추가 구현
2 parents 29ef75a + 6a27cc5 commit 30b5d60

File tree

11 files changed

+81
-29
lines changed

11 files changed

+81
-29
lines changed

src/main/java/hackathon/soa/config/AmazonConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class AmazonConfig {
4242
@Value("stories")
4343
private String storiesPath; // S3 내 테스트 관련 파일 저장 디렉토리 경로 (폴더명: stories)
4444

45+
@Value("verification")
46+
private String verificationPath; // S3 내 테스트 관련 파일 저장 디렉토리 경로 (폴더명: tests)
47+
4548
//!! s3에 어떤 디렉토리를 만들고, 그안에 뭘 저장하고 싶다면!!
4649
//1. aws 콘솔에서 s3 디렉토리 생성
4750
//2. AmazonConfig에 private String ~~Path 변수 생성

src/main/java/hackathon/soa/domain/auth/AuthController.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import jakarta.validation.Valid;
88
import jakarta.validation.constraints.Size;
99
import lombok.RequiredArgsConstructor;
10+
import org.springframework.http.MediaType;
1011
import org.springframework.web.bind.annotation.*;
12+
import org.springframework.web.multipart.MultipartFile;
1113

1214
@RestController
1315
@RequestMapping("/api/auth")
@@ -28,6 +30,19 @@ public ApiResponse<AuthResponseDTO.SignupResponseDTO> register(
2830
return ApiResponse.onSuccess(result);
2931
}
3032

33+
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
34+
@Operation(
35+
summary = "신원인증 이미지 업로드",
36+
description = "사용자의 신원정보를 증빙할 수 있는 이미지를 받아서 S3에 업로드합니다."
37+
)
38+
public ApiResponse<AuthResponseDTO.UploadResponseDTO> upload(
39+
@RequestParam Long memberId,
40+
MultipartFile file
41+
) {
42+
AuthResponseDTO.UploadResponseDTO result = authService.uploadVerification(memberId, file);
43+
return ApiResponse.onSuccess(result);
44+
}
45+
3146

3247
@GetMapping("/check")
3348
@Operation(

src/main/java/hackathon/soa/domain/auth/AuthConverter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public static AuthResponseDTO.SignupResponseDTO toSignUpResponseDTO(Member saved
1212
;
1313
}
1414

15+
1516
public static AuthResponseDTO.DuplicateCheckResponseDTO toDuplicateCheckResponseDTO(boolean isExists) {
1617
return AuthResponseDTO.DuplicateCheckResponseDTO.builder()
1718
.isAvailable(!isExists)
@@ -26,4 +27,12 @@ public static AuthResponseDTO.LoginResponseDTO toLoginResponseDTO(Member member,
2627
.accessToken(accessToken)
2728
.build();
2829
}
30+
31+
32+
public static AuthResponseDTO.UploadResponseDTO toUploadResponseDTO(Member member, String imageUrl) {
33+
return AuthResponseDTO.UploadResponseDTO.builder()
34+
.userId(member.getId())
35+
.verificationImageUrl(imageUrl)
36+
.build();
37+
}
2938
}

src/main/java/hackathon/soa/domain/auth/AuthService.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33
import hackathon.soa.common.JwtUtil;
44
import hackathon.soa.common.apiPayload.code.status.ErrorStatus;
55
import hackathon.soa.common.apiPayload.exception.AuthHandler;
6+
import hackathon.soa.config.AmazonConfig;
67
import hackathon.soa.domain.auth.dto.AuthRequestDTO;
78
import hackathon.soa.domain.auth.dto.AuthResponseDTO;
89
import hackathon.soa.domain.member.MemberConverter;
910
import hackathon.soa.domain.member.MemberRepository;
11+
import hackathon.soa.domain.s3.AmazonS3Manager;
1012
import hackathon.soa.entity.Member;
1113
import lombok.RequiredArgsConstructor;
1214
import org.springframework.security.crypto.password.PasswordEncoder;
1315
import org.springframework.stereotype.Service;
1416
import org.springframework.transaction.annotation.Transactional;
17+
import org.springframework.web.multipart.MultipartFile;
18+
19+
import java.util.UUID;
1520

1621
@Service
1722
@Transactional
@@ -21,6 +26,8 @@ public class AuthService {
2126
private final MemberRepository memberRepository;
2227
private final PasswordEncoder passwordEncoder;
2328
private final JwtUtil jwtUtil;
29+
private final AmazonS3Manager amazonS3Manager;
30+
private final AmazonConfig amazonConfig;
2431

2532
public AuthResponseDTO.SignupResponseDTO register(AuthRequestDTO.SignupRequestDTO request) {
2633
// 1) 아이디 중복 체크
@@ -69,4 +76,31 @@ public AuthResponseDTO.LoginResponseDTO login(AuthRequestDTO.LoginRequestDTO req
6976
// 45) 응답 DTO 생성
7077
return AuthConverter.toLoginResponseDTO(member, accessToken);
7178
}
79+
80+
public AuthResponseDTO.UploadResponseDTO uploadVerification(Long memberId, MultipartFile file) {
81+
// 1) Member 조회
82+
Member member = memberRepository.findById(memberId)
83+
.orElseThrow(() -> new IllegalArgumentException("해당 ID의 회원이 존재하지 않습니다: " + memberId));
84+
85+
86+
// 2) 파일명 생성
87+
String keyName = generateVerificationImageKeyName(file.getOriginalFilename());
88+
89+
90+
// 3) S3에 업로드
91+
String imageUrl = amazonS3Manager.uploadFile(keyName, file);
92+
93+
94+
// 4) Member의 verificationImageUrl 업데이트
95+
member.updateVerificationImageUrl(imageUrl);
96+
97+
98+
// 5) 응답 DTO 생성
99+
return AuthConverter.toUploadResponseDTO(member, imageUrl);
100+
}
101+
102+
private String generateVerificationImageKeyName(String originalFileName) {
103+
String ext = originalFileName.substring(originalFileName.lastIndexOf(".")); // .jpg, .png 등
104+
return amazonConfig.getVerificationPath() + "/" + UUID.randomUUID() + ext; // verification/uuid.jpg
105+
}
72106
}

src/main/java/hackathon/soa/domain/auth/dto/AuthRequestDTO.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ public static class SignupRequestDTO {
4747
@NotBlank(message = "장애 유형은 필수입니다")
4848
@Pattern(regexp = "^\\[.*\\]$", message = "장애 유형은 JSON 배열 형식이어야 합니다")
4949
private String disabilityType;
50-
51-
@NotNull(message = "프로필 이미지는 필수입니다")
52-
private Integer profileImage;
5350
}
5451

5552
@Builder

src/main/java/hackathon/soa/domain/auth/dto/AuthResponseDTO.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public static class SignupResponseDTO {
1515
private String appId;
1616
}
1717

18+
@Builder
19+
@Getter
20+
@NoArgsConstructor
21+
@AllArgsConstructor
22+
public static class UploadResponseDTO {
23+
private Long userId;
24+
private String verificationImageUrl;
25+
}
26+
1827
@Builder
1928
@Getter
2029
@NoArgsConstructor

src/main/java/hackathon/soa/domain/auth/course/CourseRepository.java renamed to src/main/java/hackathon/soa/domain/course/CourseRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package hackathon.soa.domain.auth.course;
1+
package hackathon.soa.domain.course;
22

33
import hackathon.soa.entity.Course;
44
import org.springframework.data.jpa.repository.JpaRepository;

src/main/java/hackathon/soa/domain/home/HomeService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import hackathon.soa.common.apiPayload.code.status.ErrorStatus;
44
import hackathon.soa.common.apiPayload.exception.GeneralException;
5-
import hackathon.soa.domain.auth.course.CourseRepository;
5+
import hackathon.soa.domain.course.CourseRepository;
66
import hackathon.soa.domain.home.dto.HomeResponseDTO;
77
import hackathon.soa.entity.Course;
88
import jakarta.transaction.Transactional;
99
import lombok.RequiredArgsConstructor;
1010
import org.springframework.stereotype.Service;
1111

12-
import java.util.Comparator;
1312
import java.util.List;
1413
import java.util.stream.Collectors;
1514

src/main/java/hackathon/soa/domain/member/MemberConverter.java

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,6 @@ public static Member toMember(AuthRequestDTO.SignupRequestDTO request, PasswordE
1010
// 성별 변환
1111
Gender gender = "남성".equals(request.getGender()) ? Gender.MALE : Gender.FEMALE;
1212

13-
// 프로필 이미지 변환
14-
String profileImageUrl;
15-
switch (request.getProfileImage()) {
16-
case 1:
17-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit1.png";
18-
break;
19-
case 2:
20-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit2.png";
21-
break;
22-
case 3:
23-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit3.png";
24-
break;
25-
case 4:
26-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit4.png";
27-
break;
28-
case 5:
29-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit5.png";
30-
break;
31-
default:
32-
profileImageUrl = "https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit1.png";
33-
}
34-
3513
return Member.builder()
3614
.appId(request.getAppId())
3715
.password(passwordEncoder.encode(request.getPassword()))
@@ -41,7 +19,7 @@ public static Member toMember(AuthRequestDTO.SignupRequestDTO request, PasswordE
4119
.birth(request.getBirth())
4220
.gender(gender)
4321
.disabilityType(request.getDisabilityType())
44-
.profileImageUrl(profileImageUrl)
22+
.profileImageUrl("https://umc-hack-demo-bucket.s3.ap-northeast-2.amazonaws.com/rabbit1.png")
4523
.mileage(0)
4624
.build();
4725
}

src/main/java/hackathon/soa/domain/story/StoryController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public ApiResponse<String> uploadImage(
3535
String url = testService.uploadTestImage(image, userId);
3636
return ApiResponse.onSuccess(url);
3737
}
38+
3839
@Operation(summary = "스토리 목록 조회", description = "스토리 목록을 10개씩 페이지네이션으로 반환합니다.")
3940
@GetMapping
4041
public ApiResponse<Page<StoryResponseDTO.StoryListDTO>> getStoryList(

0 commit comments

Comments
 (0)