diff --git a/build.gradle b/build.gradle index deca6ed..e92e6d3 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test' testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' } tasks.named('test') { diff --git a/src/main/java/com/likelion/session/SessionApplication.java b/src/main/java/com/likelion/session/SessionApplication.java index 658e562..e67810e 100644 --- a/src/main/java/com/likelion/session/SessionApplication.java +++ b/src/main/java/com/likelion/session/SessionApplication.java @@ -10,4 +10,4 @@ public static void main(String[] args) { SpringApplication.run(SessionApplication.class, args); } -} +} \ No newline at end of file diff --git a/src/main/java/com/likelion/session/controller/BoardController.java b/src/main/java/com/likelion/session/controller/BoardController.java index 2af74e7..b11135b 100644 --- a/src/main/java/com/likelion/session/controller/BoardController.java +++ b/src/main/java/com/likelion/session/controller/BoardController.java @@ -11,12 +11,12 @@ import java.util.List; -@RestController -@RequestMapping("/boards") -@RequiredArgsConstructor +@RestController // JSON 형태로 객체 데이터를 반환하는 컨트롤러임을 명시 +@RequestMapping("/boards") // API 요청 경로의 공통 시작점 설정 +@RequiredArgsConstructor // final이 붙은 BoardService 객체를 생성자 주입 방식으로 받아옴 (DI) public class BoardController { - private final ; + private final BoardService boardService; /* 게시글 생성 @@ -32,44 +32,44 @@ public class BoardController { -> 결과 반환 -> JSON 응답 */ - @Operation( + @Operation( // Swagger: API 문서에 제목과 상세 설명을 표시 summary = "게시글 생성", description = "새로운 게시글을 생성합니다." ) - @ + @PostMapping // HTTP POST 요청 처리: 주로 데이터를 생성할 때 사용 public ResponseEntity create(@RequestBody BoardCreateRequest request) { BoardResponse response = boardService.create(request); - return ResponseEntity.ok(response); + return ResponseEntity.ok(response); // 성공 응답(200 OK)과 함께 데이터 반환 } // 게시글 전체 조회 - @Operation( + @Operation( // API 기능 요약 summary = "게시글 전체 조회", description = "등록된 모든 게시글을 조회합니다." ) - @ + @GetMapping // HTTP GET 요청 처리: 주로 데이터를 조회할 때 사용 public ResponseEntity> findAll() { List response = boardService.findAll(); return ResponseEntity.ok(response); } // 게시글 단건 조회 - @Operation( + @Operation( // API 기능 요약 summary = "게시글 단건 조회", description = "id로 특정 게시글을 조회합니다." ) - @GetMapping("/{id}") + @GetMapping("/{id}") // 경로에 포함된 {id} 값을 변수로 사용함 public ResponseEntity findById(@PathVariable Long id) { BoardResponse response = boardService.findById(id); return ResponseEntity.ok(response); } // 게시글 수정 - @Operation( + @Operation( // API 기능 요약 summary = "게시글 수정", description = "id로 특정 게시글의 제목과 내용을 수정합니다." ) - @("/{id}") + @PutMapping("/{id}") // HTTP PUT 요청 처리: 주로 전체 데이터를 수정할 때 사용 public ResponseEntity update(@PathVariable Long id, @RequestBody BoardUpdateRequest request) { BoardResponse response = boardService.update(id, request); @@ -77,13 +77,13 @@ public ResponseEntity update(@PathVariable Long id, } // 게시글 삭제 - @Operation( + @Operation( // API 기능 요약 summary = "게시글 삭제", description = "id로 특정 게시글을 삭제합니다." ) - @("/{id}") + @DeleteMapping("/{id}") // HTTP DELETE 요청 처리: 데이터를 삭제할 때 사용 public ResponseEntity delete(@PathVariable Long id) { boardService.delete(id); - return ResponseEntity.noContent().build(); + return ResponseEntity.noContent().build(); // 삭제 성공 시 보낼 데이터가 없으므로 204 No Content 반환 } } \ No newline at end of file diff --git a/src/main/java/com/likelion/session/domain/Board.java b/src/main/java/com/likelion/session/domain/Board.java index cb873de..804d18a 100644 --- a/src/main/java/com/likelion/session/domain/Board.java +++ b/src/main/java/com/likelion/session/domain/Board.java @@ -10,55 +10,54 @@ @Getter // getter 메서드 자동 생성 @Entity // 해당 클래스 DB 테이블로 인식하고 관리 -@Table(name = "boards") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor +@Table(name = "boards") // 실제 DB에 생성될 테이블 이름을 "boards"로 지정 +@NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본 생성자 자동 생성, PROTECTED로 설정하여 무분별한 객체 생성 방지 +@AllArgsConstructor // 모든 필드를 파라미터로 받는 생성자 자동 생성 public class Board { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long ; + @Id // 테이블의 기본키(PK)임을 나타냄 + @GeneratedValue(strategy = GenerationType.IDENTITY) // PK 값을 DB가 자동으로 증가시켜줌 (MySQL Auto Increment) + private Long id; // 게시글 제목 - @Column(nullable = false, length = 100) - private String ; + @Column(nullable = false, length = 100) // null 불가, 최대 길이는 100자로 제한 + private String title; // 게시글 내용 - @Column(nullable = false, columnDefinition = "TEXT") - private String ; + @Column(nullable = false, columnDefinition = "TEXT") // null 불가, 일반 문자열보다 긴 텍스트를 담기 위해 타입을 TEXT로 지정 + private String content; // 작성자 - @Column(nullable = false, length = 30) - private String ; + @Column(nullable = false, length = 30) // null 불가, 최대 길이는 30자로 제한 + private String writer; // 생성 시간 - @Column(nullable = false) - private LocalDateTime ; + @Column(nullable = false) // null 불가 + private LocalDateTime createdAt; // 수정 시간 - @Column(nullable = false) - private LocalDateTime ; + @Column(nullable = false) // null 불가 + private LocalDateTime updatedAt; - - public Board(String title, String content, String writer) { + public Board(String title, String content, String writer) { // 새로운 게시글 객체를 처음 만들 때 사용 this.title = title; this.content = content; this.writer = writer; } - @PrePersist + @PrePersist // 데이터가 처음 DB에 저장(persist)되기 직전에 자동으로 실행됨 public void prePersist() { - this.createdAt = LocalDateTime.now(); - this.updatedAt = LocalDateTime.now(); + this.createdAt = LocalDateTime.now(); // 생성 시간 기록 + this.updatedAt = LocalDateTime.now(); // 수정 시간도 생성 시간과 동일하게 초기화 } - @PreUpdate + @PreUpdate // 데이터가 수정(update)되기 직전에 자동으로 실행됨 public void preUpdate() { this.updatedAt = LocalDateTime.now(); - } - + } // 수정 시간을 현재 시간으로 갱신 - public void update(String title, String content) { + // 게시글 수정을 위한 메서드 + public void update(String title, String content) { // 이미 만들어진 게시글의 내용을 바꿀 때 사용 this.title = title; this.content = content; } diff --git a/src/main/java/com/likelion/session/dto/BoardCreateRequest.java b/src/main/java/com/likelion/session/dto/BoardCreateRequest.java index 62b1821..59abf4e 100644 --- a/src/main/java/com/likelion/session/dto/BoardCreateRequest.java +++ b/src/main/java/com/likelion/session/dto/BoardCreateRequest.java @@ -5,13 +5,13 @@ import lombok.NoArgsConstructor; import lombok.Setter; -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor +@Getter // Lombok: 데이터를 꺼내오기 위한 getter 메서드 자동 생성 +@Setter // Lombok: 데이터를 필드에 넣어주기 위한 setter 메서드 자동 생성 +@NoArgsConstructor // Lombok: 파라미터가 없는 기본 생성자 생성 +@AllArgsConstructor // Lombok: 모든 필드를 파라미터로 받는 생성자 생성 public class BoardCreateRequest { // 넘겨주고 싶은 정보: 제목(title), 내용(content), 작성자(writer) - private String ; - private String ; - private String ; + private String title; + private String content; + private String writer; } \ No newline at end of file diff --git a/src/main/java/com/likelion/session/dto/BoardResponse.java b/src/main/java/com/likelion/session/dto/BoardResponse.java index dc6e74f..13ea5ba 100644 --- a/src/main/java/com/likelion/session/dto/BoardResponse.java +++ b/src/main/java/com/likelion/session/dto/BoardResponse.java @@ -6,9 +6,9 @@ import java.time.LocalDateTime; -@Getter -@AllArgsConstructor -@Builder +@Getter // Lombok: 응답 데이터를 꺼내 JSON으로 변환하기 위해 필요함 +@AllArgsConstructor // Lombok: 모든 데이터를 담아 객체를 만들기 위한 생성자 생성 +@Builder // Lombok: 빌더 패턴을 사용하여 객체 생성을 유연하고 가독성 있게 해줌 public class BoardResponse { // 돌려주고 싶은 응답: id, title, content, writer, createdAt, updatedAt diff --git a/src/main/java/com/likelion/session/dto/BoardUpdateRequest.java b/src/main/java/com/likelion/session/dto/BoardUpdateRequest.java index d1e98e4..0a68bdb 100644 --- a/src/main/java/com/likelion/session/dto/BoardUpdateRequest.java +++ b/src/main/java/com/likelion/session/dto/BoardUpdateRequest.java @@ -7,15 +7,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; -@Getter -@Setter -@NoArgsConstructor +@Getter // Lombok: 데이터 조회를 위한 getter 메서드 생성 +@Setter // Lombok: 데이터 수정을 위한 setter 메서드 생성 +@NoArgsConstructor // Lombok: 기본 생성자 생성 public class BoardUpdateRequest { - @NotBlank(message = "제목은 필수입니다.") - @Size(max = 100, message = "제목은 100자 이하로 입력해주세요.") + @NotBlank(message = "제목은 필수입니다.") // Validation: 빈 값, 공백만 있는 값을 허용하지 않음 + @Size(max = 100, message = "제목은 100자 이하로 입력해주세요.") // Validation: 최대 100자까지만 허용 private String title; - @NotBlank(message = "내용은 필수입니다.") + @NotBlank(message = "내용은 필수입니다.") // Validation: 내용이 비어있는지 검사함 private String content; } diff --git a/src/main/java/com/likelion/session/service/BoardService.java b/src/main/java/com/likelion/session/service/BoardService.java index 1bc58af..a59f7a1 100644 --- a/src/main/java/com/likelion/session/service/BoardService.java +++ b/src/main/java/com/likelion/session/service/BoardService.java @@ -12,13 +12,13 @@ import java.util.List; -@Slf4j -@Service -@RequiredArgsConstructor -@Transactional +@Slf4j // Lombok: 로깅(로그 출력)을 위한 객체를 생성함 +@Service // 이 클래스가 비즈니스 로직을 담당하는 서비스 레이어임을 Spring에 알림 +@RequiredArgsConstructor // final이 붙은 필드(boardRepository)에 대한 생성자를 자동으로 생성하여 의존성 주입(DI)을 해줌 +@Transactional // DB 작업 도중 에러가 나면 모든 작업을 이전으로 롤백(Rollback)하여 데이터 일관성을 유지함 public class BoardService { - private final ; + private final BoardRepository boardRepository; /* 게시글 생성 @@ -27,16 +27,16 @@ public class BoardService { - Repository를 통해 DB에 저장 - 저장된 결과를 Response DTO로 변환해서 반환 */ - public BoardResponse create(BoardCreateRequest request) { + public BoardResponse create(BoardCreateRequest request) { // DTO를 엔티티(Entity)로 변환하는 것 Board board = new Board( request.getTitle(), request.getContent(), request.getWriter() ); - Board savedBoard = boardRepository.save(board); + Board savedBoard = boardRepository.save(board); // Repository를 통해 DB에 저장 - return BoardResponse.builder() + return BoardResponse.builder() // 저장된 엔티티 정보를 DTO에 담아 반환 .id(savedBoard.getId()) .title(savedBoard.getTitle()) .content(savedBoard.getContent()) @@ -51,11 +51,11 @@ public BoardResponse create(BoardCreateRequest request) { - DB에 있는 모든 게시글을 가져옴 - Entity 리스트를 Response DTO 리스트로 변환 */ - @Transactional(readOnly = true) + @Transactional(readOnly = true) // 조회 전용 설정으로 성능 최적화에 도움을 줌 public List findAll() { return boardRepository.findAll() .stream() - .map(board -> BoardResponse.builder() + .map(board -> BoardResponse.builder() // 각 엔티티 객체를 DTO 객체로 변환 (Mapping) .id(board.getId()) .title(board.getTitle()) .content(board.getContent()) @@ -63,7 +63,7 @@ public List findAll() { .createdAt(board.getCreatedAt()) .updatedAt(board.getUpdatedAt()) .build()) - .toList(); + .toList(); // 리스트 형태로 반환 } /* @@ -71,7 +71,7 @@ public List findAll() { - id로 게시글 조회 - 없으면 예외 발생 */ - @Transactional(readOnly = true) + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public BoardResponse findById(Long id) { Board board = boardRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id)); @@ -92,11 +92,11 @@ public BoardResponse findById(Long id) { - 엔티티의 update 메서드로 값 변경 - JPA 변경 감지로 update 반영 */ - public BoardResponse update(Long id, BoardUpdateRequest request) { + public BoardResponse update(Long id, BoardUpdateRequest request) { // 게시글 수정 메서드 Board board = boardRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id)); - board.update(request.getTitle(), request.getContent()); + board.update(request.getTitle(), request.getContent()); // 엔티티 객체의 정보를 수정함 return BoardResponse.builder() .id(board.getId()) @@ -113,10 +113,10 @@ public BoardResponse update(Long id, BoardUpdateRequest request) { - id로 게시글을 찾고 - 있으면 삭제 */ - public void delete(Long id) { + public void delete(Long id) { // 게시글 삭제 메서드 Board board = boardRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id)); - boardRepository.delete(board); + boardRepository.delete(board); // Repository를 통해 DB에서 삭제함 } } \ No newline at end of file