Skip to content

Commit

Permalink
Feat(Member): 닉네임, 프로필 이미지 수정 API 구현 (#51)
Browse files Browse the repository at this point in the history
* Feat(Member): 닉네임 수정api 구현

* Feat(Member): 프로필 이미지 수정 API 구현

* Fix(*): system.out 프린트 제거

* Fix(*): 잘못 옮긴 클래스 패키지 수정

* Feat(Member): image 수정API 결과 응답 추가
  • Loading branch information
InHyeok-J authored Aug 2, 2024
1 parent 0a23a85 commit 143de0d
Show file tree
Hide file tree
Showing 25 changed files with 519 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.jabiseo.auth.dto;

import com.jabiseo.common.EnumValid;
import com.jabiseo.common.validator.EnumValid;
import com.jabiseo.member.domain.OauthServer;
import jakarta.validation.constraints.NotNull;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/api/members/**").authenticated()
.requestMatchers("/api/auth/logout").authenticated()
.requestMatchers("/api/auth/withdraw").authenticated()
.requestMatchers("/**").permitAll()
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jabiseo.common;
package com.jabiseo.common.validator;



Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.jabiseo.common.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;

@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ImageValidator.class)
public @interface ImageValid {

String message() default "잘못된 이미지 형식입니다.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.jabiseo.common.validator;

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

import java.util.List;

public class ImageValidator implements ConstraintValidator<ImageValid, MultipartFile> {

private static final List<String> ALLOW_EXTENSION_LIST = List.of("jpg", "jpeg", "png", "avif", "webp");
private static final int MAX_IMAGE_SIZE = 5 * 1024 * 1024; // 5MB

@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext constraintValidatorContext) {
if (file == null || file.isEmpty()) {
return false;
}

if (file.getSize() > MAX_IMAGE_SIZE) {
return false;
}

String fileExtension = getFileExtension(file.getOriginalFilename());
if (!ALLOW_EXTENSION_LIST.contains(fileExtension)) {
return false;
}

return true;
}

private String getFileExtension(String fileName) {
if (fileName == null || fileName.isEmpty()) {
return "";
}
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jabiseo.common;
package com.jabiseo.common.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
import com.jabiseo.database.exception.PersistenceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
Expand Down Expand Up @@ -41,9 +46,24 @@ public ResponseEntity<?> handleMethodValidationException(HandlerMethodValidation
.body(new ErrorResponse(e.getMessage(), errorCode.getErrorCode()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
ErrorCode errorCode = CommonErrorCode.INVALID_REQUEST_PARAMETER;
log.error(e.getMessage());
StringBuilder errors = new StringBuilder();
e.getBindingResult()
.getFieldErrors()
.forEach((er) -> errors.append(er.getDefaultMessage()));

return ResponseEntity
.status(errorCode.getStatusCode())
.body(new ErrorResponse(errors.toString(), errorCode.getErrorCode()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception e) {
StringBuilder errorMessage = new StringBuilder();
log.error(e.getMessage());
errorMessage.append(e.getMessage())
.append(" ")
.append(CommonErrorCode.INTERNAL_SERVER_ERROR.getMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.jabiseo.member.application.usecase;

import com.jabiseo.member.domain.Member;
import com.jabiseo.member.domain.MemberRepository;
import com.jabiseo.member.dto.UpdateNicknameRequest;
import com.jabiseo.member.dto.UpdateNicknameResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class UpdateNicknameUseCase {

private final MemberRepository memberRepository;

public UpdateNicknameResponse execute(Long memberId, UpdateNicknameRequest request) {
Member member = memberRepository.getReferenceById(memberId);
member.updateNickname(request.nickname());
return UpdateNicknameResponse.of(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.jabiseo.member.application.usecase;

import com.jabiseo.member.domain.Member;
import com.jabiseo.member.domain.MemberRepository;
import com.jabiseo.member.dto.UpdateProfileImageRequest;
import com.jabiseo.member.dto.UpdateProfileImageResponse;
import com.jabiseo.s3.S3Uploader;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
@RequiredArgsConstructor
@Transactional
public class UpdateProfileImageUseCase {

private final MemberRepository memberRepository;
private final S3Uploader s3Uploader;
private static final String PROFILE_IMAGE_PATH = "profile/";

public UpdateProfileImageResponse execute(Long memberId, UpdateProfileImageRequest request) {
Member member = memberRepository.getReferenceById(memberId);
String profileUrl = s3Uploader.upload(request.image(), PROFILE_IMAGE_PATH);
member.updateProfileImage(profileUrl);
return UpdateProfileImageResponse.of(member);
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

import com.jabiseo.config.auth.AuthMember;
import com.jabiseo.config.auth.AuthenticatedMember;
import com.jabiseo.member.dto.FindMyCurrentCertificateResponse;
import com.jabiseo.member.dto.FindMyInfoResponse;
import com.jabiseo.member.dto.UpdateMyCurrentCertificateRequest;
import com.jabiseo.member.application.usecase.FindMyCurrentCertificateUseCase;
import com.jabiseo.member.application.usecase.FindMyInfoUseCase;
import com.jabiseo.member.application.usecase.UpdateMyCurrentCertificateUseCase;
import com.jabiseo.member.application.usecase.*;
import com.jabiseo.member.dto.*;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -22,6 +19,8 @@ public class MemberController {
private final FindMyCurrentCertificateUseCase findMyCurrentCertificateUseCase;

private final UpdateMyCurrentCertificateUseCase updateMyCurrentCertificateUseCase;
private final UpdateNicknameUseCase updateNicknameUseCase;
private final UpdateProfileImageUseCase updateProfileImageUseCase;

@GetMapping
public ResponseEntity<FindMyInfoResponse> findMyInfo(
Expand All @@ -48,4 +47,17 @@ public ResponseEntity<Void> updateMyCertificateStatus(
return ResponseEntity.ok().build();
}

@PatchMapping("/nickname")
public ResponseEntity<UpdateNicknameResponse> updateMyNickname(@AuthenticatedMember AuthMember member, @Valid @RequestBody UpdateNicknameRequest request) {
UpdateNicknameResponse result = updateNicknameUseCase.execute(member.getMemberId(), request);
return ResponseEntity.ok(result);
}

@PatchMapping("/image")
public ResponseEntity<UpdateProfileImageResponse> updateImage(@AuthenticatedMember AuthMember member, @Valid @ModelAttribute UpdateProfileImageRequest request) {
UpdateProfileImageResponse result = updateProfileImageUseCase.execute(member.getMemberId(), request);
return ResponseEntity.ok(result);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.jabiseo.member.dto;

import jakarta.validation.constraints.NotBlank;

public record UpdateNicknameRequest(@NotBlank String nickname) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.jabiseo.member.dto;

import com.jabiseo.member.domain.Member;

public record UpdateNicknameResponse(String nickname,
String email,
String profileImage,
String memberId) {

public static UpdateNicknameResponse of(Member member) {
return new UpdateNicknameResponse(member.getNickname(), member.getEmail(), member.getProfileImage(), member.getId().toString());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.jabiseo.member.dto;

import com.jabiseo.common.validator.ImageValid;
import org.springframework.web.multipart.MultipartFile;

public record UpdateProfileImageRequest(@ImageValid MultipartFile image) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.jabiseo.member.dto;

import com.jabiseo.member.domain.Member;

public record UpdateProfileImageResponse(String nickname,
String email,
String profileImage,
String memberId) {

public static UpdateProfileImageResponse of(Member member) {
return new UpdateProfileImageResponse(member.getNickname(), member.getEmail(), member.getProfileImage(), member.getId().toString());
}
}
6 changes: 6 additions & 0 deletions jabiseo-api/src/main/resources/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ management:
health:
show-components: always

spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB

---
spring:
config:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.jabiseo.member.application.usecase;

import com.jabiseo.member.domain.Member;
import com.jabiseo.member.domain.MemberRepository;
import com.jabiseo.member.dto.UpdateNicknameRequest;
import com.jabiseo.member.dto.UpdateNicknameResponse;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.BDDMockito;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static fixture.MemberFixture.createMember;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;

@DisplayName("닉네임 수정 유스케이스 테스트")
@ExtendWith(MockitoExtension.class)
class UpdateNicknameUseCaseTest {

@InjectMocks
UpdateNicknameUseCase updateNicknameUseCase;

@Mock
MemberRepository memberRepository;

UpdateNicknameRequest request;

@BeforeEach
void setUp() {
request = new UpdateNicknameRequest("newNickname");
}

@Test
@DisplayName("닉네임 수정 성공")
void updateNicknameUseCaseSuccess() {
//given
Member member = createMember(1L);
given(memberRepository.getReferenceById(member.getId())).willReturn(member);

//when
UpdateNicknameResponse result = updateNicknameUseCase.execute(member.getId(), request);

//then
assertThat(result.nickname()).isEqualTo(request.nickname());
}
}
Loading

0 comments on commit 143de0d

Please sign in to comment.