이 글은 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 책을 참고하여 작성되었습니다.
(출처)
@Controller
와 JSP/Freemaker 등의 뷰 템플릿 영역이다. 외부 요청과 응답에대한 전반적인 영역이다.@Service
와 @Transactional
이 사용되는 영역이며 컨트롤러와 Dao의 중간 영역에서 사용된다.@Entity
가 사용된 영역 역시 도메인 모델이나 반드시 DB 테이블과 관계가 있어야 하는건 아니다. VO(Value Object) 또한 이 영역에 해당하기 때문이다.PostsApiController.java
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto) {
return postsService.save(requestDto);
}
}
PostsService.java
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save (PostsSaveRequestDto requestsDto) {
return postsRepository.save(requestsDto.toEntity()).getId();
}
}
@Autowired
, setter, 생성자가 있는데 가장 권장하는 방식이 생성자이다. 생성자로 Bean 객체를 받는 방식으로 @AutoWired
를 대신한다. 생성자는 @ReiquiredArgsConstructor
가 대신 생성해준다.PostsSaveRequestDto
@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;
@Builder
public PostsSaveRequestDto(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
public Posts toEntity() {
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
@WebMvcTest
대신 @SpringBootTest
와 TestRestTemplate
을 사용한다.PostsApiController.java
@RequiredArgsConstructor
@RestController
public class PostsApiController {
...
// 수정
@PutMapping("/api/v1/posts/{id}")
public Long update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto) {
return postsService.update(id, requestDto);
}
// 조회
@GetMapping("/api/v1/posts/{id}")
public PostsResponseDto findById (@PathVariable Long id) {
return postsService.findById(id);
}
}
PostsResponseDto.java
@Getter
public class PostsResponseDto {
... // 필드 선언 생략
public PostsResponseDto (Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.author = entity.getAuthor();
}
}
PostsResponseDto
는 Entity의 일부 필드만 사용하므로 생성자로 Entity를 받아 필요한 값을 필드에 넣는다.PostsUpdateRequestDto.java
@Getter
public class PostsResponseDto {
... // 필드 선언 생략
@Builder
public PostsUpdateRequestDto(String title, String content) {
this.title = title;
this.content = content;
}
}
Posts.java
@Getter
public class PostsResponseDto {
...
public void update(String title, String content) {
this.title = title;
this.content = content;
}
}
PostsService.java
@Getter
public class PostsResponseDto {
...
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {
Posts posts = postsRepository.findById(id)
.orElseThrow(()->new
IllegalArgumentException("해당 게시글이 없습니다. id="+id));
posts.update(requestDto.getTitle(), requestDto.getContent());
return id;
}
public PostsResponseDto findById (Long id) {
Posts entity = postsRepository.findById(id)
.orElseThrow(() -> new
IllegalArgumentException("해당 게시글이 없습니다. id="+id));
return new PostsResponseDto(entity);
}
}
이 부분은 Spring boot 버전 변화 후 이슈가 좀 발생 했었다.
application.properties에 spring.h2.console.enabled=true
spring.datasource.hikari.jdbc-url=jdbc:h2:mem:testdb;MODE=MYSQL
두 줄을 추가하니깐 성공했다. 만약 안 된다면 링크의 3번째 댓글의 스크린샷을 참고하여 직접 jdbc url을 붙여넣어보자.
각 게시글에 수정 날짜, 등록 날짜 등을 간편하게 등록하기 위해 사용한다.
BaseTimeEntity.java
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
@MappedSuperclass
: JPA Entity 클래스들이 해당 클래스 상속 시 필드들도 칼럼으로 인식하게 한다.@EntityListeners(AuditingEntityListener.class)
: 해당 클래스에 Auditing 기능을 포함시킨다.@CreatedDate
: Entity의 생성 후 저장 시의 시간이 자동 저장된다.@LastModifiedDate
: Entity 변경 시의 시간이 자동 저장된다.이후 Posts 클래스가 BaseTimeEntity를 상속 받고록하고, Application 클래스에 @EnableJpaAuditing
을 추가해주면 기능이 제대로 활성화 된다.