Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions LeeKunHee/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

runtimeOnly 'com.h2database:h2' //임시 저장.
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;


@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 이 부분 추가
@SpringBootApplication
@EnableJpaAuditing
public class AssignmentApplication {
public static void main(String[] args) {
SpringApplication.run(AssignmentApplication.class, args);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.leets.assignment.entity.comment;
package com.leets.assignment.domain.comment.entity;

import com.leets.assignment.entity.baseEntity.BaseEntity;
import com.leets.assignment.entity.user.User;
import com.leets.assignment.entity.post.Post;
import com.leets.assignment.global.baseEntity.BaseEntity;
import com.leets.assignment.domain.user.entity.User;
import com.leets.assignment.domain.post.entity.Post;
import jakarta.persistence.*;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.leets.assignment.domain.post.controller;

import com.leets.assignment.domain.post.dto.req.PostRequestDTO;
import com.leets.assignment.domain.post.dto.res.PostResponseDTO;
import com.leets.assignment.domain.post.service.PostService;
import com.leets.assignment.global.common.ApiResponse;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {

private final PostService postService;

// 1. 게시글 작성
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse<PostResponseDTO.PostDetailResDTO> createPost(
@Valid @RequestBody PostRequestDTO.CreatePostDTO request
) {
PostResponseDTO.PostDetailResDTO result = postService.createPost(request);
return ApiResponse.onSuccess("POST201_1", "게시글 작성에 성공했습니다.", result);
}

// 2. 게시글 전체 목록 조회
@GetMapping
public ApiResponse<List<PostResponseDTO.PostListResDTO>> getPostList() {
List<PostResponseDTO.PostListResDTO> result = postService.getPostList();
return ApiResponse.onSuccess("POST200_1", "게시글 목록 조회에 성공했습니다.", result);
}

// 3. 게시글 상세 조회
@GetMapping("/{postId}")
public ApiResponse<PostResponseDTO.PostDetailResDTO> getPost(@PathVariable Long postId) {
PostResponseDTO.PostDetailResDTO result = postService.getPost(postId);
return ApiResponse.onSuccess("POST200_2", "게시글 상세 조회에 성공했습니다.", result);
}

// 4. 게시글 수정
@PatchMapping("/{postId}")
public ApiResponse<PostResponseDTO.PostDetailResDTO> updatePost(
@PathVariable Long postId,
@Valid @RequestBody PostRequestDTO.UpdatePostDTO request
) {
PostResponseDTO.PostDetailResDTO result = postService.updatePost(postId, request);
return ApiResponse.onSuccess("POST200_3", "게시글이 수정되었습니다.", result);
}

// 5. 게시글 삭제
@DeleteMapping("/{postId}")
public ApiResponse<Void> deletePost(
@PathVariable Long postId,
@RequestParam Long userId // 쿼리 파라미터(?userId=1)로 작성자 ID를 받음
) {
postService.deletePost(postId, userId);
// 삭제는 반환할 데이터가 없으므로 result에 null을 넣습니다.
return ApiResponse.onSuccess("POST200_4", "게시글 삭제에 성공했습니다.", null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.leets.assignment.domain.post.dto.req;

import com.leets.assignment.domain.post.entity.BlockType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;

public class PostRequestDTO {

// 게시글 생성용 DTO
@Getter
@NoArgsConstructor
public static class CreatePostDTO {
@NotBlank(message = "제목을 입력해주세요.")
@Size(max = 255, message = "제목은 최대 255자까지 가능합니다.")
private String title;

@NotNull(message = "작성자 ID는 필수입니다.")
private Long userId;

@NotEmpty(message = "내용을 입력해주세요.")
@Valid // 내부 블록들의 검증을 수행하기 위해 필수!
private List<BlockDTO> blocks;
}

// 공통 블록 DTO (생성/수정 모두 사용)
@Getter
@NoArgsConstructor
public static class BlockDTO {
@NotNull(message = "순서는 필수입니다.")
private Integer sequence;

@NotNull(message = "블록 타입은 필수입니다.")
private BlockType blockType;

@NotBlank(message = "내용을 입력해주세요.") // 각 블록의 내용이 비었을 때
private String content;
}

// 게시글 수정용 DTO
@Getter
@NoArgsConstructor
public static class UpdatePostDTO {
@NotNull(message = "수정자 ID는 필수입니다.") // 권한 확인을 위해
private Long userId;

@NotBlank(message = "제목을 입력해주세요.")
@Size(max = 255, message = "제목은 최대 255자까지 가능합니다.")
private String title;

@NotEmpty(message = "내용을 입력해주세요.") // 리스트가 비어있으면 거부
@Valid
private List<BlockDTO> blocks;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.leets.assignment.domain.post.dto.res;

import com.leets.assignment.domain.post.entity.BlockType;
import com.leets.assignment.domain.post.entity.Post;
import com.leets.assignment.domain.post.entity.PostBlock;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.List;

public class PostResponseDTO {

// 1. 게시글 목록 조회용 (나중에 목록 기능 만들 때 사용)
@Builder
public record PostListResDTO (
Long postId,
String title,
String nickname,
LocalDateTime createdAt
){
public static PostListResDTO from(Post post) {
return PostListResDTO.builder()
.postId(post.getPostId())
.title(post.getTitle())
.nickname(post.getUser() != null ? post.getUser().getNickname() : "알 수 없음")
.createdAt(post.getCreatedAt())
.build();
}
}

// 2. 게시글 상세 조회용 (현재 구현 중인 기능)
@Builder
public record PostDetailResDTO (
Long postId,
String title,
String nickname,
List<BlockResDTO> blocks,
LocalDateTime createdAt,
LocalDateTime updatedAt
){
// 이 메서드가 있어야 Service에서 .from(post)를 호출할 수 있습니다!
public static PostDetailResDTO from(Post post) {
return PostDetailResDTO.builder()
.postId(post.getPostId())
.title(post.getTitle())
.nickname(post.getUser() != null ? post.getUser().getNickname() : "알 수 없음")
.createdAt(post.getCreatedAt())
.updatedAt(post.getUpdatedAt())
.blocks(post.getBlocks().stream()
.map(BlockResDTO::from) // 아래 BlockResDTO.from 호출
.toList())
.build();
}
}

// 3. 블록 상세 정보
@Builder
public record BlockResDTO (
Long blockId,
Integer sequence,
BlockType blockType,
String content
){
public static BlockResDTO from(PostBlock block) {
return BlockResDTO.builder()
.blockId(block.getBlockId())
.sequence(block.getSequence())
.blockType(block.getBlockType())
.content(block.getContent())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.leets.assignment.domain.post.entity;

public enum BlockType {
TEXT, IMAGE
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.leets.assignment.entity.post;
package com.leets.assignment.domain.post.entity;

import com.leets.assignment.entity.baseEntity.BaseEntity;
import com.leets.assignment.entity.user.User;
import com.leets.assignment.global.baseEntity.BaseEntity;
import com.leets.assignment.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -33,4 +35,12 @@ private Post(String title, User user) {
this.title = title;
this.user = user;
}

public void softDelete() {
this.deletedAt = LocalDateTime.now();
}

public void update(String title) {
this.title = title;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.leets.assignment.entity.post;
package com.leets.assignment.domain.post.entity;

import jakarta.persistence.*;
import lombok.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.leets.assignment.domain.post.exception;

public class PostForbiddenException extends RuntimeException {
public PostForbiddenException() {
super("수정 권한이 없습니다.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러마다 예외처리를 하기 보다, Post도메인의 공통 예외처리부분을 만들어두고 에러코드를 ENUM으로 관리하면 더 효율적이고 깔끔하게 관리할 수 있을 것 같습니다!

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.leets.assignment.domain.post.exception;

// RuntimeException을 상속받아야 서비스에서 쉽게 던질 수 있습니다.
public class PostNotFoundException extends RuntimeException {
public PostNotFoundException() {
super("해당 게시글을 찾을 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.leets.assignment.domain.post.repository;

import com.leets.assignment.domain.post.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
// 기본적으로 save(), findById(), delete() 등을 제공.
}
Loading