Skip to content

Commit

Permalink
GETP-200 refactor: member 컴포넌트의 profile image 기능을 application 모듈로 리팩토링 (
Browse files Browse the repository at this point in the history
  • Loading branch information
scv1702 committed Aug 21, 2024
1 parent 9c3e7b1 commit fe1f682
Show file tree
Hide file tree
Showing 13 changed files with 85 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import es.princip.getp.api.controller.ApiResponse.ApiSuccessResult;
import es.princip.getp.api.controller.member.command.dto.response.ProfileImageResponse;
import es.princip.getp.api.security.details.PrincipalDetails;
import es.princip.getp.application.member.command.ChangeProfileImageCommand;
import es.princip.getp.application.member.port.in.ChangeProfileImageUseCase;
import es.princip.getp.application.member.command.RegisterProfileImageCommand;
import es.princip.getp.application.member.port.in.ProfileImageUseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -22,7 +22,7 @@
@RequestMapping("/member/me")
public class MyMemberController {

private final ChangeProfileImageUseCase changeProfileImageUseCase;
private final ProfileImageUseCase profileImageUseCase;

/**
* 내 프로필 이미지 등록
Expand All @@ -38,8 +38,8 @@ public ResponseEntity<ApiSuccessResult<ProfileImageResponse>> uploadProfileImage
@RequestPart final MultipartFile image
) {
final Long memberId = principalDetails.getMember().getMemberId();
final ChangeProfileImageCommand command = new ChangeProfileImageCommand(memberId, image);
final String profileImageUri = changeProfileImageUseCase.changeProfileImage(command);
final RegisterProfileImageCommand command = new RegisterProfileImageCommand(memberId, image);
final String profileImageUri = profileImageUseCase.registerProfileImage(command);
final ProfileImageResponse response = new ProfileImageResponse(profileImageUri);
return ApiResponse.success(HttpStatus.CREATED, response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import org.springframework.web.multipart.MultipartFile;

public record ChangeProfileImageCommand(
public record RegisterProfileImageCommand(
Long memberId,
MultipartFile image
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package es.princip.getp.domain.member.service;
package es.princip.getp.application.member.exception;

import es.princip.getp.common.exception.ApiErrorException;
import es.princip.getp.common.exception.ErrorDescription;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package es.princip.getp.application.member.port.in;

import es.princip.getp.application.member.command.RegisterProfileImageCommand;

public interface ProfileImageUseCase {

String registerProfileImage(RegisterProfileImageCommand command);
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
package es.princip.getp.application.member.service;

import es.princip.getp.application.member.command.ChangeProfileImageCommand;
import es.princip.getp.application.member.command.EditMemberCommand;
import es.princip.getp.application.member.port.in.ChangeProfileImageUseCase;
import es.princip.getp.application.member.port.in.EditMemberUseCase;
import es.princip.getp.application.member.port.out.LoadMemberPort;
import es.princip.getp.application.member.port.out.UpdateMemberPort;
import es.princip.getp.domain.member.model.Member;
import es.princip.getp.domain.member.model.ProfileImage;
import es.princip.getp.domain.member.service.ProfileImageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService implements EditMemberUseCase, ChangeProfileImageUseCase {

private final ProfileImageService profileImageService;
public class MemberService implements EditMemberUseCase {

private final UpdateMemberPort updateMemberPort;
private final LoadMemberPort loadMemberPort;
Expand All @@ -33,19 +26,4 @@ public void editMember(final EditMemberCommand command) {
member.edit(command.nickname(), command.phoneNumber());
updateMemberPort.update(member);
}

@Override
@Transactional
public String changeProfileImage(final ChangeProfileImageCommand command) {
final Long memberId = command.memberId();
final MultipartFile image = command.image();
final Member member = loadMemberPort.loadBy(memberId);
if (member.hasProfileImage()) {
profileImageService.deleteProfileImage(member.getProfileImage());
}
ProfileImage profileImage = profileImageService.saveProfileImage(member, image);
member.changeProfileImage(profileImage);
updateMemberPort.update(member);
return profileImage.getUrl();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package es.princip.getp.domain.member.service;
package es.princip.getp.application.member.service;

import es.princip.getp.application.member.command.RegisterProfileImageCommand;
import es.princip.getp.application.member.exception.FailedToSaveProfileImageException;
import es.princip.getp.application.member.port.in.ProfileImageUseCase;
import es.princip.getp.application.member.port.out.LoadMemberPort;
import es.princip.getp.application.member.port.out.UpdateMemberPort;
import es.princip.getp.common.util.ImageUtil;
import es.princip.getp.domain.member.model.Member;
import es.princip.getp.domain.member.model.ProfileImage;
import es.princip.getp.storage.application.ImageStorage;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
Expand All @@ -18,9 +24,12 @@

@Service
@RequiredArgsConstructor
public class ProfileImageService {
class ProfileImageService implements ProfileImageUseCase {

public static final String PROFILE_IMAGE_PREFIX = "profile";
private static final String PROFILE_IMAGE_PREFIX = "profile";

private final LoadMemberPort loadMemberPort;
private final UpdateMemberPort updateMemberPort;

private final ImageStorage imageStorage;

Expand All @@ -32,7 +41,22 @@ public class ProfileImageService {
"image/x-windows-bmp"
);

public ProfileImage saveProfileImage(final Member member, final MultipartFile image) {
@Override
@Transactional
public String registerProfileImage(final RegisterProfileImageCommand command) {
final Long memberId = command.memberId();
final Member member = loadMemberPort.loadBy(memberId);
if (member.hasProfileImage()) {
deleteProfileImage(member.getProfileImage());
}
final MultipartFile image = command.image();
final ProfileImage profileImage = saveProfileImage(member, image);
member.registerProfileImage(profileImage);
updateMemberPort.update(member);
return profileImage.getUrl();
}

private ProfileImage saveProfileImage(final Member member, final MultipartFile image) {
if (!whiteImageExtensionList.contains(image.getContentType())) {
throw new FailedToSaveProfileImageException();
}
Expand All @@ -51,7 +75,7 @@ private Path getPathToSaveProfileImage(final Member member, final MultipartFile
return Paths.get(memberId).resolve(PROFILE_IMAGE_PREFIX).resolve(fileName);
}

public void deleteProfileImage(final ProfileImage profileImage) {
private void deleteProfileImage(final ProfileImage profileImage) {
imageStorage.deleteImage(URI.create(profileImage.getUrl()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import es.princip.getp.domain.member.model.Email;
import es.princip.getp.domain.member.model.Member;
import es.princip.getp.domain.member.model.ServiceTermAgreementData;
import es.princip.getp.domain.member.service.AlreadyUsedEmailException;
import es.princip.getp.domain.serviceTerm.model.ServiceTerm;
import es.princip.getp.domain.serviceTerm.model.ServiceTermTag;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -46,7 +45,7 @@ public void sendEmailVerificationCodeForSignUp(final Email email) {
public void signUp(final SignUpCommand command) {
emailVerificationService.verifyEmail(command.email(), command.verificationCode());
if (checkMemberPort.existsByEmail(command.email())) {
throw new AlreadyUsedEmailException();
throw new DuplicatedEmailException();
}
final Member member = Member.of(command.email(), command.password(), command.memberType());
member.encodePassword(passwordEncoder);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package es.princip.getp.domain.member.model;
package es.princip.getp.domain.member.exception;

import es.princip.getp.common.exception.BusinessLogicException;
import es.princip.getp.common.exception.ErrorDescription;

class NotAgreedAllRequiredServiceTermException extends BusinessLogicException {
public class NotAgreedAllRequiredServiceTermException extends BusinessLogicException {

private static final String code = "NOT_AGREED_ALL_REQUIRED_SERVICE_TERM";
private static final String message = "모든 필수 서비스 약관에 동의하지 않았습니다.";

NotAgreedAllRequiredServiceTermException() {
public NotAgreedAllRequiredServiceTermException() {
super(ErrorDescription.of(code, message));
}
}
7 changes: 3 additions & 4 deletions src/main/java/es/princip/getp/domain/member/model/Member.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package es.princip.getp.domain.member.model;

import es.princip.getp.common.domain.BaseTimeEntity;
import es.princip.getp.domain.member.exception.NotAgreedAllRequiredServiceTermException;
import es.princip.getp.domain.serviceTerm.model.ServiceTermTag;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand Down Expand Up @@ -85,11 +86,9 @@ public void agreeServiceTerms(
}

/**
* 프로필 이미지를 변경한다.
*
* @param profileImage 변경할 프로필 이미지
* 프로필 이미지를 등록한다.
*/
public void changeProfileImage(final ProfileImage profileImage) {
public void registerProfileImage(final ProfileImage profileImage) {
setProfileImage(profileImage);
}

Expand Down

This file was deleted.

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

import es.princip.getp.api.controller.ControllerTest;
import es.princip.getp.api.security.annotation.WithCustomMockUser;
import es.princip.getp.application.member.command.ChangeProfileImageCommand;
import es.princip.getp.application.member.port.in.ChangeProfileImageUseCase;
import es.princip.getp.application.member.command.RegisterProfileImageCommand;
import es.princip.getp.application.member.port.in.ProfileImageUseCase;
import es.princip.getp.domain.member.model.MemberType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -26,7 +26,7 @@
class MyMemberControllerTest extends ControllerTest {

@Autowired
private ChangeProfileImageUseCase changeProfileImageUseCase;
private ProfileImageUseCase profileImageUseCase;

@Nested
@DisplayName("프로필 업로드")
Expand All @@ -38,7 +38,7 @@ class UploadProfileImage {
@WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE)
@Test
public void uploadProfileImage() throws Exception {
given(changeProfileImageUseCase.changeProfileImage(any(ChangeProfileImageCommand.class)))
given(profileImageUseCase.registerProfileImage(any(RegisterProfileImageCommand.class)))
.willReturn(profileImage(memberId).getUrl());

mockMvc.perform(multipart("/member/me/profile-image")
Expand Down
55 changes: 29 additions & 26 deletions src/test/java/es/princip/getp/domain/member/model/MemberTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package es.princip.getp.domain.member.model;

import es.princip.getp.domain.member.exception.NotAgreedAllRequiredServiceTermException;
import es.princip.getp.domain.member.infra.SimplePasswordEncoder;
import es.princip.getp.domain.serviceTerm.model.ServiceTermTag;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -14,8 +16,6 @@
import static es.princip.getp.domain.member.fixture.PasswordFixture.password;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;

@DisplayName("회원")
Expand Down Expand Up @@ -66,52 +66,55 @@ class 회원은_서비스_약관에_동의할_수_있다 {
}

@Test
void changeProfileImage() {
Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
ProfileImage profileImage = mock(ProfileImage.class);
void 회원은_프로필_이미지를_등록할__있다() {
final Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
final ProfileImage profileImage = mock(ProfileImage.class);

member.changeProfileImage(profileImage);
member.registerProfileImage(profileImage);

assertThat(member.getProfileImage()).isEqualTo(profileImage);
}

@Test
void hasProfileImage_WhenMemberHasProfileImage_ShouldReturn_True() {
Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
ProfileImage profileImage = mock(ProfileImage.class);
@Nested
class 회원은_프로필_이미지를_등록했는지_확인할__있다 {

member.changeProfileImage(profileImage);
@Test
void 프로필_이미지를_등록한_경우() {
final Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
final ProfileImage profileImage = mock(ProfileImage.class);
member.registerProfileImage(profileImage);

assertThat(member.hasProfileImage()).isTrue();
}
assertThat(member.hasProfileImage()).isTrue();
}

@Test
void hasProfileImage_WhenMemberDoesNotHaveProfileImage_ShouldReturn_False() {
Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
@Test
void 프로필_이미지를_등록하지_않은_경우() {
final Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);

assertThat(member.hasProfileImage()).isFalse();
assertThat(member.hasProfileImage()).isFalse();
}
}

@Test
void edit() {
Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
void 회원은_자신의_회원_정보를_수정할__있다() {
final Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);

Nickname newNickname = Nickname.of("new nickname");
PhoneNumber newPhoneNumber = PhoneNumber.of("01087654321");
final Nickname newNickname = Nickname.of("new nickname");
final PhoneNumber newPhoneNumber = PhoneNumber.of("01087654321");
member.edit(newNickname, newPhoneNumber);

assertThat(member.getNickname()).isEqualTo(newNickname);
assertThat(member.getPhoneNumber()).isEqualTo(newPhoneNumber);
}

@Test
void encodePassword() {
Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
PasswordEncoder encoder = mock(PasswordEncoder.class);
given(encoder.encode(anyString())).willReturn(anyString());
void 회원은_비밀번호를_암호화___있다() {
final Member member = Member.of(email(), password(), MemberType.ROLE_PEOPLE);
final PasswordEncoder encoder = new SimplePasswordEncoder();

member.encodePassword(encoder);

assertThat(member.getPassword()).isNotEqualTo(password());
assertThat(encoder.matches(password().getValue(), member.getPassword().getValue()))
.isTrue();
}
}

0 comments on commit fe1f682

Please sign in to comment.