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
26 changes: 25 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version = '0.0.1-SNAPSHOT'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(23)
}
}

Expand All @@ -31,6 +31,30 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//implementation 'org.springframework.boot:spring-boot-starter-security'

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'

annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'com.auth0:java-jwt:4.4.0'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-mail'
}

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

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

@SpringBootApplication
@EnableJpaAuditing
public class Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.gsm._8th.class4.backed.task._1._1.domain.DTO;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ArticleDTO {
private Long idx;
private String title;
private String content;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime updatedAt;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.gsm._8th.class4.backed.task._1._1.domain.controller;

import com.gsm._8th.class4.backed.task._1._1.domain.DTO.ArticleDTO;
import com.gsm._8th.class4.backed.task._1._1.domain.service.ArticleService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {

private final ArticleService articleService;

@PostMapping
public ResponseEntity<String> posting(@RequestBody ArticleDTO dto) {
articleService.savePost(dto);
return ResponseEntity.status(201).body("Article 리소스 생성에 성공함");
}


@GetMapping
public ResponseEntity<List<ArticleDTO>> read() {
List<ArticleDTO> list = articleService.findList();
return ResponseEntity.ok(list);
}

@GetMapping("/{idx}")
public ResponseEntity<ArticleDTO> reading(@PathVariable Long idx) {
ArticleDTO post = articleService.readPost(idx);
return ResponseEntity.ok(post);
}

@PatchMapping("/{idx}")
public ResponseEntity<String> sujeong(
@PathVariable Long idx,
@RequestBody ArticleDTO dto) {
articleService.sujeong(idx, dto);
return ResponseEntity.ok("게시글이 수정되었습니다.");
}

@DeleteMapping("/{idx}")
public ResponseEntity<String> deleteByTitle(@PathVariable Long idx) {
articleService.sakjeByTitle(idx);
return ResponseEntity.noContent().build();
}
Comment on lines +37 to +49
Copy link
Member

Choose a reason for hiding this comment

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

메서드명 영어로 올바르게 통일해주세요

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.gsm._8th.class4.backed.task._1._1.domain.entity;

import com.gsm._8th.class4.backed.task._1._1.global.entity.BaseIdxEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.*;

@Entity
@Table(name = "articles")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ArticleEntity extends BaseIdxEntity {

@Column(nullable = false)
private String title;

@Column(nullable = false)
private String content;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.gsm._8th.class4.backed.task._1._1.domain.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleNotFound(IllegalArgumentException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}
Comment on lines +11 to +15
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 핸들링하면 모든 IllegalArgumentException을 핸들링하게 되서 예외 클래스를 따로 생성하는 등의 방식으로 하여 세부화하여 핸들링 해주시면 좋겠습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gsm._8th.class4.backed.task._1._1.domain.repository;

import com.gsm._8th.class4.backed.task._1._1.domain.entity.ArticleEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ArticleRepository extends JpaRepository<ArticleEntity, Long> {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.gsm._8th.class4.backed.task._1._1.domain.service;

import com.gsm._8th.class4.backed.task._1._1.domain.DTO.ArticleDTO;
import com.gsm._8th.class4.backed.task._1._1.domain.entity.ArticleEntity;
import com.gsm._8th.class4.backed.task._1._1.domain.repository.ArticleRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


@Service
@Transactional
@RequiredArgsConstructor
public class ArticleService {
private final ArticleRepository articleRepository;

public void savePost(ArticleDTO ArticleDTO) {
ArticleEntity articleEntity = ArticleEntity.builder()
.title(ArticleDTO.getTitle())
.content(ArticleDTO.getContent())
.build();
articleRepository.save(articleEntity);
}

public List<ArticleDTO> findList() {
List<ArticleEntity> posts = articleRepository.findAll();
if (posts.isEmpty()) {
return Collections.emptyList(); // 데이터가 없으면 200 OK + 빈 리스트([]) 반환

Choose a reason for hiding this comment

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

따로 Empty를 감지하고, Collections.emptyList()를 반환한 이유가 무엇인가요?

}
return posts.stream()
.map(post -> new ArticleDTO(
post.getIdx(),
post.getTitle(),
post.getContent(),
post.getCreatedAt(),
post.getUpdatedAt()
))
.collect(Collectors.toList());
}

public ArticleDTO readPost(Long idx) {
ArticleEntity post = articleRepository.findById(idx)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글입니다."));

return new ArticleDTO(

Choose a reason for hiding this comment

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

정말 필요한 수정 사항은 아니지만, Entity to DTO를 담당하는 코드를 함수로 분리하는것도 좋은방법일것 같습니다.

post.getIdx(),
post.getTitle(),
post.getContent(),
post.getCreatedAt(),
post.getUpdatedAt()
);
}

public void sujeong(Long idx, ArticleDTO dto) {
ArticleEntity post = articleRepository.findById(idx)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다."));

post.setTitle(dto.getTitle());
post.setContent(dto.getContent());
Comment on lines +63 to +64
Copy link
Member

Choose a reason for hiding this comment

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

Setter(설정자) 사용은 지양하는게 좋습니다! Entity에서의 사용은 더더욱 지양해야하고요.
설정자의 사용은 개발자로 하여금 데이터를 빠르고 쉽게 수정가능 하도록 해주지만 다른 개발자로서 해당 행동이 왜 필요하고 최종적으로 어떤 목적으로 수정되는지 한번에 알기 어렵게 하고 외부에서 Entity가 의도치 않게 변경될 수 있습니다


articleRepository.save(post);
}

public void sakjeByTitle(Long idx) {
ArticleEntity post = articleRepository.findById(idx)
.orElseThrow(() -> new IllegalArgumentException("해당 ID의 게시글이 존재하지 않습니다."));

articleRepository.delete(post);
}




}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@Getter
@MappedSuperclass
abstract class BaseIdxEntity {
public abstract class BaseIdxEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx", nullable = false, updatable = false, insertable = false, unique = true)
Expand Down
9 changes: 6 additions & 3 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ spring:
name: task.1-1
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${RDB_HOST:localhost}:${RDB_PORT:3306}/${RDB_NAME:task_1_1}
url: jdbc:mysql://${RDB_HOST:localhost}:${RDB_PORT:3306}/${RDB_NAME:task}
username: ${RDB_USER:root}
password: ${RDB_PASSWORD:123456}
password: ${RDB_PASSWORD}
jpa:
hibernate:
ddl-auto: ${DDL_AUTO:update}
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
dialect: org.hibernate.dialect.MySQL8Dialect
jackson:
deserialization:
fail-on-unknown-properties: true