diff --git a/.travis.yml b/.travis.yml index bc22bf0..e23806f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,26 @@ # 언어와 jdk의 버전을 지정한다. language: java jdk: - - openjdk11 +- openjdk11 # 어느 브랜치가 push 될 때 수행할지 지정한다. # 오직 main 브랜치가 push될 때 수행하도록 지정하였다. branches: only: - - main + - main -# 빌드 전에 gradlew의 권한을 추가한다. +# 빌드 전에 할 일 +# application 복호화하여 사용할 수있도록 하기 +# gradlew의 권한을 추가한다. before_install: - - chmod +x gradlew +- chmod +x gradlew # Travis CI 서버의 Home # gradle을 통하여 의존성을 받게 되면 이를 캐시하여 배포할 때 마다 다시 받지 않도록 설정한다. cache: directories: - - '$HOME/.m2/repository' - - '$HOME/.gradle' + - "$HOME/.m2/repository" + - "$HOME/.gradle" # main 브랜치에 push 되면 수행되는 명령어이다. # 프로젝트 내에 권한이 추가된 gradlew를 활용하여 clean, build를 진행한다. @@ -28,42 +30,40 @@ script: "./gradlew clean build" notifications: email: recipients: - - v_donguk@naver.com + - v_donguk@naver.com # deploy 명령어가 실행되기 전 전처리 과정 # CodeDeploy는 Jar 파일을 인식 못하므로 Jar+기타 설정 파일들을 모아 압축함. before_deploy: - - mkdir -p before-deploy # zip에 파합시킬 파일들을 담을 디렉토리 생성 - - cp scripts/*.sh before-deploy/ # 스크립트 파일 복사 - - cp appspec.yml before-deploy/ # CodeDeploy 설정 파일 복사 - - cp build/libs/*.jar before-deploy/ # jar 배포파일 복사 - - cd before-deploy && zip -r before-deploy * # before-deploy로 이동하여 전체 파일 압축함 - - cd ../ && mkdir -p deploy # 상위 디렉토리 이동하여 deploy 디렉토리 생성 - - mv before-deploy/before-deploy.zip deploy/backend_server.zip # deploy로 zip파일 이동 +- mkdir -p before-deploy # zip에 파합시킬 파일들을 담을 디렉토리 생성 +- cp scripts/*.sh before-deploy/ # 스크립트 파일 복사 +- cp appspec.yml before-deploy/ # CodeDeploy 설정 파일 복사 +- cp build/libs/*.jar before-deploy/ # jar 배포파일 복사 +- cd before-deploy && zip -r before-deploy * # before-deploy로 이동하여 전체 파일 압축함 +- cd ../ && mkdir -p deploy # 상위 디렉토리 이동하여 deploy 디렉토리 생성 +- mv before-deploy/before-deploy.zip deploy/backend_server.zip # deploy로 zip파일 이동 # S3 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언. deploy: - - provider: s3 - access_key_id: $AWS_ACCESS_KEY # Travis repo settings에서 설정된 값 - secret_access_key: $AWS_SECRET_KEY # Travis repo settings에서 설정된 값 - bucket: ldu-springboot-build # S3 버킷 이름 - region: ap-northeast-2 # 지역이름 - skip_cleanup: true - acl: private # zip 파일 접근을 private으로 설정 - local_dir: deploy # before_deploy에서 생성한 디렉토리에 있는 파일들만 S3로 정송 - on: - all_branches: true - - - provider: codedeploy - access_key_id: $AWS_ACCESS_KEY # Travis repo settings에서 설정된 값 - secret_access_key: $AWS_SECRET_KEY # Travis repo settings에서 설정된 값 - bucket: ldu-springboot-build # S3 버킷 이름 - key: backend_server.zip # 빌드 파일을 압축해서 전달 - bundle_type: zip # 압축 확장자 - application: backend_server # 웹 콘솔에서 등록한 CodeDeploy 애플리케이션 - deployment_group: backend_server-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹 이름 - region: ap-northeast-2 # 지역이름 - wait-until-deployed: true - on: - all_branches: true - +- provider: s3 + access_key_id: "$AWS_ACCESS_KEY" # Travis repo settings에서 설정된 값 + secret_access_key: "$AWS_SECRET_KEY" # Travis repo settings에서 설정된 값 + bucket: ldu-springboot-build # S3 버킷 이름 + region: ap-northeast-2 # 지역이름 + skip_cleanup: true + acl: private # zip 파일 접근을 private으로 설정 + local_dir: deploy # before_deploy에서 생성한 디렉토리에 있는 파일들만 S3로 정송 + on: + all_branches: true +- provider: codedeploy + access_key_id: "$AWS_ACCESS_KEY" # Travis repo settings에서 설정된 값 + secret_access_key: "$AWS_SECRET_KEY" # Travis repo settings에서 설정된 값 + bucket: ldu-springboot-build # S3 버킷 이름 + key: backend_server.zip # 빌드 파일을 압축해서 전달 + bundle_type: zip # 압축 확장자 + application: backend_server # 웹 콘솔에서 등록한 CodeDeploy 애플리케이션 + deployment_group: backend_server-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹 이름 + region: ap-northeast-2 # 지역이름 + wait-until-deployed: true + on: + all_branches: true diff --git a/application.tar.enc b/application.tar.enc new file mode 100644 index 0000000..c2b2a6d Binary files /dev/null and b/application.tar.enc differ diff --git a/build.gradle b/build.gradle index bd7b865..9e07fe8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.clonecode' -version = '1.0.1-SNAPSHOT' +version = '2.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { diff --git a/scripts/deploy.sh b/scripts/deploy.sh index a9974c3..b6869f2 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -35,6 +35,6 @@ chmod +x $JAR_NAME # JAR 파일에 대한 권한이 없으므로 부여함. echo "> $JAR_NAME 실행" -nohup java -jar -Dspring.config.location=classpath:/application.yml,/home/ubuntu/app/application-server.yml $JAR_NAME > $REPOSITORY/nohup.out 2>&1 & # nobup 실행하면 CodeDeploy가 무한 대기하므로 nobup.out을 표준 입출력용으로 별도로 사용함. +nohup java -jar $JAR_NAME > $REPOSITORY/nohup.out 2>&1 & # nobup 실행하면 CodeDeploy가 무한 대기하므로 nobup.out을 표준 입출력용으로 별도로 사용함. # 이렇게 하지 않으면 nohup.out 파일이 생성되지 않고, CodeDeploy 로그에 표준 입출력이 출력됨. # nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야만 함. \ No newline at end of file diff --git a/src/main/java/com/clonecode/inssagram/controller/HeartController.java b/src/main/java/com/clonecode/inssagram/controller/HeartController.java index 926ded6..8c5a725 100644 --- a/src/main/java/com/clonecode/inssagram/controller/HeartController.java +++ b/src/main/java/com/clonecode/inssagram/controller/HeartController.java @@ -1,7 +1,10 @@ package com.clonecode.inssagram.controller; import com.clonecode.inssagram.dto.request.HeartRequestDto; +import com.clonecode.inssagram.dto.response.HeartResponseDto; import com.clonecode.inssagram.service.HeartService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -9,12 +12,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; - +@Api(tags = {"좋아요 API Controller"}) @RestController @RequiredArgsConstructor public class HeartController { private final HeartService heartService; + @ApiOperation(value = "좋아요", notes = "좋아요 기능", response = HeartResponseDto.class) @PostMapping("/api/likes/{postId}") public ResponseEntity toggleHeart(@PathVariable(name = "postId") Long postId, @RequestBody HeartRequestDto requestDto) { diff --git a/src/main/java/com/clonecode/inssagram/controller/MyPageController.java b/src/main/java/com/clonecode/inssagram/controller/MyPageController.java new file mode 100644 index 0000000..4eb1c77 --- /dev/null +++ b/src/main/java/com/clonecode/inssagram/controller/MyPageController.java @@ -0,0 +1,28 @@ +package com.clonecode.inssagram.controller; + +import com.clonecode.inssagram.dto.response.MyPageResponseDto; +import com.clonecode.inssagram.service.PostService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +@Api(tags = {"마이페이지 API Controller"}) +@RestController +@RequestMapping("/api/users") +@RequiredArgsConstructor +public class MyPageController { + private final PostService postService; + + @ApiOperation(value = "마이페이지 조회", notes = "마이페이지 조회 기능", response = MyPageResponseDto.class) + @RequestMapping(value = "/{userId}", method = RequestMethod.GET) + public ResponseEntity getMyPage(@PathVariable(name = "userId") Long userId) { + return new ResponseEntity<>( + postService.getMyPage(userId), + HttpStatus.valueOf(HttpStatus.OK.value())); + } +} diff --git a/src/main/java/com/clonecode/inssagram/controller/PostController.java b/src/main/java/com/clonecode/inssagram/controller/PostController.java index 2222fc2..c64a9f5 100644 --- a/src/main/java/com/clonecode/inssagram/controller/PostController.java +++ b/src/main/java/com/clonecode/inssagram/controller/PostController.java @@ -10,15 +10,18 @@ import com.clonecode.inssagram.global.error.ErrorCode; import com.clonecode.inssagram.jwt.TokenProvider; import com.clonecode.inssagram.service.PostService; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.List; +@Api(tags = {"게시물 API Controller"}) @RestController @RequestMapping("/api/posts") @RequiredArgsConstructor @@ -27,10 +30,10 @@ public class PostController { private final TokenProvider tokenProvider; @ApiOperation(value = "게시물 작성", notes = "게시물 작성 기능", response = PostCreateResponseDto.class) - @PostMapping() + @PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity createPost( - PostRequestDto requestDto, - List imageFileList) { + @RequestPart PostRequestDto requestDto, + @RequestPart List imageFileList) { User user = tokenProvider.getUserFromAuthentication(); if (imageFileList == null) { @@ -45,7 +48,7 @@ public ResponseEntity getPosts() { if (tokenProvider.getUserFromAuthentication() == null) { throw new InvalidValueException(ErrorCode.LOGIN_REQUIRED); } - return new ResponseEntity<>(postService.getAllPosts(), HttpStatus.valueOf(HttpStatus.FOUND.value())); + return new ResponseEntity<>(postService.getAllPosts(), HttpStatus.valueOf(HttpStatus.OK.value())); } @ApiOperation(value = "상세 게시물 조회", notes = "상세 게시물 조회 기능", response = PostDetailResponseDto.class) @@ -54,7 +57,7 @@ public ResponseEntity getPost(@PathVariable Long postId) { if (tokenProvider.getUserFromAuthentication() == null) { throw new InvalidValueException(ErrorCode.LOGIN_REQUIRED); } - return new ResponseEntity<>(postService.getOnePost(postId), HttpStatus.valueOf(HttpStatus.FOUND.value())); + return new ResponseEntity<>(postService.getOnePost(postId), HttpStatus.valueOf(HttpStatus.OK.value())); } @ApiOperation(value = "게시물 수정", notes = "게시물 수정 기능", response = PostUpdateResponseDto.class) diff --git a/src/main/java/com/clonecode/inssagram/controller/UserController.java b/src/main/java/com/clonecode/inssagram/controller/UserController.java index 4584b90..3d99429 100644 --- a/src/main/java/com/clonecode/inssagram/controller/UserController.java +++ b/src/main/java/com/clonecode/inssagram/controller/UserController.java @@ -47,8 +47,8 @@ public ResponseEntity logout(HttpServletRequest request) { @RequestMapping(value = "/users/{userId}", method = RequestMethod.PUT) @ApiOperation(value = "유저 프로필 수정", notes = "유저 프로필 수정 기능") - public ResponseEntity editUserProfile(EditUserProfileRequestDto editUserProfileRequestDto, - List profileImageFile, @PathVariable Long userId) { + public ResponseEntity editUserProfile(@RequestPart EditUserProfileRequestDto editUserProfileRequestDto, + @RequestPart List profileImageFile, @PathVariable Long userId) { userService.editUserProfile(editUserProfileRequestDto, profileImageFile, userId); return new ResponseEntity<>("회원 정보 수정이 성공적으로 반영되었습니다", HttpStatus.valueOf(HttpStatus.OK.value())); } diff --git a/src/main/java/com/clonecode/inssagram/domain/Image.java b/src/main/java/com/clonecode/inssagram/domain/Image.java index ce0bcee..82569b6 100644 --- a/src/main/java/com/clonecode/inssagram/domain/Image.java +++ b/src/main/java/com/clonecode/inssagram/domain/Image.java @@ -15,7 +15,7 @@ public class Image { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn (name = "postId") + @JoinColumn (name = "postId", nullable = false) private Post post; @NotNull diff --git a/src/main/java/com/clonecode/inssagram/domain/Post.java b/src/main/java/com/clonecode/inssagram/domain/Post.java index 307d44d..9ef5bf7 100644 --- a/src/main/java/com/clonecode/inssagram/domain/Post.java +++ b/src/main/java/com/clonecode/inssagram/domain/Post.java @@ -34,9 +34,10 @@ public class Post extends Timestamped { @Column private Long heartNum; - public Post(User user, PostRequestDto postRequestDto) { + public Post(User user, PostRequestDto postRequestDto, Long heartNum) { this.user = user; this.postContents = postRequestDto.getPostContents(); + this.heartNum = heartNum; } public void update(PostRequestDto requestDto){ diff --git a/src/main/java/com/clonecode/inssagram/dto/request/HeartRequestDto.java b/src/main/java/com/clonecode/inssagram/dto/request/HeartRequestDto.java index e64a581..de9a6d5 100644 --- a/src/main/java/com/clonecode/inssagram/dto/request/HeartRequestDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/request/HeartRequestDto.java @@ -1,8 +1,10 @@ package com.clonecode.inssagram.dto.request; +import io.swagger.annotations.ApiModelProperty; import lombok.Getter; @Getter public class HeartRequestDto { + @ApiModelProperty(example = "현재 좋아요 상태") private Long isHeart; } diff --git a/src/main/java/com/clonecode/inssagram/dto/response/HeartResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/HeartResponseDto.java index 73d051d..1581f1e 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/HeartResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/HeartResponseDto.java @@ -1,5 +1,6 @@ package com.clonecode.inssagram.dto.response; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -10,6 +11,8 @@ @AllArgsConstructor @NoArgsConstructor public class HeartResponseDto { + @ApiModelProperty(example = "좋아요 수") private Long heartNum; + @ApiModelProperty(example = "현재 좋아요 상태") private Long isHeart; } diff --git a/src/main/java/com/clonecode/inssagram/dto/response/MyPageResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/MyPageResponseDto.java new file mode 100644 index 0000000..cff4b69 --- /dev/null +++ b/src/main/java/com/clonecode/inssagram/dto/response/MyPageResponseDto.java @@ -0,0 +1,35 @@ +package com.clonecode.inssagram.dto.response; + +import com.clonecode.inssagram.domain.User; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +public class MyPageResponseDto { + + @ApiModelProperty(example = "사용자 정보") + private UserDetailProfileResponseDto user; + + @ApiModelProperty(example = "사용자가 작성한 총 게시글 수") + private Long postTotalNum; + + @ApiModelProperty(example = "사용자가 받은 총 하트 수") + private Long heartTotalNum; + + @ApiModelProperty(example = "사용자가 작성한 게시글 목록") + private List contentList; + + @Builder + public MyPageResponseDto(User user, Long postTotalNum, Long heartTotalNum, + List contentList) { + this.user = new UserDetailProfileResponseDto(user); + this.postTotalNum = postTotalNum; + this.heartTotalNum = heartTotalNum; + this.contentList = contentList; + } + + +} diff --git a/src/main/java/com/clonecode/inssagram/dto/response/PostAllResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/PostAllResponseDto.java index 6a53cd1..6118a8b 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/PostAllResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/PostAllResponseDto.java @@ -3,6 +3,7 @@ import com.clonecode.inssagram.domain.Image; import com.clonecode.inssagram.domain.Post; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Getter; @@ -12,13 +13,23 @@ @Getter public class PostAllResponseDto { + @ApiModelProperty(example = "게시물 DB Id") private Long postId; + @ApiModelProperty(example = "게시물 작성자 정보") private UserProfileResponseDto user; + @ApiModelProperty(example = "게시물 글 내용") private String postContents; + @ApiModelProperty(example = "게시물 사진 URL") private List imageUrlList; + @ApiModelProperty(example = "게시물 하트 수") private Long heartNum; + @ApiModelProperty(example = "게시물 댓글 수") private Long commentNum; + @ApiModelProperty(example = "로그인한 사용자의 게시물 하트 여부") private Long isHeart; + @ApiModelProperty(example = "게시물 댓글 목록") + private List commentList; + @ApiModelProperty(example = "게시물 생성일자") @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime createdAt; @@ -28,6 +39,7 @@ public PostAllResponseDto(Post post, Long heartNum, Long commentNum, Long isHear this.user = new UserProfileResponseDto(post.getUser()); this.postContents = post.getPostContents(); this.imageUrlList = post.getImageList().stream().map(Image::getImageUrl).collect(Collectors.toList()); + this.commentList = post.getCommentList().stream().map(CommentResponseDto::new).limit(2).collect(Collectors.toList()); this.createdAt = post.getCreatedAt(); this.heartNum = heartNum; this.commentNum = commentNum; diff --git a/src/main/java/com/clonecode/inssagram/dto/response/PostCreateResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/PostCreateResponseDto.java index 72e5d56..8aebc5c 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/PostCreateResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/PostCreateResponseDto.java @@ -10,13 +10,13 @@ @Getter public class PostCreateResponseDto { - @ApiModelProperty(example = "게시물 Id") + @ApiModelProperty(example = "게시물 DB Id") @NotBlank private Long postId; - @ApiModelProperty(example = "사진 url") + @ApiModelProperty(example = "게시물 사진 URL") @NotBlank private String imageUrl; - @ApiModelProperty(example = "게시물 좋아요 수") + @ApiModelProperty(example = "게시물 하트 수") @NotBlank private Long heartNum; @ApiModelProperty(example = "게시물 댓글 수") diff --git a/src/main/java/com/clonecode/inssagram/dto/response/PostDetailResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/PostDetailResponseDto.java index 3d99919..3735343 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/PostDetailResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/PostDetailResponseDto.java @@ -3,6 +3,7 @@ import com.clonecode.inssagram.domain.Image; import com.clonecode.inssagram.domain.Post; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Getter; @@ -12,16 +13,24 @@ @Getter public class PostDetailResponseDto { + @ApiModelProperty(example = "게시물 DB Id") private Long postId; + @ApiModelProperty(example = "게시물 작성자 정보") private UserProfileResponseDto user; + @ApiModelProperty(example = "게시물 글 내용") private String postContents; + @ApiModelProperty(example = "게시물 사진 URL") private List imageUrlList; + @ApiModelProperty(example = "게시물 하트 수") private Long heartNum; + @ApiModelProperty(example = "게시물 댓글 수") private Long commentNum; + @ApiModelProperty(example = "로그인한 사용자의 게시물 하트 여부") private Long isHeart; + @ApiModelProperty(example = "게시물 생성일자") @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime createdAt; - + @ApiModelProperty(example = "게시물 댓글 목록") private List commentList; @Builder diff --git a/src/main/java/com/clonecode/inssagram/dto/response/PostUpdateResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/PostUpdateResponseDto.java index e25d7a8..64e7f21 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/PostUpdateResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/PostUpdateResponseDto.java @@ -1,10 +1,12 @@ package com.clonecode.inssagram.dto.response; import com.clonecode.inssagram.domain.Post; +import io.swagger.annotations.ApiModelProperty; import lombok.Getter; @Getter public class PostUpdateResponseDto { + @ApiModelProperty(example = "게시물 글 내용") private String postContents; public PostUpdateResponseDto(Post post){ diff --git a/src/main/java/com/clonecode/inssagram/dto/response/UserDetailProfileResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/UserDetailProfileResponseDto.java new file mode 100644 index 0000000..595ef35 --- /dev/null +++ b/src/main/java/com/clonecode/inssagram/dto/response/UserDetailProfileResponseDto.java @@ -0,0 +1,26 @@ +package com.clonecode.inssagram.dto.response; + +import com.clonecode.inssagram.domain.User; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; + +@Getter +public class UserDetailProfileResponseDto { + + @ApiModelProperty(example = "사용자 DB Id") + private Long userId; + @ApiModelProperty(example = "사용자 이름") + private String username; + @ApiModelProperty(example = "사용자 프로필 사진 URL") + private String profileImageUrl; + @ApiModelProperty(example = "사용자 자기소개") + private String intro; + + public UserDetailProfileResponseDto(User user) { + this.userId = user.getId(); + this.username = user.getUsername(); + this.profileImageUrl = user.getProfileImageUrl(); + this.intro = user.getIntro(); + } + +} diff --git a/src/main/java/com/clonecode/inssagram/dto/response/UserProfileResponseDto.java b/src/main/java/com/clonecode/inssagram/dto/response/UserProfileResponseDto.java index 95b8050..49123d5 100644 --- a/src/main/java/com/clonecode/inssagram/dto/response/UserProfileResponseDto.java +++ b/src/main/java/com/clonecode/inssagram/dto/response/UserProfileResponseDto.java @@ -1,12 +1,16 @@ package com.clonecode.inssagram.dto.response; import com.clonecode.inssagram.domain.User; +import io.swagger.annotations.ApiModelProperty; import lombok.Getter; @Getter public class UserProfileResponseDto { + @ApiModelProperty(example = "사용자 DB Id") private Long userId; + @ApiModelProperty(example = "사용자 이름(별명)") private String username; + @ApiModelProperty(example = "사용자 프로필 사진 URL") private String profileImageUrl; diff --git a/src/main/java/com/clonecode/inssagram/global/error/ErrorCode.java b/src/main/java/com/clonecode/inssagram/global/error/ErrorCode.java index 3a4ef88..68b24b1 100644 --- a/src/main/java/com/clonecode/inssagram/global/error/ErrorCode.java +++ b/src/main/java/com/clonecode/inssagram/global/error/ErrorCode.java @@ -22,6 +22,7 @@ public enum ErrorCode { USER_NOT_FOUND(404, "USER_NOT_FOUND", "해당 유저가 존재하지 않습니다."), PASSWORD_NOT_MATCHED(400, "PASSWORDS_NOT_MATCHED", "비밀번호와 비밀번호 확인이 일치하지 않습니다."), LOGIN_REQUIRED(400, "LOGIN_REQUIRED", "로그인이 필요합니다."), + USER_UNAUTHORIZED(401, "USER_UNAUTHORIZED", "유저 정보는 본인만 수정 가능합니다."), //Post POST_NOT_FOUND(400, "POST_NOT_FOUND", "존재하지 않는 게시글입니다."), diff --git a/src/main/java/com/clonecode/inssagram/repository/PostRepository.java b/src/main/java/com/clonecode/inssagram/repository/PostRepository.java index ab6dc85..dc6762c 100644 --- a/src/main/java/com/clonecode/inssagram/repository/PostRepository.java +++ b/src/main/java/com/clonecode/inssagram/repository/PostRepository.java @@ -1,6 +1,7 @@ package com.clonecode.inssagram.repository; import com.clonecode.inssagram.domain.Post; +import com.clonecode.inssagram.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; @@ -8,4 +9,10 @@ public interface PostRepository extends JpaRepository { List findAllByOrderByCreatedAtDesc(); + List findAllByUserOrderByCreatedAtDesc(User user); + + Long countByUserId(Long userId); + Post findByUserId(Long userId); + + List findAllByUserId(Long userId); } diff --git a/src/main/java/com/clonecode/inssagram/service/HeartService.java b/src/main/java/com/clonecode/inssagram/service/HeartService.java index 90b9191..8df5e89 100644 --- a/src/main/java/com/clonecode/inssagram/service/HeartService.java +++ b/src/main/java/com/clonecode/inssagram/service/HeartService.java @@ -12,10 +12,12 @@ import com.clonecode.inssagram.jwt.TokenProvider; import com.clonecode.inssagram.repository.HeartRepository; import com.clonecode.inssagram.repository.PostRepository; +import com.clonecode.inssagram.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import javax.transaction.Transactional; +import java.util.List; import java.util.Optional; @Service @@ -23,6 +25,7 @@ public class HeartService { private final HeartRepository heartRepository; private final PostRepository postRepository; + private final UserRepository userRepository; private final TokenProvider tokenProvider; @@ -89,4 +92,15 @@ public Long isHeart(Long postId, User user) { } + + public Long totalHeartsByUser(Long userId) { + User user = userRepository.findById(userId). orElseThrow( + () -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); + List postList = postRepository.findAllByUserId(userId); + + Long heartTotalNum = postList.stream().mapToLong(Post::getHeartNum).sum(); + + return heartTotalNum; + } + } diff --git a/src/main/java/com/clonecode/inssagram/service/PostService.java b/src/main/java/com/clonecode/inssagram/service/PostService.java index e970c62..738ce24 100644 --- a/src/main/java/com/clonecode/inssagram/service/PostService.java +++ b/src/main/java/com/clonecode/inssagram/service/PostService.java @@ -1,17 +1,15 @@ package com.clonecode.inssagram.service; -import com.clonecode.inssagram.domain.*; -import com.clonecode.inssagram.dto.request.HeartRequestDto; +import com.clonecode.inssagram.domain.Image; +import com.clonecode.inssagram.domain.Post; +import com.clonecode.inssagram.domain.User; import com.clonecode.inssagram.dto.request.PostRequestDto; import com.clonecode.inssagram.dto.response.*; import com.clonecode.inssagram.exception.EntityNotFoundException; import com.clonecode.inssagram.exception.InvalidValueException; import com.clonecode.inssagram.global.error.ErrorCode; import com.clonecode.inssagram.jwt.TokenProvider; -import com.clonecode.inssagram.repository.CommentRepository; -import com.clonecode.inssagram.repository.HeartRepository; -import com.clonecode.inssagram.repository.ImageRepository; -import com.clonecode.inssagram.repository.PostRepository; +import com.clonecode.inssagram.repository.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -30,14 +28,15 @@ public class PostService { private final StorageService storageService; private final HeartService heartService; private final TokenProvider tokenProvider; + private final UserRepository userRepository; private final HeartRepository heartRepository; @Transactional public ResponseDto writePost(User user, PostRequestDto requestDto, List imageFileList) { List imageUrlList = storageService.uploadFile(imageFileList, "posts/"); //이미지 S3에 업로드 - Post post = new Post(user, requestDto); + Post post = new Post(user, requestDto, 0L); postRepository.save(post); //게시물 db에 저장 - List collect = imageUrlList.stream().map(image->new Image(image,post)).collect(Collectors.toList()); //String List를 Image List로 변환 + List collect = imageUrlList.stream().map(image -> new Image(image, post)).collect(Collectors.toList()); //String List를 Image List로 변환 imageRepository.saveAll(collect); Long commentNum = commentRepository.countByPost(post); //댓글 수 게시물id로 세서 찾기 - 게시물을 저장 먼저 해야 id가 생겨서 count 가능 return ResponseDto.success(PostCreateResponseDto.builder() //responseDto 돌려주기 @@ -57,7 +56,7 @@ public ResponseDto getAllPosts() { for (Post post : posts) { //각 포스트마다 돌면서 Long commentNum = commentRepository.countByPost(post); //댓글 수 게시물id로 세서 찾기 postAllResponseDtoList.add(PostAllResponseDto.builder()//빈 array에 responseDto 만들어서 넣어주기 - .isHeart(heartService.isHeart(post.getId(),user)) + .isHeart(heartService.isHeart(post.getId(), user)) .post(post) .heartNum(post.getHeartNum()) .commentNum(commentNum) @@ -73,11 +72,11 @@ public ResponseDto getOnePost(Long postId) { .orElseThrow(() -> new EntityNotFoundException(ErrorCode.POST_NOT_FOUND)); //없을 시 찾을 수 없음 User user = tokenProvider.getUserFromAuthentication(); Long commentNum = commentRepository.countByPost(post); //댓글 수 게시물id로 세서 찾기 - return ResponseDto.success(PostDetailResponseDto.builder() //responseDto 돌려주기 + return ResponseDto.success(PostDetailResponseDto.builder() //responseDto 돌려주기 .post(post) .heartNum(post.getHeartNum()) .commentNum(commentNum) - .isHeart(heartService.isHeart(post.getId(),user)) + .isHeart(heartService.isHeart(post.getId(), user)) .build()); } @@ -85,8 +84,8 @@ public ResponseDto getOnePost(Long postId) { @Transactional public ResponseDto updateOnePost(Long postId, User user, PostRequestDto requestDto) { Post post = postRepository.findById(postId) //게시물 찾기 - .orElseThrow(() ->new EntityNotFoundException(ErrorCode.POST_NOT_FOUND)); //없을 시 찾을 수 없음 - if(!user.getId().equals(post.getUser().getId())){ //로그인한 사용자=게시물 작성자인지 확인 + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.POST_NOT_FOUND)); //없을 시 찾을 수 없음 + if (!user.getId().equals(post.getUser().getId())) { //로그인한 사용자=게시물 작성자인지 확인 throw new InvalidValueException(ErrorCode.POST_UNAUTHORIZED); //아닐 시 권한 없음 } post.update(requestDto); //맞을 시 업데이트 @@ -97,9 +96,44 @@ public ResponseDto updateOnePost(Long postId, User user, PostRequestDto reque public void deleteOnePost(Long postId, User user) { Post post = postRepository.findById(postId) //게시물 찾기 .orElseThrow(() -> new EntityNotFoundException(ErrorCode.POST_NOT_FOUND)); //없을 시 찾을 수 없음 - if(!user.getId().equals(post.getUser().getId())){ //로그인한 사용자=게시물 작성자인지 확인 + if (!user.getId().equals(post.getUser().getId())) { //로그인한 사용자=게시물 작성자인지 확인 throw new InvalidValueException(ErrorCode.POST_UNAUTHORIZED); //아닐 시 권한 없음 } postRepository.deleteById(postId); //맞을 시 삭제 } + + // 유저가 쓴 글 조회 + @Transactional + public ResponseDto getMyPage(Long userId) { + User user = userRepository.findById(userId).orElseThrow( + () -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); + MyPageResponseDto myPageResponseDto = MyPageResponseDto.builder() + .user(user) + .postTotalNum(postRepository.countByUserId(userId)) + .heartTotalNum(heartService.totalHeartsByUser(userId)) + .contentList(getMyPostList(user)) + .build(); + return ResponseDto.success(myPageResponseDto); + } + + public List getMyPostList(User user) { + List posts = postRepository.findAllByUserOrderByCreatedAtDesc(user); + if (!(posts == null)) { + List contentsList = new ArrayList<>(); + for (Post post : posts) { + Long commentNum = commentRepository.countByPost(post); + PostCreateResponseDto mypostResponseDto = PostCreateResponseDto.builder() + .post(post) + .imageUrl(post.getImageList().get(0).getImageUrl()) + .heartNum(post.getHeartNum()) + .commentNum(commentNum) + .build(); + contentsList.add(mypostResponseDto); + } + return contentsList; + } else { + return null; + } + } + } diff --git a/src/main/java/com/clonecode/inssagram/service/UserService.java b/src/main/java/com/clonecode/inssagram/service/UserService.java index b5ee97b..670ebeb 100644 --- a/src/main/java/com/clonecode/inssagram/service/UserService.java +++ b/src/main/java/com/clonecode/inssagram/service/UserService.java @@ -19,6 +19,7 @@ import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Objects; import java.util.Optional; @RequiredArgsConstructor @@ -87,16 +88,18 @@ public ResponseDto login(LoginRequestDto requestDto) { } //로그아웃 - public ResponseDto logout(HttpServletRequest request) { + public void logout(HttpServletRequest request) { if (!tokenProvider.validateToken(request.getHeader("Refresh-Token"))) { - return ResponseDto.fail(ErrorCode.INVALID_TOKEN); + ResponseDto.fail(ErrorCode.INVALID_TOKEN); + return; } User user = tokenProvider.getUserFromAuthentication(); if (null == user) { - return ResponseDto.fail(ErrorCode.NOT_LOGIN_STATE); + ResponseDto.fail(ErrorCode.NOT_LOGIN_STATE); + return; } - return tokenProvider.deleteRefreshToken(user); + tokenProvider.deleteRefreshToken(user); } @Transactional(readOnly = true) @@ -136,7 +139,15 @@ public void editUserProfile(EditUserProfileRequestDto editUserProfileRequestDto, String userProfileImageUrl = storageService.uploadFile(profileImageFile, "userProfile/").get(0); //영속성 컨텍스트 User 넣음 User user = userRepository.findById(userId).orElseThrow(() -> new InvalidValueException(ErrorCode.USER_NOT_FOUND)); - + + User userFromAuthentication = tokenProvider.getUserFromAuthentication(); + + //로그인한 사람과 변경하는 유저 프로필의 값이 똑같아야 변경가능함. + if (!Objects.equals(user.getId(), userFromAuthentication.getId())) { + //다르면 exception + throw new InvalidValueException(ErrorCode.USER_UNAUTHORIZED); + } + //영속성 컨텍스트 update 변경 user.update(editUserProfileRequestDto, userProfileImageUrl); // Transaction 끝날 때 컨텍스트 스탭샷과 비교하여 변경 감지 후 flush되며 update 쿼리 나감. diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ac50716..f07500a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,23 @@ spring: + # h2: + # console: + # enabled: true + + # datasource: + # url: jdbc:h2:mem:inssagram + # driver-class-name: org.h2.Driver + # username: sa + # password: + + datasource: + url: jdbc:mysql://ldudb.cs3wzmj0phr7.ap-northeast-2.rds.amazonaws.com:3306/inssagram + username: ${MYSQLID} + password: ${MYSQLPWD} + jpa: show_sql: true hibernate: - ddl-auto: update + ddl-auto: none properties: hibernate: format_sql: true @@ -21,3 +36,15 @@ server: # echo 'spring-boot-security-jwt-hanghae-assignment-spring-boot-security-jwt-secret-key' | base64 jwt: secret: 4oCYc3ByaW5nLWJvb3Qtc2VjdXJpdHktand0LWhhbmdoYWUtYXNzaWdubWVudC1zcHJpbmctYm9vdC1zZWN1cml0eS1qd3Qtc2VjcmV0LWtleeKAmQo= + +cloud: + aws: + credentials: + access-key: ${ACCESSKEY} + secret-key: ${SECRETKEY} + s3: + bucket: inssagram + region: + static: ap-northeast-2 + stack: + auto: false \ No newline at end of file