먼저 각 기능에 필요한 DTO를 만들었다.
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;
}
}
}
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;
}
}
}
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를 작성해주었다.
@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);
}
}
@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);
}
}
@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
@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();
}
}
@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;
}
}
@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();
}
}
DB와 연결된 Repository에 추가적으로 필요한 코드를 작성하였다.
Spring data JPA
를 사용하였기 때문에 CRUD 작성은 하지 않아도 되었다.
@Repository
public interface PostRepository extends JpaRepository<Post, Integer> {
List<Post> findTop3ByVisibleIsTrueOrderByIdDesc();
List<Post> findAllByVisibleIsTrueOrderByIdDesc();
Optional<Post> findByIdAndVisibleIsTrue(Integer id);
}
@Repository
public interface PostFileRepository extends JpaRepository<PostFile, String> {
}
@Repository
public interface ProjectRepository extends JpaRepository<Project, Integer> {
List<Project> findTop3ByVisibleIsTrueOrderByIdDesc();
List<Project> findAllByVisibleTrue();
}
@Repository
public interface ProjectFileRepository extends JpaRepository<ProjectFile, String> {
}
@Repository
public interface AdminRepository extends JpaRepository<Admin, String> {
}
다음은 Spring Security
적용을 할 것이다.