본문 바로가기

photogram 리팩토링

photogram RestApi 리팩토링 일기 2일차

728x90

리팩토링 하기전 소스코드

UserApiController

 @PutMapping("/user/{id}")
    public CMResDto<?> update(@PathVariable long id,
                              @Valid UserUpdateDto userUpdateDto,
                              BindingResult bindingResult,
                              @AuthenticationPrincipal PrincipalDetails principalDetails
                              ){
User userEntity = userService.memberUpdate(id, userUpdateDto);

return new CMResDto<>(1,"회원수정완료",userEntity);// 응답시에 유저 엔티티의 모든 getter 함수가 호출되고 JSON으로 파싱하여 응답한다.
}

UserService

@Transactional
public User memberUpdate(long id, UserUpdateDto userUpdateDto,@AuthenticationPrincipal PrincipalDetails principalDetails){
    //1 영속화
    User userEntity=userRepository.findById(id).orElseThrow(()->{return new CustomVaildationApiException("찾을 수 없는 Id입니다");});
    //2.영속화된 오브젝트를 수정 - 더티체킹 (업데이트완료)
    userEntity.updateUser(
            bCryptPasswordEncoder.encode(userUpdateDto.getPassword()),
            userUpdateDto.getName(),
            userUpdateDto.getWebsite(),
            userUpdateDto.getBio(),
            userUpdateDto.getPhone(),
            userUpdateDto.getGender());
    //3 principal 세션변경
    principalDetails.setUser(userEntity);



    return userEntity;
}

문제점: Entity의 모든 스팩이 노출이 되므로 비밀번호 등 필요없는 정보도 노출이 됨.

 

리팩토링 후 소스코드 (Dto를 별도로 생성해서 Service로직에서 Dto 반환)

 

UserUpdateDtoRes.java

package com.cos.photogramstart.web.dto.user;

import com.cos.photogramstart.domain.user.User;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Data
public class UserUpdateDtoRes {

    private String name;
    private String username;
    private String website;
    private String bio;
    private String email;
    private String phone;
    private String gender;

    public UserUpdateDtoRes(User user) {
        this.name = user.getName();
        this.username = user.getUsername();
        this.website = user.getWebsite();
        this.bio = user.getBio();
        this.email = user.getEmail();
        this.phone = user.getPhone();
        this.gender = user.getGender();
    }
}

UserApiController

@PutMapping("/user/{id}")
public CMResDto<?> update(@PathVariable long id,
                          @Valid UserUpdateDto userUpdateDto,
                          BindingResult bindingResult,
                          @AuthenticationPrincipal PrincipalDetails principalDetails
                          ){


    UserUpdateDtoRes userUpdateDtoRes = userService.memberUpdate(id, userUpdateDto, principalDetails);

    return new CMResDto<>(1,"회원수정완료",userUpdateDtoRes);// 응답시에 유저 엔티티의 모든 getter 함수가 호출되고 JSON으로 파싱하여 응답한다.


}

UserService

@Transactional
public UserUpdateDtoRes memberUpdate(long id, UserUpdateDto userUpdateDto,@AuthenticationPrincipal PrincipalDetails principalDetails){
    //1 영속화
    User userEntity=userRepository.findById(id).orElseThrow(()->{return new CustomVaildationApiException("찾을 수 없는 Id입니다");});
    //2.영속화된 오브젝트를 수정 - 더티체킹 (업데이트완료)
    userEntity.updateUser(
            bCryptPasswordEncoder.encode(userUpdateDto.getPassword()),
            userUpdateDto.getName(),
            userUpdateDto.getWebsite(),
            userUpdateDto.getBio(),
            userUpdateDto.getPhone(),
            userUpdateDto.getGender());
    //3 principal 세션변경
    principalDetails.setUser(userEntity);

    //4. dto에 Entity데이터 담아주기
    UserUpdateDtoRes userUpdateDtoRes=new UserUpdateDtoRes(userEntity);

    return userUpdateDtoRes;
}

공통응답DTO 클래스를 만들어서 code 및 Message , 데이터까지 넘겨주는 형식으로 진행하였습니다.

@AllArgsConstructor
@NoArgsConstructor
@Data
public class CMResDto<T>{
    private int code; // 1(성공),-1(실패)
    private String message;
    private T data;
}

 

 

RestApiController 리팩토링 완료. AOP 처리까지해서 전처리과정을 하나의 클래스로 처리

CommentApiController

package com.cos.photogramstart.web.api;


import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.Comment.Comment;
import com.cos.photogramstart.handler.ex.CustomVaildationApiException;
import com.cos.photogramstart.handler.ex.CustomVaildationException;
import com.cos.photogramstart.service.CommentService;
import com.cos.photogramstart.web.api.dto.CommentResDto;
import com.cos.photogramstart.web.dto.CMResDto;
import com.cos.photogramstart.web.dto.comment.CommentDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequiredArgsConstructor
public class CommentApiController {

    private final CommentService commentService;

    @PostMapping("/api/comment")
    public ResponseEntity<?> commentSave(@Valid @RequestBody CommentDto commentDto,
                                         BindingResult bindingResult,
                                         @AuthenticationPrincipal PrincipalDetails principalDetails){

        CommentResDto commentResDto = commentService.applywrite(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());//content,imageId,userId
        return new ResponseEntity<>(new CMResDto<>(1,"댓글쓰기성공",commentResDto), HttpStatus.CREATED);
    }

    @DeleteMapping("/api/comment/{id}")
    public ResponseEntity<?> commentDelete(@PathVariable long id){

        commentService.applyDelete(id);

        return new ResponseEntity<>(new CMResDto<>(1,"삭제성공",null),HttpStatus.OK);
    }
}

 

