From b48b31cd6af80ac80be6c989f4a70df7c7e9f71e Mon Sep 17 00:00:00 2001 From: JinHwanKim Date: Sat, 5 Mar 2022 03:32:58 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20image=20file=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor : CardService 메서드 정리 * refactor : GroupService 역할 분리 * feat : ImageFileService * refactor : service 코드 도메인으로 이동 * refactor : fileService 제거 * refactor : CardService가 ImageFile을 다루도록 수정 * refactor : CardService 리팩토링 * refactor : Profile Image 요청 루트 경로 수정 * feat : resource handler에 utf8 decode resolver 추가 --- .../card-photos => card-photos}/default.png | Bin page/src/components/ProfileCard.tsx | 2 +- .../com/giggle/samehere/GlobalAdvisor.java | 2 +- .../giggle/samehere/SameHereApplication.java | 5 +- .../card/application/CardService.java | 111 +++++++++++------- .../com/giggle/samehere/card/domain/Card.java | 20 ++-- .../giggle/samehere/card/domain/CardItem.java | 13 +- .../card/domain/CardItemRepository.java | 4 +- .../samehere/card/domain/ImageFile.java | 63 ++++++++++ .../samehere/card/dto/CardItemResponse.java | 2 +- .../giggle/samehere/card/dto/CardRequest.java | 8 +- .../samehere/card/dto/CardResponse.java | 10 +- .../samehere/card/dto/CardSimpleResponse.java | 2 +- .../samehere/card/dto/ImageFileResponse.java | 20 ++++ .../exception/FileUploadException.java | 2 +- .../card/presentation/CardController.java | 47 ++++---- .../giggle/samehere/config/WebMvcConfig.java | 31 +++-- .../com/giggle/samehere/file/FileService.java | 61 ---------- .../samehere/file/MultipartFileName.java | 33 ------ .../group/application/GroupService.java | 38 +----- .../samehere/group/domain/CardGroup.java | 1 + .../group/domain/CardGroupRepository.java | 1 + .../giggle/samehere/group/domain/Group.java | 5 +- .../group/presentation/GroupController.java | 12 +- .../{service => application}/ItemService.java | 7 +- .../com/giggle/samehere/item/domain/Item.java | 17 +-- .../item/domain/ItemChoicesConverter.java | 3 +- .../giggle/samehere/item/dto/ItemRequest.java | 1 + .../samehere/item/dto/ItemResponse.java | 1 + .../item/presentation/ItemController.java | 13 +- .../src/main/resources/application.properties | 4 +- 31 files changed, 265 insertions(+), 274 deletions(-) rename {server/card-photos => card-photos}/default.png (100%) create mode 100644 server/src/main/java/com/giggle/samehere/card/domain/ImageFile.java create mode 100644 server/src/main/java/com/giggle/samehere/card/dto/ImageFileResponse.java rename server/src/main/java/com/giggle/samehere/{file => card}/exception/FileUploadException.java (70%) delete mode 100644 server/src/main/java/com/giggle/samehere/file/FileService.java delete mode 100644 server/src/main/java/com/giggle/samehere/file/MultipartFileName.java rename server/src/main/java/com/giggle/samehere/item/{service => application}/ItemService.java (96%) diff --git a/server/card-photos/default.png b/card-photos/default.png similarity index 100% rename from server/card-photos/default.png rename to card-photos/default.png diff --git a/page/src/components/ProfileCard.tsx b/page/src/components/ProfileCard.tsx index 2e28dc2..4085b5b 100644 --- a/page/src/components/ProfileCard.tsx +++ b/page/src/components/ProfileCard.tsx @@ -13,7 +13,7 @@ const ProfileCard: FC = ({ name, gender, email, job, imageUrl }) => { return (
- +
{name} | {gender} diff --git a/server/src/main/java/com/giggle/samehere/GlobalAdvisor.java b/server/src/main/java/com/giggle/samehere/GlobalAdvisor.java index 8e6e340..9d4e458 100644 --- a/server/src/main/java/com/giggle/samehere/GlobalAdvisor.java +++ b/server/src/main/java/com/giggle/samehere/GlobalAdvisor.java @@ -1,7 +1,7 @@ package com.giggle.samehere; import com.giggle.samehere.card.exception.CardException; -import com.giggle.samehere.file.exception.FileUploadException; +import com.giggle.samehere.card.exception.FileUploadException; import com.giggle.samehere.group.exception.GroupException; import com.giggle.samehere.item.exception.ItemException; import org.slf4j.Logger; diff --git a/server/src/main/java/com/giggle/samehere/SameHereApplication.java b/server/src/main/java/com/giggle/samehere/SameHereApplication.java index de25d7e..74e5d63 100644 --- a/server/src/main/java/com/giggle/samehere/SameHereApplication.java +++ b/server/src/main/java/com/giggle/samehere/SameHereApplication.java @@ -4,13 +4,14 @@ import com.giggle.samehere.group.domain.GroupRepository; import com.giggle.samehere.item.domain.Item; import com.giggle.samehere.item.domain.ItemRepository; -import java.util.Arrays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; +import java.util.Arrays; + @SpringBootApplication public class SameHereApplication { @@ -44,4 +45,4 @@ public void setUp() { itemRepository.save(Item.shortAnswerQuestion("깃허브")); itemRepository.save(Item.shortAnswerQuestion("링크드인")); } -} \ No newline at end of file +} diff --git a/server/src/main/java/com/giggle/samehere/card/application/CardService.java b/server/src/main/java/com/giggle/samehere/card/application/CardService.java index 10425e3..a1ba56a 100644 --- a/server/src/main/java/com/giggle/samehere/card/application/CardService.java +++ b/server/src/main/java/com/giggle/samehere/card/application/CardService.java @@ -4,89 +4,103 @@ import com.giggle.samehere.card.domain.CardItem; import com.giggle.samehere.card.domain.CardItemRepository; import com.giggle.samehere.card.domain.CardRepository; -import com.giggle.samehere.card.dto.CardItemResponse; +import com.giggle.samehere.card.domain.ImageFile; import com.giggle.samehere.card.dto.CardRequest; import com.giggle.samehere.card.dto.CardResponse; import com.giggle.samehere.card.dto.CardSimpleResponse; import com.giggle.samehere.card.exception.CardException; -import com.giggle.samehere.group.application.GroupService; +import com.giggle.samehere.card.exception.FileUploadException; import com.giggle.samehere.group.domain.CardGroup; import com.giggle.samehere.group.domain.CardGroupRepository; -import com.giggle.samehere.group.dto.GroupResponse; +import com.giggle.samehere.group.domain.Group; +import com.giggle.samehere.group.domain.GroupRepository; +import com.giggle.samehere.group.exception.GroupException; import com.giggle.samehere.item.domain.Item; import com.giggle.samehere.item.domain.ItemRepository; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; @Service public class CardService { - private final GroupService groupService; + @Value("${cards.profile.image.upload.folder}") + private String IMAGE_FOLDER_PATH; + + @Value("${cards.profile.image.default.name}") + private String DEFAULT_PROFILE_IMAGE_FILE_NAME; + private final CardRepository cardRepository; + private final ItemRepository itemRepository; private final CardItemRepository cardItemRepository; + private final GroupRepository groupRepository; private final CardGroupRepository cardGroupRepository; - private final ItemRepository itemRepository; public CardService( - GroupService groupService, CardRepository cardRepository, + ItemRepository itemRepository, CardItemRepository cardItemRepository, - CardGroupRepository cardGroupRepository, - ItemRepository itemRepository + GroupRepository groupRepository, + CardGroupRepository cardGroupRepository ) { - this.groupService = groupService; this.cardRepository = cardRepository; + this.itemRepository = itemRepository; this.cardItemRepository = cardItemRepository; + this.groupRepository = groupRepository; this.cardGroupRepository = cardGroupRepository; - this.itemRepository = itemRepository; } @Transactional - public CardResponse create(CardRequest request, String imageFile) { - final Card card = request.toCard(imageFile); - cardRepository.save(card); - return CardResponse.of(card, Collections.emptyList(), Collections.emptyList()); + public CardResponse create(CardRequest request, MultipartFile multipartFile) { + try { + final ImageFile imageFile = ImageFile.save(IMAGE_FOLDER_PATH, multipartFile); + final Card card = request.toCard(imageFile); + cardRepository.save(card); + return cardResponse(card); + } catch (FileUploadException fue) { + return create(request); + } } @Transactional - public CardResponse createInGroup(Long groupId, CardRequest request, String imageFile) { + public CardResponse create(CardRequest request) { + final ImageFile imageFile = ImageFile.pathOf(IMAGE_FOLDER_PATH + DEFAULT_PROFILE_IMAGE_FILE_NAME); final Card card = request.toCard(imageFile); cardRepository.save(card); - final List groupResponses = groupService.enter(groupId, card); - return CardResponse.of(card, Collections.emptyList(), groupResponses); + return cardResponse(card); } - @Transactional(readOnly = true) - public CardResponse findById(Long id) { - final Card card = getCard(id); - final List cardItems = cardItemRepository.findAllByCardId(card.getId()); - final List groups = groupService.findAllByCard(card); - return CardResponse.of(card, CardItemResponse.listOf(cardItems), groups); + @Transactional + public CardResponse enterInGroup(Long cardId, Long groupId) { + final Card card = getCard(cardId); + final Group group = groupRepository.findById(groupId).orElseThrow(IllegalArgumentException::new); + checkDuplicated(card, group); + cardGroupRepository.save(new CardGroup(card, group)); + return cardResponse(card); } - @Transactional(readOnly = true) - public List findAllCardsByGroup(Long groupId) { - final List cardGroups = cardGroupRepository.findAllByGroupId(groupId); - final List cardsInGroup = cardGroups.stream().map(CardGroup::getCard).collect(Collectors.toList()); - return CardSimpleResponse.listOf(cardsInGroup); + private void checkDuplicated(Card card, Group enteringGroup) { + cardGroupRepository.findAllByCard(card).stream() + .filter(it -> it.isGroup(enteringGroup)) + .findAny() + .ifPresent(it -> { + throw new GroupException("이미 가입된 카드입니다."); + }); } @Transactional public CardResponse update(Long id, CardRequest request) { - final Card card = getCard(id); - card.update(request.toCard(card.getPhotosImagePath())); + final Card oldCard = getCard(id); + final Card newCard = request.toCard(oldCard.getImagePath()); + oldCard.update(newCard); final List cardItems = getRequestCardItems(request, id); - cardItems.forEach(CardItem::validateAnswer); - cardItemRepository.deleteAllByCardId(id); cardItemRepository.saveAll(cardItems); - - final List groups = groupService.findAllByCard(card); - return CardResponse.of(card, CardItemResponse.listOf(cardItems), groups); + return cardResponse(oldCard); } private List getRequestCardItems(CardRequest request, Long cardId) { @@ -95,12 +109,17 @@ private List getRequestCardItems(CardRequest request, Long cardId) { .collect(Collectors.toList()); } - @Transactional - public CardResponse enterInGroup(Long cardId, Long groupId) { - final Card card = getCard(cardId); - final List groupResponses = groupService.enter(groupId, card); - final List cardItems = cardItemRepository.findAllByCardId(card.getId()); - return CardResponse.of(card, CardItemResponse.listOf(cardItems), groupResponses); + @Transactional(readOnly = true) + public CardResponse findById(Long id) { + final Card card = getCard(id); + return cardResponse(card); + } + + @Transactional(readOnly = true) + public List findAllCardsByGroup(Long groupId) { + final List cardGroups = cardGroupRepository.findAllByGroupId(groupId); + final List cardsInGroup = cardGroups.stream().map(CardGroup::getCard).collect(Collectors.toList()); + return CardSimpleResponse.listOf(cardsInGroup); } private Item getItem(Long itemId) { @@ -110,4 +129,12 @@ private Item getItem(Long itemId) { private Card getCard(Long targetId) { return cardRepository.findById(targetId).orElseThrow(() -> new CardException("존재하지 않는 카드입니다.")); } + + private CardResponse cardResponse(Card card) { + final List cardItems = cardItemRepository.findAllByCardId(card.getId()); + final List groups = cardGroupRepository.findAllByCard(card).stream() + .map(CardGroup::getGroup) + .collect(Collectors.toList()); + return CardResponse.of(card, cardItems, groups); + } } diff --git a/server/src/main/java/com/giggle/samehere/card/domain/Card.java b/server/src/main/java/com/giggle/samehere/card/domain/Card.java index 37deefd..85abbda 100644 --- a/server/src/main/java/com/giggle/samehere/card/domain/Card.java +++ b/server/src/main/java/com/giggle/samehere/card/domain/Card.java @@ -3,7 +3,6 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; -import org.springframework.data.annotation.Transient; @Entity public class Card { @@ -15,17 +14,18 @@ public class Card { private String email; private String gender; private String profession; - private String image; + private String imagePath; private String password; - public Card() {} + public Card() { + } - public Card(String name, String email, String gender, String profession, String image, String password) { + public Card(String name, String email, String gender, String profession, String imagePath, String password) { this.name = name; this.email = email; this.gender = gender; this.profession = profession; - this.image = image; + this.imagePath = imagePath; this.password = password; } @@ -58,14 +58,8 @@ public String getProfession() { return profession; } - public String getImage() { - return image; - } - - // TODO :: 저장 경로 수정 - public String getPhotosImagePath() { - if (image == null || id == null) return null; - return "/card-photos/" + id + "/" + image; + public String getImagePath() { + return imagePath; } public String getPassword() { diff --git a/server/src/main/java/com/giggle/samehere/card/domain/CardItem.java b/server/src/main/java/com/giggle/samehere/card/domain/CardItem.java index e7060f4..4117e02 100644 --- a/server/src/main/java/com/giggle/samehere/card/domain/CardItem.java +++ b/server/src/main/java/com/giggle/samehere/card/domain/CardItem.java @@ -1,11 +1,8 @@ package com.giggle.samehere.card.domain; import com.giggle.samehere.item.domain.Item; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.ManyToOne; + +import javax.persistence.*; @Entity public class CardItem { @@ -19,15 +16,13 @@ public class CardItem { private Item item; private String answer; - protected CardItem() {} + protected CardItem() { + } public CardItem(Long cardId, Item item, String answer) { this.cardId = cardId; this.item = item; this.answer = answer; - } - - public void validateAnswer() { item.validateAnswer(answer); } diff --git a/server/src/main/java/com/giggle/samehere/card/domain/CardItemRepository.java b/server/src/main/java/com/giggle/samehere/card/domain/CardItemRepository.java index ab4bd5a..ce3d7c2 100644 --- a/server/src/main/java/com/giggle/samehere/card/domain/CardItemRepository.java +++ b/server/src/main/java/com/giggle/samehere/card/domain/CardItemRepository.java @@ -1,9 +1,9 @@ package com.giggle.samehere.card.domain; -import java.util.List; - import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface CardItemRepository extends JpaRepository { List findAllByCardId(Long cardId); diff --git a/server/src/main/java/com/giggle/samehere/card/domain/ImageFile.java b/server/src/main/java/com/giggle/samehere/card/domain/ImageFile.java new file mode 100644 index 0000000..12fbd1c --- /dev/null +++ b/server/src/main/java/com/giggle/samehere/card/domain/ImageFile.java @@ -0,0 +1,63 @@ +package com.giggle.samehere.card.domain; + +import com.giggle.samehere.card.exception.FileUploadException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +public class ImageFile { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd.HH:mm:ss"); + + private final Path filePath; + + private ImageFile(Path filePath) { + this.filePath = filePath; + } + + public static ImageFile pathOf(String filePath) { + return new ImageFile(Path.of(filePath)); + } + + public static ImageFile save(Path directoryPath, MultipartFile multipartFile) { + try (InputStream inputStream = multipartFile.getInputStream()) { + if (!Files.exists(directoryPath)) { + Files.createDirectories(directoryPath); + } + final Path filePath = filePath(directoryPath, multipartFile); + Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING); + return new ImageFile(filePath); + } catch (Exception e) { + e.printStackTrace(); + throw new FileUploadException(); + } + } + + public static ImageFile save(String directoryPath, MultipartFile multipartFile) { + return save(Path.of(directoryPath), multipartFile); + } + + private static Path filePath(Path directoryPath, MultipartFile multipartFile) { + final String prefix = LocalDateTime.now().format(DATE_TIME_FORMATTER) + ":"; + final String fileName = prefix+StringUtils.cleanPath(multipartFile.getOriginalFilename()); + final Path path = directoryPath.resolve(fileName); + return uniquePath(path); + } + + private static Path uniquePath(Path path) { + if (Files.exists(path)) { + return uniquePath(Path.of(path + UUID.randomUUID().toString().substring(0, 5))); + } + return path; + } + + public Path path() { + return filePath; + } +} diff --git a/server/src/main/java/com/giggle/samehere/card/dto/CardItemResponse.java b/server/src/main/java/com/giggle/samehere/card/dto/CardItemResponse.java index 52e9af1..6395851 100644 --- a/server/src/main/java/com/giggle/samehere/card/dto/CardItemResponse.java +++ b/server/src/main/java/com/giggle/samehere/card/dto/CardItemResponse.java @@ -16,7 +16,7 @@ public CardItemResponse(String itemName, String value) { } public static List listOf(List cardItems) { - return cardItems.stream().map(it-> of(it)).collect(Collectors.toList()); + return cardItems.stream().map(it -> of(it)).collect(Collectors.toList()); } public static CardItemResponse of(CardItem cardItem) { diff --git a/server/src/main/java/com/giggle/samehere/card/dto/CardRequest.java b/server/src/main/java/com/giggle/samehere/card/dto/CardRequest.java index 9a9136b..5026ace 100644 --- a/server/src/main/java/com/giggle/samehere/card/dto/CardRequest.java +++ b/server/src/main/java/com/giggle/samehere/card/dto/CardRequest.java @@ -1,6 +1,7 @@ package com.giggle.samehere.card.dto; import com.giggle.samehere.card.domain.Card; +import com.giggle.samehere.card.domain.ImageFile; import java.util.List; @@ -13,7 +14,8 @@ public class CardRequest { private String password; private List cardItems; - public CardRequest() {} + public CardRequest() { + } public CardRequest(String name, String email, String gender, String profession, String password, List cardItems) { @@ -29,6 +31,10 @@ public Card toCard(String imageName) { return new Card(name, email, gender, profession, imageName, password); } + public Card toCard(ImageFile imageFile) { + return new Card(name, email, gender, profession, imageFile.path().toString(), password); + } + public String getName() { return name; } diff --git a/server/src/main/java/com/giggle/samehere/card/dto/CardResponse.java b/server/src/main/java/com/giggle/samehere/card/dto/CardResponse.java index cc67d46..32f5ee1 100644 --- a/server/src/main/java/com/giggle/samehere/card/dto/CardResponse.java +++ b/server/src/main/java/com/giggle/samehere/card/dto/CardResponse.java @@ -1,6 +1,8 @@ package com.giggle.samehere.card.dto; import com.giggle.samehere.card.domain.Card; +import com.giggle.samehere.card.domain.CardItem; +import com.giggle.samehere.group.domain.Group; import com.giggle.samehere.group.dto.GroupResponse; import java.util.List; @@ -27,16 +29,16 @@ public CardResponse(Long id, String name, String email, String gender, String pr this.cardItems = cardItems; } - public static CardResponse of(Card card, List cardItems, List groups) { + public static CardResponse of(Card card, List cardItems, List groups) { return new CardResponse( card.getId(), card.getName(), card.getEmail(), card.getGender(), card.getProfession(), - card.getImage(), - groups, - cardItems + card.getImagePath(), + GroupResponse.listOf(groups), + CardItemResponse.listOf(cardItems) ); } diff --git a/server/src/main/java/com/giggle/samehere/card/dto/CardSimpleResponse.java b/server/src/main/java/com/giggle/samehere/card/dto/CardSimpleResponse.java index 34958db..eac72a7 100644 --- a/server/src/main/java/com/giggle/samehere/card/dto/CardSimpleResponse.java +++ b/server/src/main/java/com/giggle/samehere/card/dto/CardSimpleResponse.java @@ -20,7 +20,7 @@ public CardSimpleResponse(Long id, String name, String gender, String imageUrl) } public static CardSimpleResponse of(Card card) { - return new CardSimpleResponse(card.getId(), card.getName(), card.getGender(), card.getImage()); + return new CardSimpleResponse(card.getId(), card.getName(), card.getGender(), card.getImagePath()); } public static List listOf(List cardsInGroup) { diff --git a/server/src/main/java/com/giggle/samehere/card/dto/ImageFileResponse.java b/server/src/main/java/com/giggle/samehere/card/dto/ImageFileResponse.java new file mode 100644 index 0000000..bbc8f08 --- /dev/null +++ b/server/src/main/java/com/giggle/samehere/card/dto/ImageFileResponse.java @@ -0,0 +1,20 @@ +package com.giggle.samehere.card.dto; + +import com.giggle.samehere.card.domain.ImageFile; + +public class ImageFileResponse { + + private final String path; + + public ImageFileResponse(String path) { + this.path = path; + } + + public static ImageFileResponse of(ImageFile imageFile) { + return new ImageFileResponse(imageFile.path().toString()); + } + + public String getPath() { + return path; + } +} diff --git a/server/src/main/java/com/giggle/samehere/file/exception/FileUploadException.java b/server/src/main/java/com/giggle/samehere/card/exception/FileUploadException.java similarity index 70% rename from server/src/main/java/com/giggle/samehere/file/exception/FileUploadException.java rename to server/src/main/java/com/giggle/samehere/card/exception/FileUploadException.java index e7bb86f..5738f4a 100644 --- a/server/src/main/java/com/giggle/samehere/file/exception/FileUploadException.java +++ b/server/src/main/java/com/giggle/samehere/card/exception/FileUploadException.java @@ -1,4 +1,4 @@ -package com.giggle.samehere.file.exception; +package com.giggle.samehere.card.exception; public class FileUploadException extends RuntimeException { diff --git a/server/src/main/java/com/giggle/samehere/card/presentation/CardController.java b/server/src/main/java/com/giggle/samehere/card/presentation/CardController.java index 225807d..652b892 100644 --- a/server/src/main/java/com/giggle/samehere/card/presentation/CardController.java +++ b/server/src/main/java/com/giggle/samehere/card/presentation/CardController.java @@ -4,53 +4,52 @@ import com.giggle.samehere.card.dto.CardRequest; import com.giggle.samehere.card.dto.CardResponse; import com.giggle.samehere.card.dto.CardSimpleResponse; -import com.giggle.samehere.file.FileService; -import java.util.List; +import com.giggle.samehere.card.exception.FileUploadException; +import java.util.Objects; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + @RestController @RequestMapping("cards") public class CardController { private final CardService cardService; - private final FileService fileService; - public CardController(CardService cardService, FileService fileService) { + public CardController(CardService cardService) { this.cardService = cardService; - this.fileService = fileService; } @PostMapping public ResponseEntity create( - CardRequest request, - @RequestParam(value = "image", required = false) MultipartFile multipartFile + @ModelAttribute CardRequest request, + @RequestParam(required = false) MultipartFile image ) { - final String fileName = fileService.saveImageFile(multipartFile); - final CardResponse response = cardService.create(request, fileName); + final CardResponse response = createCard(request, image); return ResponseEntity.ok(response); } @PostMapping("/groups/{groupId}") - public ResponseEntity createInGroup( - @PathVariable Long groupId, CardRequest cardRequest, - @RequestParam(value = "image", required = false) MultipartFile multipartFile + public ResponseEntity create( + @PathVariable Long groupId, + @ModelAttribute CardRequest request, + @RequestParam(required = false) MultipartFile image ) { - final String imageName = fileService.saveImageFile(multipartFile); - final CardResponse response = cardService.createInGroup(groupId, cardRequest, imageName); - return ResponseEntity.ok(response); + final CardResponse savedCard = createCard(request, image); + return joinInGroup(savedCard.getId(), groupId); + } + + private CardResponse createCard(CardRequest request, MultipartFile multipartFile) { + if(Objects.isNull(multipartFile) || multipartFile.isEmpty()) { + return cardService.create(request); + } + return cardService.create(request, multipartFile); } @PostMapping("/{cardId}/groups/{groupId}") - public ResponseEntity enter(@PathVariable Long cardId, @PathVariable Long groupId) { + public ResponseEntity joinInGroup(@PathVariable Long cardId, @PathVariable Long groupId) { final CardResponse response = cardService.enterInGroup(cardId, groupId); return ResponseEntity.ok(response); } diff --git a/server/src/main/java/com/giggle/samehere/config/WebMvcConfig.java b/server/src/main/java/com/giggle/samehere/config/WebMvcConfig.java index 7edfee8..4907120 100644 --- a/server/src/main/java/com/giggle/samehere/config/WebMvcConfig.java +++ b/server/src/main/java/com/giggle/samehere/config/WebMvcConfig.java @@ -1,12 +1,18 @@ package com.giggle.samehere.config; +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.PathResourceResolver; +import org.springframework.web.servlet.resource.ResourceResolver; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @@ -20,17 +26,28 @@ public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOriginPatterns(allowedOrigins) - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowCredentials(true) - .maxAge(3000); + .allowedOriginPatterns(allowedOrigins) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowCredentials(true) + .maxAge(3000); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - final Path fileRoot = Paths.get("./"+ cardUploadFolderName).toAbsolutePath().normalize(); + final Path fileRoot = Paths.get("./" + cardUploadFolderName).toAbsolutePath().normalize(); registry - .addResourceHandler("/resources/**") - .addResourceLocations(fileRoot.toUri().toString()); + .addResourceHandler("/" + cardUploadFolderName + "/**") + .addResourceLocations(fileRoot.toUri().toString()) + .resourceChain(true) + .addResolver(new Utf8DecodeResourceHandler()); + } +} + +class Utf8DecodeResourceHandler extends PathResourceResolver implements ResourceResolver { + + @Override + protected Resource getResource(String resourcePath, Resource location) throws IOException { + resourcePath = URLDecoder.decode(resourcePath, StandardCharsets.UTF_8); + return super.getResource(resourcePath, location); } } diff --git a/server/src/main/java/com/giggle/samehere/file/FileService.java b/server/src/main/java/com/giggle/samehere/file/FileService.java deleted file mode 100644 index 424b4b0..0000000 --- a/server/src/main/java/com/giggle/samehere/file/FileService.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.giggle.samehere.file; - -import com.giggle.samehere.file.exception.FileUploadException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Objects; -import java.util.UUID; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -@Service -public class FileService { - - @Value("${cards.profile.image.resource.root}") - private String ROOT_PATH; - - @Value("${cards.profile.image.default.name}") - private String DEFAULT_FILE_NAME; - - @Value("${cards.profile.image.upload.folder}") - private String PHOTO_UPLOAD_FOLDER; - - public String saveImageFile(MultipartFile multipartFile) { - if (Objects.isNull(multipartFile) || multipartFile.isEmpty()) { - return ROOT_PATH + DEFAULT_FILE_NAME; - } - try (InputStream inputStream = multipartFile.getInputStream()) { - final MultipartFileName fileName = MultipartFileName.of(multipartFile); - saveFile(inputStream, fileName.asPathIn(directoryPath())); - return ROOT_PATH + fileName.asString(); - } catch (FileUploadException | IOException e) { - e.printStackTrace(); - return ROOT_PATH + DEFAULT_FILE_NAME; - } - } - - private void saveFile(InputStream inputStream, Path path) throws IOException { - final Path uniquePath = findUniquePath(path); - Files.copy(inputStream, uniquePath, StandardCopyOption.REPLACE_EXISTING); - } - - private Path findUniquePath(Path path) { - if (Files.exists(path)) { - return findUniquePath(Path.of(path + UUID.randomUUID().toString().substring(0, 5))); - } - return path; - } - - private Path directoryPath() throws IOException { - final Path directoryPath = Paths.get(PHOTO_UPLOAD_FOLDER); - if (!Files.exists(directoryPath)) { - Files.createDirectories(directoryPath); - } - return directoryPath; - } -} diff --git a/server/src/main/java/com/giggle/samehere/file/MultipartFileName.java b/server/src/main/java/com/giggle/samehere/file/MultipartFileName.java deleted file mode 100644 index 5cb16d7..0000000 --- a/server/src/main/java/com/giggle/samehere/file/MultipartFileName.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.giggle.samehere.file; - -import com.giggle.samehere.file.exception.FileUploadException; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.util.Objects; -import org.springframework.util.StringUtils; -import org.springframework.web.multipart.MultipartFile; - -public class MultipartFileName { - - private final String fileName; - - public MultipartFileName(String fileName) { - this.fileName = fileName; - } - - public static MultipartFileName of(MultipartFile multipartFile) { - if (Objects.isNull(multipartFile) || multipartFile.isEmpty()) { - throw new FileUploadException(); - } - final String uniqueFileName = LocalDateTime.now() + StringUtils.cleanPath(multipartFile.getOriginalFilename()); - return new MultipartFileName(uniqueFileName); - } - - public Path asPathIn(Path path) { - return path.resolve(fileName); - } - - public String asString() { - return fileName; - } -} diff --git a/server/src/main/java/com/giggle/samehere/group/application/GroupService.java b/server/src/main/java/com/giggle/samehere/group/application/GroupService.java index fec4f8b..f3aed26 100644 --- a/server/src/main/java/com/giggle/samehere/group/application/GroupService.java +++ b/server/src/main/java/com/giggle/samehere/group/application/GroupService.java @@ -1,26 +1,21 @@ package com.giggle.samehere.group.application; -import com.giggle.samehere.card.domain.Card; -import com.giggle.samehere.group.domain.CardGroup; -import com.giggle.samehere.group.domain.CardGroupRepository; import com.giggle.samehere.group.domain.Group; import com.giggle.samehere.group.domain.GroupRepository; import com.giggle.samehere.group.dto.GroupRequest; import com.giggle.samehere.group.dto.GroupResponse; -import com.giggle.samehere.group.exception.GroupException; -import java.util.List; -import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service public class GroupService { - private final CardGroupRepository cardGroupRepository; private final GroupRepository groupRepository; - public GroupService(CardGroupRepository cardGroupRepository, GroupRepository groupRepository) { - this.cardGroupRepository = cardGroupRepository; + public GroupService(GroupRepository groupRepository) { this.groupRepository = groupRepository; } @@ -51,31 +46,6 @@ public GroupResponse update(Long id, GroupRequest request) { return GroupResponse.of(group); } - @Transactional - public List enter(Long groupId, Card card) { - final Group enteringGroup = findGroupById(groupId); - checkDuplicated(card, enteringGroup); - - cardGroupRepository.save(new CardGroup(card, enteringGroup)); - return findAllByCard(card); - } - - private void checkDuplicated(Card card, Group enteringGroup) { - cardGroupRepository.findAllByCard(card).stream() - .filter(it -> it.isGroup(enteringGroup)) - .findAny() - .ifPresent(it -> { - throw new GroupException("이미 가입된 카드입니다."); - }); - } - - @Transactional(readOnly = true) - public List findAllByCard(Card card) { - final List cardGroups = cardGroupRepository.findAllByCard(card); - final List groups = cardGroups.stream().map(CardGroup::getGroup).collect(Collectors.toList()); - return GroupResponse.listOf(groups); - } - private Group findGroupById(Long id) { return groupRepository.findById(id).orElseThrow(IllegalArgumentException::new); } diff --git a/server/src/main/java/com/giggle/samehere/group/domain/CardGroup.java b/server/src/main/java/com/giggle/samehere/group/domain/CardGroup.java index f3cc7ef..c7e6ed1 100644 --- a/server/src/main/java/com/giggle/samehere/group/domain/CardGroup.java +++ b/server/src/main/java/com/giggle/samehere/group/domain/CardGroup.java @@ -1,6 +1,7 @@ package com.giggle.samehere.group.domain; import com.giggle.samehere.card.domain.Card; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; diff --git a/server/src/main/java/com/giggle/samehere/group/domain/CardGroupRepository.java b/server/src/main/java/com/giggle/samehere/group/domain/CardGroupRepository.java index 20b2cbe..dd4bac6 100644 --- a/server/src/main/java/com/giggle/samehere/group/domain/CardGroupRepository.java +++ b/server/src/main/java/com/giggle/samehere/group/domain/CardGroupRepository.java @@ -9,5 +9,6 @@ public interface CardGroupRepository extends JpaRepository { List findAllByCard(Card card); List findAllByGroup(Group group); + List findAllByGroupId(Long groupId); } diff --git a/server/src/main/java/com/giggle/samehere/group/domain/Group.java b/server/src/main/java/com/giggle/samehere/group/domain/Group.java index 9877bcf..b7cb8f7 100644 --- a/server/src/main/java/com/giggle/samehere/group/domain/Group.java +++ b/server/src/main/java/com/giggle/samehere/group/domain/Group.java @@ -1,10 +1,10 @@ package com.giggle.samehere.group.domain; -import java.util.Objects; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; +import java.util.Objects; @Table(name = "GROUPS") @Entity @@ -16,7 +16,8 @@ public class Group { private String name; private String password; - public Group() {} + public Group() { + } public Group(String name, String password) { this.name = name; diff --git a/server/src/main/java/com/giggle/samehere/group/presentation/GroupController.java b/server/src/main/java/com/giggle/samehere/group/presentation/GroupController.java index a094dd9..0b9078d 100644 --- a/server/src/main/java/com/giggle/samehere/group/presentation/GroupController.java +++ b/server/src/main/java/com/giggle/samehere/group/presentation/GroupController.java @@ -3,16 +3,10 @@ import com.giggle.samehere.group.application.GroupService; import com.giggle.samehere.group.dto.GroupRequest; import com.giggle.samehere.group.dto.GroupResponse; -import java.util.List; - import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/groups") diff --git a/server/src/main/java/com/giggle/samehere/item/service/ItemService.java b/server/src/main/java/com/giggle/samehere/item/application/ItemService.java similarity index 96% rename from server/src/main/java/com/giggle/samehere/item/service/ItemService.java rename to server/src/main/java/com/giggle/samehere/item/application/ItemService.java index 52cea0b..2ece01e 100644 --- a/server/src/main/java/com/giggle/samehere/item/service/ItemService.java +++ b/server/src/main/java/com/giggle/samehere/item/application/ItemService.java @@ -1,14 +1,15 @@ -package com.giggle.samehere.item.service; +package com.giggle.samehere.item.application; import com.giggle.samehere.item.domain.Item; import com.giggle.samehere.item.domain.ItemRepository; import com.giggle.samehere.item.dto.ItemRequest; import com.giggle.samehere.item.dto.ItemResponse; -import java.util.List; -import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service public class ItemService { diff --git a/server/src/main/java/com/giggle/samehere/item/domain/Item.java b/server/src/main/java/com/giggle/samehere/item/domain/Item.java index 868eb24..d4313fc 100644 --- a/server/src/main/java/com/giggle/samehere/item/domain/Item.java +++ b/server/src/main/java/com/giggle/samehere/item/domain/Item.java @@ -1,17 +1,11 @@ package com.giggle.samehere.item.domain; import com.giggle.samehere.item.exception.ItemException; + +import javax.persistence.*; import java.util.Collections; import java.util.List; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Convert; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; @Entity public class Item { @@ -28,7 +22,8 @@ public class Item { @Convert(converter = ItemChoicesConverter.class) private List itemChoices = Collections.emptyList(); - protected Item() {} + protected Item() { + } private Item(ItemAnswerType answerType, String name, List itemChoices) { this.answerType = answerType; @@ -49,11 +44,11 @@ public void updateName(Item other) { } public void validateAnswer(String answer) { - if(answerType == ItemAnswerType.MULTIPLE) { + if (answerType == ItemAnswerType.MULTIPLE) { itemChoices.stream() .filter(it -> it.equals(answer)) .findAny() - .orElseThrow(() -> new ItemException("\'"+answer+"\'는 " + name + "의 답변이 될 수 없습니다.")); + .orElseThrow(() -> new ItemException("\'" + answer + "\'는 " + name + "의 답변이 될 수 없습니다.")); } } diff --git a/server/src/main/java/com/giggle/samehere/item/domain/ItemChoicesConverter.java b/server/src/main/java/com/giggle/samehere/item/domain/ItemChoicesConverter.java index bf1f034..c170f0c 100644 --- a/server/src/main/java/com/giggle/samehere/item/domain/ItemChoicesConverter.java +++ b/server/src/main/java/com/giggle/samehere/item/domain/ItemChoicesConverter.java @@ -3,9 +3,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; + import javax.persistence.AttributeConverter; import javax.persistence.Converter; +import java.util.List; @Converter public class ItemChoicesConverter implements AttributeConverter, String> { diff --git a/server/src/main/java/com/giggle/samehere/item/dto/ItemRequest.java b/server/src/main/java/com/giggle/samehere/item/dto/ItemRequest.java index 2d0480f..453955e 100644 --- a/server/src/main/java/com/giggle/samehere/item/dto/ItemRequest.java +++ b/server/src/main/java/com/giggle/samehere/item/dto/ItemRequest.java @@ -1,6 +1,7 @@ package com.giggle.samehere.item.dto; import com.giggle.samehere.item.domain.Item; + import java.util.List; import java.util.Objects; diff --git a/server/src/main/java/com/giggle/samehere/item/dto/ItemResponse.java b/server/src/main/java/com/giggle/samehere/item/dto/ItemResponse.java index 99b7746..aaf9a9f 100644 --- a/server/src/main/java/com/giggle/samehere/item/dto/ItemResponse.java +++ b/server/src/main/java/com/giggle/samehere/item/dto/ItemResponse.java @@ -2,6 +2,7 @@ import com.giggle.samehere.item.domain.Item; import com.giggle.samehere.item.domain.ItemAnswerType; + import java.util.List; public class ItemResponse { diff --git a/server/src/main/java/com/giggle/samehere/item/presentation/ItemController.java b/server/src/main/java/com/giggle/samehere/item/presentation/ItemController.java index 9c09c5e..db3cd68 100644 --- a/server/src/main/java/com/giggle/samehere/item/presentation/ItemController.java +++ b/server/src/main/java/com/giggle/samehere/item/presentation/ItemController.java @@ -1,17 +1,12 @@ package com.giggle.samehere.item.presentation; +import com.giggle.samehere.item.application.ItemService; import com.giggle.samehere.item.dto.ItemRequest; import com.giggle.samehere.item.dto.ItemResponse; -import com.giggle.samehere.item.service.ItemService; -import java.util.List; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/items") diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 06b2e92..2f34ed5 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -17,5 +17,5 @@ springdoc.swagger.operations-sorter= alpha cors.allowed.origins= http://localhost:3000/ cards.profile.image.upload.folder = card-photos -cards.profile.image.default.name = default.png -cards.profile.image.resource.root = /resource/ +cards.profile.image.default.name = /default.png +cards.profile.image.resource.root = /resource