[Mlog] 나만의 블로그 만들기 - 기능 개발

노의빈·2023년 10월 16일
0

Mlog

목록 보기
4/13

📌 DTO

먼저 각 기능에 필요한 DTO를 만들었다.

PostDto

public class PostDto {
    @Data
    @NoArgsConstructor
    public static class ListDto {
        private Integer id;
        private String title;
        private String previewContent;
        private String thumbnail;
        private LocalDateTime writingTime;

        @Builder
        public ListDto(Integer id, String title, String previewContent, String thumbnail, LocalDateTime writingTime) {
            this.id = id;
            this.title = title;
            this.previewContent = previewContent;
            this.thumbnail = thumbnail;
            this.writingTime = writingTime;
        }
    }
    @Data
    public static class DetailDto {
        private Integer id;
        private String title;
        private String content;
        private LocalDateTime writingTime;

        @Builder
        public DetailDto(Integer id, String title, String content, LocalDateTime writingTime) {
            this.id = id;
            this.title = title;
            this.content = content;
            this.writingTime = writingTime;
        }
    }
    @Data
    @NoArgsConstructor
    public static class AddDto {
        private String title;
        private String content;
        private String previewContent;
        private String thumbnail;
        private List<String> fileList;
        private Boolean visible;

        public Post toEntity(List<PostFile> fileList) {
            return Post.builder()
                    .title(title)
                    .content(content)
                    .previewContent(previewContent)
                    .writingTime(LocalDateTime.now())
                    .thumbnail(thumbnail)
                    .fileList(fileList)
                    .visible(visible)
                    .build();
        }
        @Builder
        public AddDto(String title, String content, String previewContent, String thumbnail, List<String> fileList, Boolean visible) {
            this.title = title;
            this.content = content;
            this.previewContent = previewContent;
            this.thumbnail = thumbnail;
            this.fileList = fileList;
            this.visible = visible;
        }
    }
    @Data
    @NoArgsConstructor
    public static class DeleteDto {
        private Integer id;

        public DeleteDto(Integer id) {
            this.id = id;
        }
    }
    @Data
    @NoArgsConstructor
    public static class ModifyDto {
        private Integer id;
        private String title;
        private String content;
        private String previewContent;
        private String thumbnail;
        private List<String> fileList;
        private Boolean visible;

        @Builder
        public ModifyDto(Integer id, String title, String content, String previewContent, String thumbnail, List<String> fileList, Boolean visible) {
            this.id = id;
            this.title = title;
            this.content = content;
            this.previewContent = previewContent;
            this.thumbnail = thumbnail;
            this.fileList = fileList;
            this.visible = visible;
        }
    }
}

ProjectDto

public class ProjectDto {
    @Data
    public static class ListDto {
        private Integer id;
        private String title;
        private String thumbnail;
        private String summary;
        private String skills;
        private String learning;
        private String duration;
        private LocalDateTime writingTime;
        @Builder
        public ListDto(Integer id, String title, String thumbnail, String summary, String skills, String learning, String duration, LocalDateTime writingTime) {
            this.id = id;
            this.title = title;
            this.thumbnail = thumbnail;
            this.summary = summary;
            this.skills = skills;
            this.learning = learning;
            this.duration = duration;
            this.writingTime = writingTime;
        }
    }
    @Data
    public static class DetailDto {
        private Integer id;
        private String title;
        private String content;
        private String summary;
        private String duration;
        private String skills;
        private String learning;

        @Builder
        public DetailDto(Integer id, String title, String content, String summary, String duration, String skills, String learning) {
            this.id = id;
            this.title = title;
            this.content = content;
            this.summary = summary;
            this.duration = duration;
            this.skills = skills;
            this.learning = learning;
        }
    }
    @Data
    @NoArgsConstructor
    public static class AddDto {
        private String title;
        private String content;
        private String thumbnail;
        private String duration;
        private String skills;
        private String learning;
        private String summary;
        private Boolean visible;

