Table of Contents
API ๋ง๋ค๊ธฐ
API๋ฅผ ๋ง๋ค์ด๋ณด๋ฉฐ ๊ตฌ์กฐ ์ดํดํ๊ธฐ
ํ์ํ ํด๋์ค
- Request ๋ฐ์ดํฐ ๋ฐ์ Dto
- API ์์ฒญ ๋ฐ์ Controller
- ํธ๋์ญ์ , ๋๋ฉ์ธ ๊ธฐ๋ฅ ๊ฐ ์์๋ฅผ ๋ณด์ฅํ๋ Service (๋น์ง๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ์ง๋ ์์)
Spring ์น ๊ณ์ธต

Web Layer
- ์ปจํธ๋กค๋ฌ์ JSP๋ฑ์ ๋ทฐ ํ ํ๋ฆฟ ์์ญ
- Filter, ์ธํฐ์ ํธ, ControllerAdvice ๋ฑ ์ธ๋ถ ์์ฒญ๊ณผ ์๋ต์ ๋ํ ์ ๋ฐ์ ์ธ ์์ญ
Service Layer
- @Service์ ์ฌ์ฉ๋๋ ์์ญ
- Controller์ Dao ์ค๊ฐ์์ ์ฌ์ฉ๋จ
- @Transactional์ด ์ฌ์ฉ๋์ด์ผ ํจ
Repository Layer
- ๋ฐ์ดํฐ ์ ์ฅ์์ ์ ๊ทผํ๋ ์์ญ
DTOs
- Data Transfer Object
- ๊ณ์ธต ๊ฐ ๋ฐ์ดํฐ ๊ตํ์ ์ํ ๊ฐ์ฒด DTO์ ์์ญ
- ๋ทฐ ํ ํ๋ฆฟ ์์ง์์ ์ฌ์ฉ๋๊ฑฐ๋ Repository Later์์ ๊ฒฐ๊ณผ๋ก ๋์ด์จ ๊ฐ์ฒด๋ค์ด ํด๋น๋จ
Domain Model
- @Entity๊ฐ ์ฌ์ฉ๋ ์์ญ
- VO(๊ฐ ๊ฐ์ฒด)๋ ์ด ์์ญ์ ํด๋น๋๋ฏ๋ก, ๋ฌด์กฐ๊ฑด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ ์ด๋ธ๊ณผ ๊ด๊ณ์์ง๋ ์์
๐ก ์๋น์ค์ ๋๋ฉ์ธ์ ์ญํ ๋ถ๋ด์ ์ด๋ป๊ฒ ํ๋๊ฐ?
์๋น์ค ํด๋์ค ๋ด๋ถ์์ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๊ฒ๋ณด๋ค๋
๋๋ฉ์ธ ๋ชจ๋ธ ๋ด์์ ๋ก์ง์ ์ฒ๋ฆฌํ๊ณ ์๋น์ค ๋ฉ์๋๋ ํธ๋์ญ์ ๊ณผ ๋๋ฉ์ธ ๊ฐ์ ์์๋ง ๋ณด์ฅํ๋๋ก ๊ตฌ์ฑ
- ์๋น์ค ๋ฉ์๋ ์์
@Transactional
public Order cancelOrder(int orderId){
Orders order = ordersRepository.findById(orderId);
Billing billing = billingRepository.findById(orderId);
Delivery delivery = deliveryRepository.findById(orderId);
delivery.cancel();
order.cancel();
billing.cancel();
return order;
}
๊ฐ ๋๋ฉ์ธ ๋ชจ๋ธ์ด ์ทจ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์๋น์ค ๋ฉ์๋๋ ํธ๋์ญ์ ๊ณผ ๋๋ฉ์ธ ๊ฐ์ ์์๋ง ๋ณด์ฅ
Bean์ ์ฃผ์ ๋ฐ๋ ๋ฐฉ์
1. ์์ฑ์
- @Autowired์ ๋์ผํ ํจ๊ณผ๋ฅผ ๊ฐ์ง
- @RequiredArgsConstructor์์ final์ด ์ ์ธ๋ ๋ชจ๋ ํ๋๋ฅผ ์ธ์๊ฐ์ผ๋ก ํ๋ ์์ฑ์๋ฅผ ์์ฑํด์ฃผ๋ฏ๋ก, ํด๋์ค์ ์์กด์ฑ ๊ด๊ณ๊ฐ ๋ณ๊ฒฝ๋์ด๋ ์ง์ ์ฝ๋๋ฅผ ์์ ํ์ง ์์๋ ๋จ
- ๊ฐ์ฅ ๊ถ์ฅํ๋ ๋ฐฉ์์
2. setter
3. @Autowired
ํด๋์ค ์์ฑํด์ CRUD ๊ตฌํํ๊ธฐ
Controller ํด๋์ค ๊ตฌํ
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto){
return postsService.save(requestDto);
}
@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);
}
@DeleteMapping("/api/v1/posts/{id}")
public Long delete(@PathVariable Long id){
postsService.delete(id);
return id;
}
}
Service ํด๋์ค ๊ตฌํ
- update ์๋น์ค ๋ฉ์๋
- id์ ์ผ์นํ๋ Posts ๊ฐ์ฒด๊ฐ ์๋์ง ํ์ธ ํ ๋ถ๋ฌ์ด
- PostsUpdateRequestDto์ title๊ณผ content๋ฅผ ๋ด์์จ ํ ๋๋ฉ์ธ ๊ฐ์ฒด(Posts)์ update ๋ฉ์๋์ PostsUpdateRequestDto ์ ๋ฌ
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto){
return postsRepository.save(requestDto.toEntity()).getId();
}
@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);
}
@Transactional(readOnly = true)
public List<PostsListResponseDto> findAllDesc(){
return postsRepository.findAllDesc().stream()
.map(PostsListResponseDto::new)
.collect(Collectors.toList());
}
@Transactional
public void delete(Long id){
Posts posts=postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("ํด๋น ๊ฒ์๊ธ์ด ์์ต๋๋ค. id= "+id));
postsRepository.delete(posts);
}
}
์์์ฑ ์ปจํ ์คํธ
- ์ํฐํฐ๋ฅผ ์๊ตฌ ์ ์ฅํ๋ ํ๊ฒฝ
- Spring Data JPA๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, ํธ๋์ญ์ ์์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉด ํด๋น ๋ฐ์ดํฐ๋ ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ ์ง๋ ์ํ์
- ๋ฐ์ดํฐ ๊ฐ์ ๋ณ๊ฒฝ ์ ํธ๋์ญ์ ์ด ๋๋๋ ์์ ์ ํด๋น ํ ์ด๋ธ์ ๋ณ๊ฒฝ๋ถ์ ๋ฐ์ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ํ์๊ฐ ์์(Dirty Checking)
DTO ํด๋์ค ๊ตฌํ
DTO ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ์ด์
Entity ํด๋์ค๋ฅผ ๊ธฐ์ค์ผ๋ก ํ ์ด๋ธ ์์ฑ, ์คํค๋ง ๋ณ๊ฒฝ์ด ์ํ๋จ
ํ์ง๋ง Request/Response์ฉ DTO๋ View๋ฅผ ์ํ ํด๋์ค์ด๋ฏ๋ก ์์ฃผ ๋ณ๊ฒฝ๋์ด์ผ ํจ
๋ฐ๋ผ์ Entity ํด๋์ค๋ฅผ ์ ๋ Request/Response ํด๋์ค๋ก ์ฌ์ฉํ๋ฉด ์๋จ
์ค์ ๋ก Controller์์ ๊ฒฐ๊ด๊ฐ์ผ๋ก ์ฌ๋ฌ ํ ์ด๋ธ์ ์กฐ์ธํด ๋ฐํํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์
View Layer์ DTOํด๋์ค์ DB Layer์ Entity ํด๋์ค๋ก ๋๋์ด ์ฒ ์ ํ ์ญํ ๋ถ๋ฆฌ ํ์
- PostsSaveRequestDto
title, content, author๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ ฅ ๋ฐ์ ์ํฐํฐ๋ก ๋ง๋ฆ
@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();
}
}
- PostsResponseDto
๋ชจ๋ ํ๋๋ฅผ ๊ฐ์ง ์์ฑ์๊ฐ ํ์ํ์ง ์์ผ๋ฏ๋ก ์์ฑ์๋ก Entity๋ฅผ ๋ฐ์ ํ๋์ ๊ฐ ์ ๋ ฅ
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
private String author;
public PostsResponseDto(Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.author = entity.getAuthor();
}
}
ํ ์คํธ ๋ฉ์๋ ๊ตฌํ
@Test
public void edit_Posts() throws Exception{
//given
Posts savedPosts=postsRepository.save(Posts.builder()
.title("title")
.content("content")
.author("me")
.build());
Long updateId=savedPosts.getId();
String expectedTitle="title2";
String expectedContent="content2";
PostsUpdateRequestDto requestDto=PostsUpdateRequestDto.builder()
.title(expectedTitle)
.content(expectedContent)
.build();
String url="http://localhost:"+port+"api/v1/posts/"+updateId;
HttpEntity<PostsUpdateRequestDto> requestEntity=new HttpEntity<>(requestDto);
//when
ResponseEntity<Long> responseEntity=restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class);
//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
}
JPA Auditing์ผ๋ก ์์ฑ์๊ฐ/์์ ์๊ฐ ์๋ํ
๋ฐ์ดํฐ์ ์์ฑ ์๊ฐ๊ณผ ์์ ์๊ฐ์ ๋ชจ๋ ํ ์ด๋ธ๊ณผ ์๋น์ค ๋ฉ์๋์ ํฌํจํ ์ ์๋๋ก ํ๋ ๋ฐฉ๋ฒ
BaseTimeEntity ํด๋์ค ๊ตฌํ
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
๋ชจ๋ Entity์ ์์ ํด๋์ค๊ฐ ๋์ด Entity๋ค์ createdDate, modifiedDate๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌ
@MappedSuperclass: JPA Entity ํด๋์ค๋ค์ด ํด๋น ํด๋์ค๋ฅผ ์์ํ ๊ฒฝ์ฐ BaseTimeEntity์ ํ๋๋ค๋ ์นผ๋ผ์ผ๋ก ์ธ์ํ๋๋ก ํจ
Entity ํด๋์ค์ ์์ ํ์
public class Posts extends BaseTimeEntity {
...
}
Application ํด๋์ค์ ํ์ฑํ ์ด๋ ธํ ์ด์ ์ถ๊ฐ
@EnableJpaAuditing
@SpringBootApplication
public class Application {
...
}
'Spring Boot > ์คํ๋ง ๋ถํธ์ AWS๋ก ํผ์ ๊ตฌํํ๋ ์น ์๋น์ค' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
6์ฅ: AWS ์๋ฒ ํ๊ฒฝ ๋ง๋ค๊ธฐ (0) | 2022.06.27 |
---|---|
3์ฅ: JPA๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ค๋ฃจ๊ธฐ (1) (0) | 2022.06.15 |
2์ฅ: ํ ์คํธ ์ฝ๋ ์์ฑํ๊ธฐ (0) | 2022.06.13 |
1์ฅ: ์ธํ ๋ฆฌ์ ์ด ์ฌ์ฉํ๊ธฐ (์์ ์ค) (0) | 2022.06.13 |
๋๊ธ