ImageApiController

 

package com.cos.photogramstart.web.api;

import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.image.Image;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.service.LikesService;
import com.cos.photogramstart.web.api.dto.ImageResDto;
import com.cos.photogramstart.web.dto.CMResDto;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
public class ImageApiController {


    private final ImageService imageService;

    private final LikesService likesServcie;

    @GetMapping("/api/image")
    public ResponseEntity<?> imageStory(@AuthenticationPrincipal PrincipalDetails principalDetails,
                                        @RequestParam(value="offset",defaultValue = "0") int offset,
                                        @RequestParam(value="limit",defaultValue = "100") int limit,

                                        @PageableDefault(size=3,sort="id",direction = Sort.Direction.DESC)Pageable pageable){

        List<Image> images =  imageService.imageStory(principalDetails.getUser().getId(),offset,limit);
        List<ImageResDto> collect = images.stream().map(i -> new ImageResDto(i)).collect(Collectors.toList());

        return new ResponseEntity<>(new CMResDto<>(1,"성공",collect),HttpStatus.OK);
    }

    @PostMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> likes(@AuthenticationPrincipal PrincipalDetails principalDetails,
                                   @PathVariable long imageId){
        likesServcie.likes(imageId,principalDetails.getUser().getId());
        return new ResponseEntity<>(new CMResDto<>(1,"좋아요성공",null), HttpStatus.CREATED);
    }
    @DeleteMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> unlikes(@AuthenticationPrincipal PrincipalDetails principalDetails,
                                   @PathVariable long imageId){
        likesServcie.cancellikes(imageId,principalDetails.getUser().getId());
        return new ResponseEntity<>(new CMResDto<>(1,"좋아요취소성공",null), HttpStatus.OK);
    }

}

SubscribeApiController

package com.cos.photogramstart.web.api;


import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.service.SubscribeService;
import com.cos.photogramstart.web.dto.CMResDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class SubscribeApiController {

    private final SubscribeService subscribeService;


    @PostMapping("/api/subscribe/{toUserId}")
    public ResponseEntity<?> subscribe(@PathVariable long toUserId, @AuthenticationPrincipal PrincipalDetails principalDetails) {
        subscribeService.subscribe(principalDetails.getUser().getId(), toUserId);
        return new ResponseEntity<>(new CMResDto<>(1, "구독하기 성공!", null), HttpStatus.OK);
    }


    @DeleteMapping("/api/subscribe/{toUserId}")
    public ResponseEntity<?> unsubscribe(@PathVariable long toUserId, @AuthenticationPrincipal PrincipalDetails principalDetails) {
        subscribeService.unsubscribe(principalDetails.getUser().getId(), toUserId);
        return new ResponseEntity<>(new CMResDto<>(1, "구독취소하기 성공!", null), HttpStatus.OK);
    }
}

UserApiController

package com.cos.photogramstart.web.api;

import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.subscribe.Subscribe;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.handler.ex.CustomVaildationApiException;
import com.cos.photogramstart.handler.ex.CustomVaildationException;
import com.cos.photogramstart.service.SubscribeService;
import com.cos.photogramstart.service.UserService;
import com.cos.photogramstart.web.dto.CMResDto;
import com.cos.photogramstart.web.dto.subscribe.subscribeResponseDto;
import com.cos.photogramstart.web.dto.user.UserUpdateDto;
import com.cos.photogramstart.web.dto.user.UserUpdateDtoRes;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
@RestController
public class UserApiController {

    private final UserService userService;

    private final SubscribeService subscribeService;

    @PostMapping("/api/user/{principalId}/profileImageUrl")
    public ResponseEntity<?> profileImageUrlUpdate(@PathVariable long principalId,
                                                   MultipartFile profileImageFile,
                                                   @AuthenticationPrincipal PrincipalDetails principalDetails){

            userService.profileimageupdate(principalId,profileImageFile,principalDetails);

            return new ResponseEntity<>(new CMResDto<>(1,"프로필 사진변경 성공",null),HttpStatus.OK);
    }

    @GetMapping("/api/user/{pageUserId}/subscribe")
    public ResponseEntity<?> subscribeList(@PathVariable long pageUserId,@AuthenticationPrincipal PrincipalDetails principalDetails){

        List<subscribeResponseDto> subscribeResponseDtos=subscribeService.subscribeList(principalDetails.getUser().getId(),pageUserId);

        return new ResponseEntity<>(new CMResDto<>(1,"구독자 정보 리스트 가져오기 성공",subscribeResponseDtos), HttpStatus.OK);
    }

    @PutMapping("/user/{id}")
    public CMResDto<?> update(@PathVariable long id,
                              @Valid UserUpdateDto userUpdateDto,
                              BindingResult bindingResult,
                              @AuthenticationPrincipal PrincipalDetails principalDetails
                              ){


        UserUpdateDtoRes userUpdateDtoRes = userService.memberUpdate(id, userUpdateDto, principalDetails);

        return new CMResDto<>(1,"회원수정완료",userUpdateDtoRes);// 응답시에 유저 엔티티의 모든 getter 함수가 호출되고 JSON으로 파싱하여 응답한다.


    }
}

 

 

핵심결론: Controller는 호출하고 DTO로 반환시켜줘야함. Open in view를 꺼놓고 DTO를 서비스 로직에서

반환시켜놓고 Controller단에서는 호출 및 반환만 잘해주면 된다.

728x90

'photogram 리팩토링' 카테고리의 다른 글

photogram 스크롤시 페이징 처리  (0) 2023.02.19
photogram 리팩토링 일기  (0) 2023.02.17