        @Builder
        public AddDto(String title, String content, String thumbnail, String duration, String skills, String learning, String summary, Boolean visible) {
            this.title = title;
            this.content = content;
            this.thumbnail = thumbnail;
            this.duration = duration;
            this.skills = skills;
            this.learning = learning;
            this.summary = summary;
            this.visible = visible;
        }

        public Project toEntity() {
            return Project.builder()
                    .title(title)
                    .content(content)
                    .thumbnail(thumbnail)
                    .summary(summary)
                    .duration(duration)
                    .skills(skills)
                    .learning(learning)
                    .writingTime(LocalDateTime.now())
                    .visible(visible)
                    .build();
        }
    }
    @Data
    @NoArgsConstructor
    public static class DeleteDto {
        private Integer id;
        @Builder
        public DeleteDto(Integer id) {
            this.id = id;
        }
    }
    @Data
    @NoArgsConstructor
    public static class ModifyDto {
        private Integer id;
        private String title;
        private String content;
        private String summary;
        private String thumbnail;
        private String duration;
        private String skills;
        private String learning;
        private Boolean visible;

        @Builder
        public ModifyDto(Integer id, String title, String content, String summary, String thumbnail, String duration, String skills, String learning, Boolean visible) {
            this.id = id;
            this.title = title;
            this.content = content;
            this.summary = summary;
            this.thumbnail = thumbnail;
            this.duration = duration;
            this.skills = skills;
            this.learning = learning;
            this.visible = visible;
        }
    }
}

AdminDto

package com.web.mlog_server.mvc.admin;

import lombok.Builder;
import lombok.Data;

import java.time.LocalDateTime;

public class AdminDto {
    @Data
    public static class LoginDto {
        private String id;
        private String password;
    }

    @Data
    public static class TableDto {
        private Integer id;
        private String title;
        private LocalDateTime writingTime;
        private Boolean visible;

        @Builder
        public TableDto(Integer id, String title, LocalDateTime writingTime, Boolean visible) {
            this.id = id;
            this.title = title;
            this.writingTime = writingTime;
            this.visible = visible;
        }
    }
}

📌 Controller

각 기능들을 호출할 수 있도록 Controller를 작성해주었다.

@RestController
@RequestMapping("/api/post")
@Slf4j
@RequiredArgsConstructor
public class PostController {
    private final PostService postService;

    @GetMapping("")
    public List<PostDto.ListDto> getPostList() {
        return postService.getPostList();
    }
    @GetMapping("/{id}")
    public PostDto.DetailDto getPostDetail(@PathVariable("id") int id) {
        return postService.getPostDetail(id);
    }

    @PostMapping("")
    public boolean postAdd(@RequestBody PostDto.AddDto dto) {
        return postService.addPost(dto);
    }

    @DeleteMapping("")
    public boolean changeVisibility(@RequestBody PostDto.DeleteDto dto) {
        return postService.changeVisibility(dto.getId());
    }

    @PutMapping("")
    public boolean postModify(@RequestBody PostDto.ModifyDto dto) {
        return postService.modifyPost(dto);
    }

    @GetMapping("/preview")
    public List<PostDto.ListDto> getPreviewPost() {
        return postService.getPreviewPost();
    }

    @PostMapping("/file")
    public String fileUpload(MultipartFile file) {
        return postService.uploadFile(file);
    }
}

ProjectController

@RestController
@RequestMapping("/api/project")
@Slf4j
@RequiredArgsConstructor
public class ProjectController {
    private final ProjectService projectService;

