Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ out/

### VS Code ###
.vscode/

# 데이터베이스 데이터 폴더 제외
/mysql_data/

# OS 관련 임시 파일
.DS_Store
Thumbs.db
15 changes: 11 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.3'
id 'org.springframework.boot' version '3.5.11'
id 'io.spring.dependency-management' version '1.1.7'
}

Expand All @@ -26,13 +26,20 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.13'
// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'
}

tasks.named('test') {
Expand Down
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3.8'
services:
db:
image: mysql:8.0
container_name: spring-study-db
ports:
- "3307:3306"
environment:
MYSQL_ROOT_PASSWORD: tngus3872
MYSQL_DATABASE: spring-1team
volumes:
- ./mysql_data:/var/lib/mysql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

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

@EnableJpaAuditing
@SpringBootApplication
public class Spring1teamApplication {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.study.spring1team.domain.comment.controller;

import com.study.spring1team.domain.comment.dto.CommentRequestDTO;
import com.study.spring1team.domain.comment.service.CommentService;
import com.study.spring1team.global.apiPayload.ApiResponse;
import com.study.spring1team.global.apiPayload.code.GeneralSuccessCode;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class CommentController {

private final CommentService commentService;

@PostMapping("/posts/{postId}/comments")
public ApiResponse<String> createComment(
@PathVariable Long postId,
@Valid @RequestBody CommentRequestDTO request
) {
commentService.createComment(postId, request);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, "댓글이 성공적으로 작성되었습니다.");
}

@DeleteMapping("/comments/{commentId}")
public ApiResponse<String> deleteComment(@PathVariable Long commentId) {
commentService.deleteComment(commentId);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, "댓글이 성공적으로 삭제되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.study.spring1team.domain.comment.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;

@Getter
public class CommentRequestDTO {

@NotBlank(message = "댓글을 입력해주세요.")
private String content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.study.spring1team.domain.comment.entity;

import com.study.spring1team.domain.post.entity.Post;
import com.study.spring1team.domain.user.entity.User;
import com.study.spring1team.global.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Comment extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, columnDefinition = "TEXT")
private String content;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User author;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.study.spring1team.domain.comment.repository;

import com.study.spring1team.domain.comment.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentRepository extends JpaRepository<Comment, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.study.spring1team.domain.comment.service;

import com.study.spring1team.domain.comment.dto.CommentRequestDTO;
import com.study.spring1team.domain.comment.entity.Comment;
import com.study.spring1team.domain.comment.repository.CommentRepository;
import com.study.spring1team.domain.post.entity.Post;
import com.study.spring1team.domain.post.repository.PostRepository;
import com.study.spring1team.domain.user.entity.User;
import com.study.spring1team.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class CommentService {

private final CommentRepository commentRepository;
private final PostRepository postRepository;
private final UserRepository userRepository;

// 유저 고정값 설정
private static final Long DEFAULT_USER_ID = 1L;

public Long createComment(Long postId, CommentRequestDTO request) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다."));

User user = userRepository.findById(DEFAULT_USER_ID)
.orElseThrow(() -> new IllegalArgumentException("해당 사용자가 없습니다."));

Comment comment = Comment.builder()
.content(request.getContent())
.author(user)
.post(post)
.build();

return commentRepository.save(comment).getId();
}

public void deleteComment(Long commentId) {
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new IllegalArgumentException("해당 댓글이 없습니다."));

commentRepository.delete(comment);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.study.spring1team.controller;
package com.study.spring1team.domain.ping.controller;

import com.study.spring1team.global.apiPayload.ApiResponse;
import com.study.spring1team.global.apiPayload.code.GeneralSuccessCode;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -9,7 +11,7 @@
public class PingController {

@GetMapping("/ping")
public String ping() {
return "pong";
public ApiResponse ping() {
return ApiResponse.onSuccess(GeneralSuccessCode.OK,"pong");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.study.spring1team.domain.post.controller;

import com.study.spring1team.domain.post.entity.Post;
import com.study.spring1team.domain.post.service.PostService;
import com.study.spring1team.global.apiPayload.ApiResponse;
import com.study.spring1team.domain.post.dto.PostRequestDTO;
import com.study.spring1team.global.apiPayload.code.GeneralSuccessCode;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

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

@PostMapping()
public ApiResponse<String> createPost(@Valid @RequestBody PostRequestDTO request) {
postService.createPost(request);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, "게시글이 성공적으로 생성되었습니다.");
}

@GetMapping
public List<Post> getPosts() {
return postService.getPostList();
}

@GetMapping("/{postId}")
public ApiResponse<Post> getPost(@PathVariable Long postId) {
return ApiResponse.onSuccess(GeneralSuccessCode.OK, postService.getPost(postId));
}

@PutMapping("/{postId}")
public ApiResponse<String> updatePost(
@PathVariable Long postId,
@Valid @RequestBody PostRequestDTO request
) {
postService.updatePost(postId, request);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, "게시글이 성공적으로 수정되었습니다.");
}

@DeleteMapping("/{postId}")
public ApiResponse<String> deletePost(@PathVariable Long postId) {
postService.deletePost(postId);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, "게시글이 성공적으로 삭제되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.study.spring1team.domain.post.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class PostRequestDTO {

@NotBlank(message = "제목은 필수 입력 항목입니다.")
@Size(min = 1, max = 50, message = "제목은 1자 이상 50자 이하로 입력해주세요.")
private String title;

@NotBlank(message = "내용은 필수 입력 항목입니다.")
private String content;

private Long categoryId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.study.spring1team.domain.post.dto;

public class PostResponseDTO {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.study.spring1team.domain.post.entity;

import com.study.spring1team.global.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Category extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, length = 20)
private String name;

@OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
private List<Post> postList = new ArrayList<>();
}
41 changes: 41 additions & 0 deletions src/main/java/com/study/spring1team/domain/post/entity/Post.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.study.spring1team.domain.post.entity;

import com.study.spring1team.domain.comment.entity.Comment;
import com.study.spring1team.domain.user.entity.User;
import com.study.spring1team.global.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Post extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Setter
@Column(nullable = false, length = 50)
private String title;

@Setter
@Column(columnDefinition = "TEXT", nullable = false)
private String content;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User author; // 작성자 매핑

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category; // 카테고리 매핑

@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> commentList = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.study.spring1team.domain.post.repository;

import com.study.spring1team.domain.post.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Long> {
}
Loading