Skip to content

Commit

Permalink
Merge pull request #30 from SQUAD-D/feature#18/image-update
Browse files Browse the repository at this point in the history
board image update
  • Loading branch information
songhaechan authored Nov 25, 2023
2 parents 885642e + 625ccb5 commit 34486c3
Show file tree
Hide file tree
Showing 33 changed files with 216 additions and 75 deletions.
20 changes: 19 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
## Project Architecture

![pja.png](mdimg%2Fpja.png)
![pja.png](mdimg%2Fpja.png)

## Project Preview

- Main

![main.png](mdimg%2Fmain.png)

- Board List

![boardList.png](mdimg%2FboardList.png)

- Board Create

![boardDetail.png](mdimg%2FboardDetail.png)

- Board Detail

![boardComment.png](mdimg%2FboardComment.png)
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dependencies {
implementation 'software.amazon.awssdk:s3:2.16.58'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-quartz:2.7.5'
implementation 'org.apache.commons:commons-collections4:4.0'
}

tasks.named('test') {
Expand Down
Binary file added dump.rdb
Binary file not shown.
Binary file added mdimg/boardComment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mdimg/boardDetail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mdimg/boardList.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mdimg/main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public CommonIdResponse saveBoard(
@Valid @RequestBody CreateBoardRequest createBoard) {
return boardService.createBoard(memberId, createBoard);
}

// 이미지 S3 전송
@PostMapping(value = "/boards/img")
public ImageInfoResponse saveImg(
@RequestPart(value = "image") MultipartFile image) {
return boardService.saveImage(image);
return boardService.saveImageToS3(image);
}

// 게시글 삭제
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ public void nickValidation(
public CommonIdResponse loginMember(
@RequestBody @Valid LoginRequest loginRequest,
HttpServletRequest request) {
Member member = memberService.login(loginRequest);
return memberService.provideSession(member, request);
return memberService.provideSession(loginRequest, request);
}

// 로그아웃
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import squad.board.service.BoardService;
import squad.board.service.CommentService;

import java.util.Optional;

@RestController
@RequestMapping("/api/my-page")
@RequiredArgsConstructor
Expand Down
1 change: 0 additions & 1 deletion src/main/java/squad/board/dto/ContentListResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;

@Getter
@Setter
public class ContentListResponse<Content> {
private List<Content> contents;
private Pagination pagination;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/squad/board/dto/Pagination.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import squad.board.exception.board.BoardStatus;

@Getter
@Setter
public class Pagination {
private final Long defaultPageSize = 10L;
private Long totalContent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.time.LocalDateTime;

@Getter
@Setter
@AllArgsConstructor
// 상세 게시글 DTO
public class BoardDetailResponse {
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/squad/board/dto/board/BoardPaging.java

This file was deleted.

1 change: 0 additions & 1 deletion src/main/java/squad/board/dto/board/BoardResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.time.LocalDateTime;

@Getter
@Setter
@AllArgsConstructor
// 게시판 목록 DTO
public class BoardResponse {
Expand Down
39 changes: 37 additions & 2 deletions src/main/java/squad/board/dto/board/BoardUpdateRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,47 @@
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Getter
@Setter
@AllArgsConstructor
public class BoardUpdateRequest {
@NotEmpty(message = "제목을 입력해주세요.")
private String title;
@NotEmpty(message = "내용을 입력해주세요.")
private String content;
private List<ImageInfoRequest> imageInfoList;

public BoardUpdateRequest(String title, String content, List<ImageInfoRequest> imageInfo) {
this.title = title;
this.content = content.replace(".com/tmp/", ".com/original/");
this.imageInfoList = imageInfo;
}

public List<String> checkDeletedImage(List<String> originalUuid) {
List<String> requestUuid = extractImageUuid();
return originalUuid.stream()
.filter(o -> isNoneMatchWithRegex(requestUuid, o))
.toList();
}

private boolean isNoneMatchWithRegex(List<String> requestUuid, String o) {
return requestUuid.stream().noneMatch(Predicate.isEqual(o));
}

private List<String> extractImageUuid() {
List<String> uuid = new ArrayList<>();
String regex = "(?<=\\boriginal\\/)(.*?)(?=\\\")";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
uuid.add(matcher.group());
}
return uuid;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.List;

@Getter
@Setter
public class CreateBoardRequest {
@NotEmpty(message = "제목을 입력해주세요.")
private String title;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.time.LocalDateTime;

@Getter
@Setter
public class CommentSaveRequest {
@NotEmpty(message = "댓글 내용을 입력해주세요.")
private String content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import lombok.Setter;

@Getter
@Setter
public class CommentUpdateRequest {
private String content;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Optional;

@Getter
@Setter
@AllArgsConstructor
public class CreateMemberRequest {
@NotEmpty(message = "아이디를 입력해주세요.")
Expand Down
1 change: 0 additions & 1 deletion src/main/java/squad/board/dto/member/LoginRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.Setter;

@Getter
@Setter
public class LoginRequest {
@NotEmpty(message = "아이디를 입력해주세요.")
private String loginId;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/squad/board/dto/member/LoginResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import lombok.Getter;

@AllArgsConstructor
@Getter
public class LoginResponse {
private Long memberId;
private String nickName;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/squad/board/dto/member/MemberResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import squad.board.domain.member.Member;

@Getter
@Setter
public class MemberResponse {
private String loginId;

Expand All @@ -15,7 +14,7 @@ public class MemberResponse {
private String name;

private String nickName;

public MemberResponse(Member member) {
this.loginId = member.getLoginId();
this.loginPw = member.getLoginPw();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.Objects;

@Getter
@Setter
@AllArgsConstructor
public class MemberUpdateRequest {
@NotEmpty(message = "아이디를 입력해주세요.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.Setter;

@Getter
@Setter
public class ValidateLoginIdRequest {
@NotEmpty(message = "아이디를 입력해주세요.")
private String loginId;
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/squad/board/repository/ImageMapper.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package squad.board.repository;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import squad.board.dto.board.ImageInfoRequest;

import java.util.List;

@Mapper
public interface ImageMapper {
void save(ImageInfoRequest request, Long boardId);
void save(@Param("list") List<ImageInfoRequest> imageInfo, Long boardId);

// 이미지UUID로 파일이름 조회
List<String> findImageUUID(Long boardId);
// 이미지 UUID 로 파일이름 조회
List<String> findImageUuid(Long boardId);

void deleteByBoardId(Long boardId);

void deleteByImageUuid(List<String> uuid);
}

64 changes: 48 additions & 16 deletions src/main/java/squad/board/service/BoardService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand Down Expand Up @@ -33,46 +34,57 @@ public class BoardService {
private final S3Service s3Service;
private static final long MAX_IMAGE_SIZE = 5000000L;
private static final int MAX_IMAGE_NAME_SIZE = 100;

private static final String TEMP_FOLDER_NAME = "tmp";
private static final String ORIGINAL_FOLDER_NAME = "original";
private static final String IMAGE_EXTENSION_EXTRACT_REGEX = "(.png|.jpg|.jpeg)$";

public CommonIdResponse createBoard(Long memberId, CreateBoardRequest createBoard) {
// 게시글 저장
Board board = createBoard.toEntity(memberId);
boardMapper.save(board);
// 이미지 정보 저장
List<ImageInfoRequest> imageInfoRequests = createBoard.getImageInfo();
for (ImageInfoRequest request : imageInfoRequests) {
if (CollectionUtils.isNotEmpty(imageInfoRequests)) {
// DB에 이미지 정보 저장
imageMapper.save(request, board.getBoardId());
// tmp 폴더의 이미지를 original 폴더로 이동
s3Service.moveImageToOriginal(request.getImageUUID(), "tmp", "original");
saveImageInfo(board.getBoardId(), imageInfoRequests);
}
return new CommonIdResponse(board.getBoardId());
}

public ImageInfoResponse saveImage(MultipartFile image) {
String imgOriginalName = image.getOriginalFilename();
if (!imgOriginalName.substring(imgOriginalName.lastIndexOf(".")).matches("(.png|.jpg|.jpeg)$")) {
private void saveImageInfo(Long boardId, List<ImageInfoRequest> imageInfoRequests) {
imageMapper.save(imageInfoRequests, boardId);
for (ImageInfoRequest request : imageInfoRequests) {
// tmp 폴더의 이미지를 original 폴더로 이동
s3Service.moveImageToOriginal(request.getImageUUID(), TEMP_FOLDER_NAME, ORIGINAL_FOLDER_NAME);
}
}

public ImageInfoResponse saveImageToS3(MultipartFile image) {
imageValidation(image);
String uuid = s3Service.saveFile(image, TEMP_FOLDER_NAME);
String imgSrc = s3Service.loadImage(uuid, TEMP_FOLDER_NAME);
return new ImageInfoResponse(uuid, image.getSize(), image.getOriginalFilename(), imgSrc);
}

private void imageValidation(MultipartFile image) {
String imageName = image.getOriginalFilename();
if (!imageName.substring(imageName.lastIndexOf(".")).matches(IMAGE_EXTENSION_EXTRACT_REGEX)) {
throw new ImageException(ImageStatus.INVALID_IMAGE_EXTENSION);
}
// 이미지 파일명 길이 제한
if (imgOriginalName.length() > MAX_IMAGE_NAME_SIZE) {
if (imageName.length() > MAX_IMAGE_NAME_SIZE) {
throw new ImageException(ImageStatus.IMAGE_NAME_SIZE_EXCEEDED);
}
long imgSize = image.getSize();
// 이미지 파일 크기 제한
if (imgSize > MAX_IMAGE_SIZE) {
throw new ImageException(ImageStatus.IMAGE_SIZE_EXCEEDED);
}
String uuid = s3Service.saveFile(image, "tmp");
String imgSrc = s3Service.loadImage(uuid, "tmp");
return new ImageInfoResponse(uuid, imgSize, imgOriginalName, imgSrc);
}

@Transactional(readOnly = true)
public ContentListResponse<BoardResponse> findBoards(Long size, Long requestPage, Long memberId) {
if (memberId == null) {
log.info("memberId = {}");
throw new BoardException(BoardStatus.INVALID_MEMBER_ID);
}
Long offset = calcOffset(requestPage, size);
Expand All @@ -93,21 +105,41 @@ public BoardDetailResponse findOneBoard(Long boardId) {
}

public CommonIdResponse deleteBoard(Long boardId) {
List<String> imageUUID = imageMapper.findImageUUID(boardId);
List<String> imageUUID = imageMapper.findImageUuid(boardId);
for (String uuid : imageUUID) {
s3Service.deleteImage(uuid, "original");
}
imageMapper.deleteByBoardId(boardId);
boardMapper.deleteById(boardId);
commentMapper.deleteByBoardId(boardId);
return new CommonIdResponse(boardId);
}

public CommonIdResponse updateBoard(Long boardId, BoardUpdateRequest dto) {
public CommonIdResponse updateBoard(Long boardId, BoardUpdateRequest updateBoard) {
// 이미지 정보 저장
List<ImageInfoRequest> imageInfoRequests = updateBoard.getImageInfoList();
// DB에 이미지 정보 저장
if (CollectionUtils.isNotEmpty(imageInfoRequests)) {
saveImageInfo(boardId, imageInfoRequests);
}
// 기존 이미지 정보
List<String> savedImageUuid = imageMapper.findImageUuid(boardId);
// 이미지가 있는 게시글일 경우 check
if (CollectionUtils.isNotEmpty(savedImageUuid)) {
List<String> deleteRequestImageUuid = updateBoard.checkDeletedImage(savedImageUuid);
deleteImageInfo(deleteRequestImageUuid);
}
LocalDateTime modifiedDate = LocalDateTime.now();
boardMapper.updateById(boardId, dto, modifiedDate);
boardMapper.updateById(boardId, updateBoard, modifiedDate);
return new CommonIdResponse(boardId);
}

private void deleteImageInfo(List<String> deleteRequestImageUuid) {
if (CollectionUtils.isNotEmpty(deleteRequestImageUuid)) {
imageMapper.deleteByImageUuid(deleteRequestImageUuid);
}
}

@Transactional(readOnly = true)
public ContentListResponse<BoardResponse> searchBoard(String keyWord, Long size, Long requestPage, String searchType) {
Long offset = calcOffset(requestPage, size);
Expand Down
Loading

0 comments on commit 34486c3

Please sign in to comment.