    @GetMapping("/preview")
    public List<ProjectDto.ListDto> getPreviewProject() {
        return projectService.getPreviewPost();
    }
    @GetMapping("")
    public List<ProjectDto.ListDto> projectList() {
        return projectService.getProjectList();
    }
    @GetMapping("/{id}")
    public ProjectDto.DetailDto projectDetail(@PathVariable("id") Integer id) {
        return projectService.getProjectDetail(id);
    }
    @PostMapping("")
    public boolean projectAdd(@RequestBody ProjectDto.AddDto dto) {
        return projectService.addProject(dto);
    }
    @DeleteMapping("")
    public boolean changeVisibility(@RequestBody ProjectDto.DeleteDto dto) {
        return projectService.changeVisibility(dto);
    }
    @PutMapping("")
    public boolean projectModify(@RequestBody ProjectDto.ModifyDto dto) {
        return projectService.modifyProject(dto);
    }
}

AdminController

@RestController
@RequestMapping("/api/admin")
@Slf4j
@Secured("ADMIN")
@RequiredArgsConstructor
public class AdminController {
    private final AdminService adminService;

    @PostMapping("/login")
    public TokenInfo login(@RequestBody AdminDto.LoginDto dto) {
        return adminService.login(dto.getId(), dto.getPassword());
    }

    @PostMapping("/auth")
    public boolean isAuth() {
        return true;
    }

    @GetMapping("/postList")
    public List<AdminDto.TableDto> getAllPosts() {
        return adminService.getAllPosts();
    }
    @GetMapping("/projectList")
    public List<AdminDto.TableDto> getAllProjects() {
        return adminService.getAllProjects();
    }

    @GetMapping("/post/{id}")
    public PostDto.DetailDto getPostDetail(@PathVariable("id") Integer id) {
        return adminService.getPostDetail(id);
    }
}

📌 Service

각 기능들을 실제로 수행하는 Service단의 코드를 작성하였다.

PostService

@Service
@Slf4j
@RequiredArgsConstructor
public class PostService {
    private final PostRepository postRepository;
    private final PostFileRepository postFileRepository;
    private final FileUtil fileUtil;

    public List<PostDto.ListDto> getPreviewPost() {
        return postRepository.findTop3ByVisibleIsTrueOrderByIdDesc()
                .stream()
                .map(Post::toListDto)
                .toList();
    }

    public List<PostDto.ListDto> getPostList() {
        return postRepository.findAllByVisibleIsTrueOrderByIdDesc()
                .stream().map(Post::toListDto)
                .toList();
    }
    public PostDto.DetailDto getPostDetail(int id) {
        return postRepository.findByIdAndVisibleIsTrue(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 포스트입니다."))
                .toDetailDto();
    }
    @Transactional
    public boolean addPost(PostDto.AddDto dto) {
        try {
            List<PostFile> fileList = postFileRepository.findAllById(dto.getFileList());
            Post post = postRepository.save(dto.toEntity(fileList));
            for (PostFile postFile : fileList) {
                postFile.setPost(post);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "포스트 업로드에 실패하였습니다.");
        }
        return true;
    }
    @Transactional
    public boolean changeVisibility(int id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 포스트입니다."));
        try {
            post.setVisible(!post.getVisible());
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "포스트 수정에 실패했습니다.");
        }
        return true;
    }

    @Transactional
    public boolean modifyPost(PostDto.ModifyDto dto) {
        Post post = postRepository.findById(dto.getId())
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 포스트입니다."));
        try {
            post.modifyPost(dto);
            List<PostFile> fileList = postFileRepository.findAllById(dto.getFileList());
            for (PostFile postFile : fileList) {
                postFile.setPost(post);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "포스트 수정에 실패했습니다.");
        }
        return true;
    }

    public String uploadFile(MultipartFile file) {
        PostFile postFile = fileUtil.getPostFile(file);
        postFileRepository.save(postFile);
        fileUtil.uploadFile(file, postFile.getFileName());

        return postFile.getFileName();
    }
}

ProjectService

@Service
@Slf4j
@RequiredArgsConstructor
public class ProjectService {
    private final ProjectRepository projectRepository;
    private final ProjectFileRepository projectFileRepository;

