diff --git a/build.gradle b/build.gradle index 14aa9f4a..d249ef71 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-jdbc' runtimeOnly 'com.mysql:mysql-connector-j' + // s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + // test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/src/main/java/com/blog/common/config/S3Config.java b/src/main/java/com/blog/common/config/S3Config.java new file mode 100644 index 00000000..138ba30f --- /dev/null +++ b/src/main/java/com/blog/common/config/S3Config.java @@ -0,0 +1,34 @@ +package com.blog.common.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @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 AmazonS3 amazonS3() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + + return AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } + +} diff --git a/src/main/java/com/blog/workspace/adapter/in/web/AuthController.java b/src/main/java/com/blog/workspace/adapter/in/web/AuthController.java index 93ff6b9b..b9942a38 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/AuthController.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/AuthController.java @@ -12,12 +12,17 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; +import java.io.IOException; + @RestController @RequestMapping("/auth") public class AuthController { + private static final Logger log = LoggerFactory.getLogger(AuthController.class); // 자체 회원가입 private final RegisterUserUseCase registerService; @@ -36,7 +41,7 @@ public AuthController(RegisterUserUseCase registerService, AuthUserUseCase authS /// 자체 회원가입 기능 @PostMapping - public ApiResponse register(@RequestBody @Valid UserRegisterRequest request) { + public ApiResponse register(@ModelAttribute UserRegisterRequest request) throws IOException { registerService.registerUser(request); return ApiResponse.ok("회원 가입 성공했습니다."); diff --git a/src/main/java/com/blog/workspace/adapter/in/web/PostController.java b/src/main/java/com/blog/workspace/adapter/in/web/PostController.java index 6a1ccbd6..ccb89085 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/PostController.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/PostController.java @@ -12,6 +12,8 @@ import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; +import java.io.IOException; + @RestController @RequestMapping("/posts") public class PostController { @@ -24,7 +26,7 @@ public PostController(PostUseCase postService) { // 게시글 작성 @PostMapping - ApiResponse createPost(@RequestUserId Long userId, @RequestBody @Valid PostRequest postRequest) { + ApiResponse createPost(@RequestUserId Long userId, @ModelAttribute @Valid PostRequest postRequest) throws IOException { postService.savePost(postRequest, userId); @@ -52,7 +54,7 @@ ApiResponse> getPostList(@RequestParam(required = false, // 게시글 수정 @PutMapping("/{postId}") - ApiResponse updatePost(@RequestUserId Long userId, @PathVariable Long postId, @RequestBody PostUpdateRequest request) { + ApiResponse updatePost(@RequestUserId Long userId, @PathVariable Long postId, @ModelAttribute PostUpdateRequest request) throws IOException { postService.updatePost(postId, userId, request); diff --git a/src/main/java/com/blog/workspace/adapter/in/web/UserController.java b/src/main/java/com/blog/workspace/adapter/in/web/UserController.java index 07e69506..a1b78ff4 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/UserController.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/UserController.java @@ -9,6 +9,8 @@ import com.blog.workspace.domain.user.User; import org.springframework.web.bind.annotation.*; +import java.io.IOException; + @RestController @RequestMapping("/users") public class UserController { @@ -31,7 +33,7 @@ public ApiResponse getMyInfo(@RequestUserId Long userId) { } @PutMapping() - public ApiResponse updateMyInfo(@RequestUserId Long userId, @RequestBody UserUpdateRequest request) { + public ApiResponse updateMyInfo(@RequestUserId Long userId, @ModelAttribute UserUpdateRequest request) throws IOException { updateService.updateUser(userId, request); return ApiResponse.ok("수정되었습니다."); diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/ContentRequest.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/ContentRequest.java index 736dd912..4ab943e5 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/ContentRequest.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/ContentRequest.java @@ -1,11 +1,19 @@ package com.blog.workspace.adapter.in.web.dto.request; import com.blog.workspace.domain.post.ContentType; +import org.springframework.web.multipart.MultipartFile; public class ContentRequest { private ContentType type; private String content; + private MultipartFile image; + + public ContentRequest(ContentType type, String content, MultipartFile image) { + this.type = type; + this.content = content; + this.image = image; + } /// @Getter public ContentType getType() { @@ -15,4 +23,8 @@ public ContentType getType() { public String getContent() { return content; } + + public MultipartFile getImage() { + return image; + } } diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostRequest.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostRequest.java index ff92a5a2..93574dee 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostRequest.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostRequest.java @@ -12,6 +12,11 @@ public class PostRequest { @NotNull(message = "글 작성은 필수입니다.") private List content; + public PostRequest(String title, List content) { + this.title = title; + this.content = content; + } + public String getTitle() { return title; } diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostUpdateRequest.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostUpdateRequest.java index 2fea057b..cf7d5c06 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostUpdateRequest.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/PostUpdateRequest.java @@ -12,6 +12,14 @@ public class PostUpdateRequest { private List content; + + public PostUpdateRequest(String title, List content) { + this.title = title; + this.content = content; + } + + /**@Getter + */ public String getTitle() { return title; } diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserRegisterRequest.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserRegisterRequest.java index a83c632e..4e161509 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserRegisterRequest.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserRegisterRequest.java @@ -1,6 +1,7 @@ package com.blog.workspace.adapter.in.web.dto.request; import jakarta.validation.constraints.*; +import org.springframework.web.multipart.MultipartFile; public class UserRegisterRequest { @@ -22,7 +23,7 @@ public class UserRegisterRequest { @NotNull(message = "passwordCheck는 반드시 입력해야하는 필수 사항입니다!") private String passwordCheck; - private String imageUrl; + private MultipartFile imageUrl; @NotNull(message = "description는 반드시 입력해야하는 필수 사항입니다!") @Size(max = 30, message = "한 줄 소개는 최대 30글자 입니다.") @@ -32,6 +33,17 @@ public class UserRegisterRequest { // 3월 16일 이전의 경우는 입력이 안되도록 추가 설정 private String birthday; + public UserRegisterRequest(String email, String username, String nickname, String password, String passwordCheck, MultipartFile imageUrl, String description, String birthday) { + this.email = email; + this.username = username; + this.nickname = nickname; + this.password = password; + this.passwordCheck = passwordCheck; + this.imageUrl = imageUrl; + this.description = description; + this.birthday = birthday; + } + // Getter public String getEmail() { @@ -54,7 +66,7 @@ public String getPasswordCheck() { return passwordCheck; } - public String getImageUrl() { + public MultipartFile getImageUrl() { return imageUrl; } diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserUpdateRequest.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserUpdateRequest.java index 11243d3f..a4e18bc3 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserUpdateRequest.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/request/UserUpdateRequest.java @@ -2,6 +2,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import org.springframework.web.multipart.MultipartFile; public class UserUpdateRequest { @@ -16,7 +17,7 @@ public class UserUpdateRequest { @NotBlank(message = "passwordCheck는 반드시 입력해야하는 필수 사항입니다!") private String passwordCheck; - private String imageUrl; + private MultipartFile imageUrl; @NotBlank(message = "description는 반드시 입력해야하는 필수 사항입니다!") private String description; @@ -24,6 +25,15 @@ public class UserUpdateRequest { @NotBlank(message = "descripbirthdaytion는 반드시 입력해야하는 필수 사항입니다!") private String birthday; + public UserUpdateRequest(String nickname, String password, String passwordCheck, MultipartFile imageUrl, String description, String birthday) { + this.nickname = nickname; + this.password = password; + this.passwordCheck = passwordCheck; + this.imageUrl = imageUrl; + this.description = description; + this.birthday = birthday; + } + public String getNickname() { return nickname; @@ -33,7 +43,7 @@ public String getPassword() { return password; } - public String getImageUrl() { + public MultipartFile getImageUrl() { return imageUrl; } diff --git a/src/main/java/com/blog/workspace/adapter/in/web/dto/response/UserResponse.java b/src/main/java/com/blog/workspace/adapter/in/web/dto/response/UserResponse.java index 89e9864e..d33b8fc9 100644 --- a/src/main/java/com/blog/workspace/adapter/in/web/dto/response/UserResponse.java +++ b/src/main/java/com/blog/workspace/adapter/in/web/dto/response/UserResponse.java @@ -9,12 +9,14 @@ public class UserResponse { private final Long id; private final String nickname; + private final String imageUrl; private final String description; public UserResponse(User user) { this.id = user.getId(); this.nickname = user.getNickname(); this.description = user.getDescription(); + this.imageUrl = user.getImageUrl(); } /// @Getter @@ -29,4 +31,8 @@ public String getNickname() { public String getDescription() { return description; } + + public String getImageUrl() { + return imageUrl; + } } diff --git a/src/main/java/com/blog/workspace/adapter/out/ContentBlockPersistenceAdapter.java b/src/main/java/com/blog/workspace/adapter/out/ContentBlockPersistenceAdapter.java index 737bbed1..b3c07d9b 100644 --- a/src/main/java/com/blog/workspace/adapter/out/ContentBlockPersistenceAdapter.java +++ b/src/main/java/com/blog/workspace/adapter/out/ContentBlockPersistenceAdapter.java @@ -2,8 +2,10 @@ import com.blog.workspace.adapter.out.jdbc.post.ContentBlockJdbc; import com.blog.workspace.adapter.out.jdbc.post.ContentBlockJdbcRepository; +import com.blog.workspace.application.out.image.ImagePort; import com.blog.workspace.application.out.post.ContentBlockPort; import com.blog.workspace.domain.post.ContentBlock; +import com.blog.workspace.domain.post.ContentType; import org.springframework.stereotype.Component; import java.util.List; @@ -12,9 +14,11 @@ public class ContentBlockPersistenceAdapter implements ContentBlockPort { private final ContentBlockJdbcRepository repository; + private final ImagePort imagePort; - public ContentBlockPersistenceAdapter(ContentBlockJdbcRepository repository) { + public ContentBlockPersistenceAdapter(ContentBlockJdbcRepository repository, ImagePort imagePort) { this.repository = repository; + this.imagePort = imagePort; } @Override @@ -40,6 +44,15 @@ public void deleteBlockById(Long id) { @Override public void deleteBlockByPost(Long postId) { + + /// S3에서 이미지 삭제 + List blockList = loadBlocks(postId); + for (ContentBlock block : blockList) { + if (block.getType() == ContentType.IMAGE){ + imagePort.deleteFile(block.getContent()); + } + } + repository.deleteByPostId(postId); } diff --git a/src/main/java/com/blog/workspace/adapter/out/external/s3/S3ImagePersistenceAdapter.java b/src/main/java/com/blog/workspace/adapter/out/external/s3/S3ImagePersistenceAdapter.java new file mode 100644 index 00000000..f81809b1 --- /dev/null +++ b/src/main/java/com/blog/workspace/adapter/out/external/s3/S3ImagePersistenceAdapter.java @@ -0,0 +1,62 @@ +package com.blog.workspace.adapter.out.external.s3; + +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.blog.workspace.application.out.image.ImagePort; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; + +import java.io.IOException; +import java.util.UUID; + +@Component +public class S3ImagePersistenceAdapter implements ImagePort { + + private final AmazonS3 amazonS3; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + public S3ImagePersistenceAdapter(AmazonS3 amazonS3) { + this.amazonS3 = amazonS3; + } + + // 이미지 하나 저장 + @Override + public String uploadFiles(MultipartFile file) throws IOException { + + /// S3 관련 내용 저장 + String filename = createFileName(file.getOriginalFilename()); + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(file.getSize()); + metadata.setContentType(file.getContentType()); + + try { + amazonS3.putObject(new PutObjectRequest(bucket, filename, file.getInputStream(), metadata) + .withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다."); + } + + return amazonS3.getUrl(bucket, filename).toString(); + } + + // 이미지 하나 삭제 + @Override + public void deleteFile(String fileName) { + amazonS3.deleteObject(bucket, fileName); + } + + /// 내부 함수 + // 파일명을 난수화하기 위해 UUID 를 활용하여 난수를 돌린다. + private String createFileName(String fileName){ + return UUID.randomUUID().toString().concat(fileName); + } + +} diff --git a/src/main/java/com/blog/workspace/application/in/auth/RegisterUserUseCase.java b/src/main/java/com/blog/workspace/application/in/auth/RegisterUserUseCase.java index 11fcf634..f923abc6 100644 --- a/src/main/java/com/blog/workspace/application/in/auth/RegisterUserUseCase.java +++ b/src/main/java/com/blog/workspace/application/in/auth/RegisterUserUseCase.java @@ -3,6 +3,8 @@ import com.blog.workspace.adapter.in.web.dto.request.UserRegisterRequest; import com.blog.workspace.domain.user.User; +import java.io.IOException; + public interface RegisterUserUseCase { /* @@ -13,6 +15,6 @@ public interface RegisterUserUseCase { */ // 자체 회원가입 - User registerUser(UserRegisterRequest request); + User registerUser(UserRegisterRequest request) throws IOException; } diff --git a/src/main/java/com/blog/workspace/application/in/post/PostUseCase.java b/src/main/java/com/blog/workspace/application/in/post/PostUseCase.java index 29b51bae..c5cbaa7b 100644 --- a/src/main/java/com/blog/workspace/application/in/post/PostUseCase.java +++ b/src/main/java/com/blog/workspace/application/in/post/PostUseCase.java @@ -8,6 +8,8 @@ import com.blog.workspace.adapter.in.web.dto.response.PostListResponse; import com.blog.workspace.domain.post.Post; +import java.io.IOException; + public interface PostUseCase { /* @@ -21,7 +23,7 @@ public interface PostUseCase { */ /// 게시글 작성 - Post savePost(PostRequest request, Long userId); + Post savePost(PostRequest request, Long userId) throws IOException; /// 게시글 조회 // 게시글 상세 조회 @@ -31,7 +33,7 @@ public interface PostUseCase { Page loadPosts(Pageable pageable, Long userId); /// 게시글 수정 - Post updatePost(Long postId, Long userId, PostUpdateRequest request); + Post updatePost(Long postId, Long userId, PostUpdateRequest request) throws IOException; /// 게시글 삭제 void deletePost(Long userId, Long postId); diff --git a/src/main/java/com/blog/workspace/application/in/user/UpdateUserUseCase.java b/src/main/java/com/blog/workspace/application/in/user/UpdateUserUseCase.java index e141d5d8..11997a27 100644 --- a/src/main/java/com/blog/workspace/application/in/user/UpdateUserUseCase.java +++ b/src/main/java/com/blog/workspace/application/in/user/UpdateUserUseCase.java @@ -1,7 +1,8 @@ package com.blog.workspace.application.in.user; import com.blog.workspace.adapter.in.web.dto.request.UserUpdateRequest; -import com.blog.workspace.domain.user.User; + +import java.io.IOException; public interface UpdateUserUseCase { @@ -10,5 +11,5 @@ public interface UpdateUserUseCase { 카카오 로그인 유저의 개인정보 수정 - [이미지, 닉네임, 한줄 소개, 생년월일]에 대해 수정이 가능하다. */ - void updateUser(Long userId, UserUpdateRequest request); + void updateUser(Long userId, UserUpdateRequest request) throws IOException; } diff --git a/src/main/java/com/blog/workspace/application/out/image/ImagePort.java b/src/main/java/com/blog/workspace/application/out/image/ImagePort.java new file mode 100644 index 00000000..34949634 --- /dev/null +++ b/src/main/java/com/blog/workspace/application/out/image/ImagePort.java @@ -0,0 +1,26 @@ +package com.blog.workspace.application.out.image; + +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +public interface ImagePort { + + /* + 이미지 관련 포트는 그대로 사용하도록 한다. + 연관관계없이, 서비스 계층에서 저장된 이미지 + 다른 엔티티간의 연관관계를 사용하도록 한다. + */ + + /// SavePort + // 이미지 하나 저장 + String uploadFiles(MultipartFile multipartFile) throws IOException; + + /// DeletePort + // 이미지 하나 삭제 + void deleteFile(String fileName); + +} diff --git a/src/main/java/com/blog/workspace/application/service/PostService.java b/src/main/java/com/blog/workspace/application/service/PostService.java index 9206b7b2..fed197df 100644 --- a/src/main/java/com/blog/workspace/application/service/PostService.java +++ b/src/main/java/com/blog/workspace/application/service/PostService.java @@ -12,6 +12,7 @@ import com.blog.workspace.application.in.comment.CommentUseCase; import com.blog.workspace.application.in.post.PostUseCase; import com.blog.workspace.application.out.comment.LoadCommentPort; +import com.blog.workspace.application.out.image.ImagePort; import com.blog.workspace.application.out.post.ContentBlockPort; import com.blog.workspace.application.out.post.DeletePostPort; import com.blog.workspace.application.out.post.LoadPostPort; @@ -25,11 +26,10 @@ import com.blog.workspace.domain.post.ContentType; import com.blog.workspace.domain.post.Post; import com.blog.workspace.domain.user.User; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -48,21 +48,21 @@ public class PostService implements PostUseCase { private final LoadCommentPort commentPort; private final UserPort userPort; - private final CommentUseCase commentUseCase; + private final ImagePort imagePort; /// 생성자 - public PostService(SavePostPort savePort, LoadPostPort loadPort, DeletePostPort deletePort, ContentBlockPort contentPort, LoadCommentPort commentPort, UserPort userPort, CommentUseCase commentUseCase) { + public PostService(SavePostPort savePort, LoadPostPort loadPort, DeletePostPort deletePort, ContentBlockPort contentPort, LoadCommentPort commentPort, UserPort userPort, CommentUseCase commentUseCase, ImagePort imagePort) { this.savePort = savePort; this.loadPort = loadPort; this.deletePort = deletePort; this.contentPort = contentPort; this.commentPort = commentPort; this.userPort = userPort; - this.commentUseCase = commentUseCase; + this.imagePort = imagePort; } @Override - public Post savePost(PostRequest request, Long userId) { + public Post savePost(PostRequest request, Long userId) throws IOException { /// 유저 검증 userPort.findMe(userId) @@ -78,7 +78,7 @@ public Post savePost(PostRequest request, Long userId) { /// 내용 블럭 저장 // 순서를 위한 변수 설정 int ord = 0; - createContenetBlock(contents, savedPost, ord); + createContentBlock(contents, savedPost, ord); return savedPost; } @@ -147,7 +147,7 @@ public Page loadPosts(Pageable pageable, Long userId) { // 게시글 수정 @Override - public Post updatePost(Long postId, Long userId, PostUpdateRequest request) { + public Post updatePost(Long postId, Long userId, PostUpdateRequest request) throws IOException { /// 제목과 내용이 모두 비어 있는 경우 예외 처리 if ((request.getTitle() == null || request.getTitle().trim().isEmpty()) && @@ -190,15 +190,24 @@ public void deletePost(Long userId, Long postId) { /// 내부 함수 // 블록 생성 - private void createContenetBlock(List contents, Post savedPost, int ord) { + private void createContentBlock(List contents, Post savedPost, int ord) throws IOException { for (ContentRequest content : contents) { - ContentBlock contentBlock = ContentBlock.of(savedPost.getId(), content.getType(), content.getContent(), ++ord); + + String blockContent; + + if (content.getType() == ContentType.IMAGE) { + blockContent = imagePort.uploadFiles(content.getImage()); + } else { + blockContent = content.getContent(); + } + + ContentBlock contentBlock = ContentBlock.of(savedPost.getId(), content.getType(), blockContent, ++ord); contentPort.saveBlock(contentBlock); } } // 글 수정하기 - private void changePost(PostUpdateRequest request, Post post) { + private void changePost(PostUpdateRequest request, Post post) throws IOException { // 제목이 요청에 있으면 수정 if (request.getTitle() != null && !request.getTitle().isEmpty()) { post.changeTitle(request.getTitle()); @@ -209,7 +218,7 @@ private void changePost(PostUpdateRequest request, Post post) { contentPort.deleteBlockByPost(post.getId()); // 새롭게 내용을 저장 - createContenetBlock(request.getContent(), post, 0); + createContentBlock(request.getContent(), post, 0); } } diff --git a/src/main/java/com/blog/workspace/application/service/UserService.java b/src/main/java/com/blog/workspace/application/service/UserService.java index c7fce5b6..2f086976 100644 --- a/src/main/java/com/blog/workspace/application/service/UserService.java +++ b/src/main/java/com/blog/workspace/application/service/UserService.java @@ -5,6 +5,7 @@ import com.blog.workspace.application.in.user.GetUserUseCase; import com.blog.workspace.application.in.auth.RegisterUserUseCase; import com.blog.workspace.application.in.user.UpdateUserUseCase; +import com.blog.workspace.application.out.image.ImagePort; import com.blog.workspace.application.out.user.UserPort; import com.blog.workspace.application.service.exception.DuplicationUserException; import com.blog.workspace.application.service.exception.NotSamePasswordException; @@ -13,7 +14,9 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.time.LocalDateTime; import java.util.NoSuchElementException; @@ -24,14 +27,18 @@ public class UserService implements GetUserUseCase, RegisterUserUseCase, UpdateU private static final Logger log = LoggerFactory.getLogger(UserService.class); private final UserPort userPort; - public UserService(UserPort userPort) { + /// 이미지 관련 의존성 + private final ImagePort imagePort; + + public UserService(UserPort userPort, ImagePort imagePort) { this.userPort = userPort; + this.imagePort = imagePort; } /// 저장 @Override - public User registerUser(UserRegisterRequest request) { + public User registerUser(UserRegisterRequest request) throws IOException { /// 예외처리 // 중복된 이메일 사용 불가 @@ -42,7 +49,7 @@ public User registerUser(UserRegisterRequest request) { // 비밀번호와 비밀번호 확인의 동일여부 체크 validationPassWord(request.getPassword(), request.getPasswordCheck()); - // 객체 생성 + /// 객체 생성 LocalDateTime now = LocalDateTime.now(); User user = createUser(request, now); @@ -50,6 +57,30 @@ public User registerUser(UserRegisterRequest request) { return userPort.saveUser(user); } + /// 함수 20줄 이하로 만들 위해 내부 함수로 사용 + private User createUser(UserRegisterRequest request, LocalDateTime now) throws IOException { + + String imageUrl = ""; + + /// 이미지가 존재한다면 저장 + if (request.getImageUrl() != null) { + imageUrl = createImage(request.getImageUrl()); + } + + return new User( + request.getEmail(), + request.getNickname(), + request.getUsername(), + request.getPassword(), + imageUrl, + false, + request.getDescription(), + request.getBirthday(), + now, + now + ); + } + /// 조회 @Override public User getMyInfo(Long userId) { @@ -62,7 +93,7 @@ public User getMyInfo(Long userId) { /// 수정 @Override - public void updateUser(Long userId, UserUpdateRequest request) { + public void updateUser(Long userId, UserUpdateRequest request) throws IOException { User user = userPort.findMe(userId) .orElseThrow(() -> new NoSuchElementException("존재하지 않는 유저는 수정할 수 없습니다.")); @@ -82,37 +113,19 @@ public void updateUser(Long userId, UserUpdateRequest request) { } - log.info(user.getNickname()); - log.info(user.getDescription()); - userPort.updateUser(user); - } - /// 함수 20줄 이하로 만들 위해 내부 함수로 사용 - private User createUser(UserRegisterRequest request, LocalDateTime now) { - return new User( - request.getEmail(), - request.getNickname(), - request.getUsername(), - request.getPassword(), - request.getImageUrl(), - false, - request.getDescription(), - request.getBirthday(), - now, - now - ); - } - private void validationPassWord(String passWord, String passwordCheck) { - if (!passWord.equals(passwordCheck)) { + private void validationPassWord(String password, String passwordCheck) { + if (!password.equals(passwordCheck)) { throw new NotSamePasswordException("비밀번호가 일치하지 않습니다."); } } /// 유저 정보 수정 - private void changeUserInfo(UserUpdateRequest request, User user) { + private void changeUserInfo(UserUpdateRequest request, User user) throws IOException { + // 닉네임이 요청에 있으면 수정 if (request.getNickname() != null && !request.getNickname().isEmpty()) { user.changeNickname(request.getNickname()); @@ -130,10 +143,17 @@ private void changeUserInfo(UserUpdateRequest request, User user) { // 이미지 URL이 요청에 있으면 수정 if (request.getImageUrl() != null && !request.getImageUrl().isEmpty()) { - user.changeImageUrl(request.getImageUrl()); + + String imageUrl = createImage(request.getImageUrl()); + user.changeImageUrl(imageUrl); } } + /// 유저 이미지 저장 + private String createImage(MultipartFile file) throws IOException { + return imagePort.uploadFiles(file); + } +