    public List<ProjectDto.ListDto> getPreviewPost() {
        return projectRepository.findTop3ByVisibleIsTrueOrderByIdDesc()
                .stream()
                .map(Project::toListDto)
                .toList();
    }
    public List<ProjectDto.ListDto> getProjectList() {
        return projectRepository.findAllByVisibleTrue()
                .stream()
                .map(Project::toListDto)
                .toList();
    }
    public ProjectDto.DetailDto getProjectDetail(Integer id) {
        return projectRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트입니다."))
                .toDetailDto();
    }
    public boolean addProject(ProjectDto.AddDto dto) {
        try {
            projectRepository.save(dto.toEntity());
        } catch (Exception e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "프로젝트 업로드에 실패했습니다.");
        }
        return true;
    }
    @Transactional
    public boolean changeVisibility(ProjectDto.DeleteDto dto) {
        try {
            Project project = projectRepository.findById(dto.getId())
                    .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트입니다."));
            project.setVisible(!project.getVisible());
        } catch (Exception e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "프로젝트 삭제에 실패했습니다.");
        }
        return true;
    }
    @Transactional
    public boolean modifyProject(ProjectDto.ModifyDto dto) {
        try {
            Project project = projectRepository.findById(dto.getId())
                    .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트입니다."));
            project.modifyProject(dto);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "프로젝트 수정에 실패했습니다.");
        }
        return true;
    }
}

AdminService

@Service
@Slf4j
@RequiredArgsConstructor
public class AdminService {
    private final AdminRepository adminRepository;
    private final PostRepository postRepository;
    private final ProjectRepository projectRepository;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final JwtProvider jwtProvider;

    @Transactional
    public TokenInfo login(String id, String password) {
        //--- 1. ID, 암호화된 PW를 기반으로 Authentication 객체 생성
        // 이 때 authentication 은 인증 여부를 확인하는 authenticated 값이 false 로 설정되어있음.
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(id, password);
        //--- 2. 실제 검증 과정 (사용자 비밀번호 확인)
        // authenticate 함수가 실행되면, CustomUserDetailsService 에서 구현한 loadUserByUsername 함수가 자동으로 실행 됨.
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(token);
        Admin admin = adminRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "입력된 정보와 일치하는 사용자가 없습니다."));
        //--- 3. 인증 정보를 기반으로 JWT 생성
        TokenInfo tokenInfo = jwtProvider.generateToken(authentication);
        return tokenInfo;
    }

    public List<AdminDto.TableDto> getAllPosts() {
        return postRepository.findAll()
                .stream()
                .map(Post::toTableDto)
                .toList();
    }
    public List<AdminDto.TableDto> getAllProjects() {
        return projectRepository.findAll()
                .stream()
                .map(Project::toTableDto)
                .toList();
    }
    public PostDto.DetailDto getPostDetail(Integer id) {
        return postRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 포스트입니다."))
                .toDetailDto();
    }
}

📌 Repository

DB와 연결된 Repository에 추가적으로 필요한 코드를 작성하였다.
Spring data JPA를 사용하였기 때문에 CRUD 작성은 하지 않아도 되었다.

PostRepository

@Repository
public interface PostRepository extends JpaRepository<Post, Integer> {
    List<Post> findTop3ByVisibleIsTrueOrderByIdDesc();
    List<Post> findAllByVisibleIsTrueOrderByIdDesc();
    Optional<Post> findByIdAndVisibleIsTrue(Integer id);
}

PostFileRepository

@Repository
public interface PostFileRepository extends JpaRepository<PostFile, String> {
}

ProjectRepository

@Repository
public interface ProjectRepository extends JpaRepository<Project, Integer> {
    List<Project> findTop3ByVisibleIsTrueOrderByIdDesc();
    List<Project> findAllByVisibleTrue();
}

ProjectFileRepository

@Repository
public interface ProjectFileRepository extends JpaRepository<ProjectFile, String> {
}

AdminRepository

@Repository
public interface AdminRepository extends JpaRepository<Admin, String> {
}

다음은 Spring Security 적용을 할 것이다.

profile
백엔드 공부 중입니다.

0개의 댓글