이동욱 님의 스프링 부트와 AWS로 혼자 구현하는 웹서비스를 공부하며 기록한 것입니다.
자세한 내용은 책을 통해 공부해야 하기 때문에 간단하게 정리한 내용만 있습니다.
1. 롬복 Lombok, Gradle에 추가하기
build.gradle
의 의존성 설정에
dependencies {
compile(org.projectlombok:lombok)
}
을 추가하고, [settings]-[Annotation Processor]에서 Enable annotation processing을 체크한다.
2. Spring Data JPA 적용하기
웹 애플리케이션에서 관계형 데이터베이스(RDB)는 빠질 수 없는 요소로, 애플리케이션 코드보다 SQL로 채워졌었다.
SQL을 통해서만 데이터베이스에 저장하고 조회하다보니 SQL이 반복되고,
가장 결정적으로 객체지향 프로그래밍과 패러다임 불일치의 문제를 가져온다.
서로 지향하는 바가 다른 객체지향 프로그래밍 언어와 SQL을 중간에서 패러다임 일치를 시켜주기 위한 기술로 JPA를 사용한다.
Spring Data JPA는 JPA를 좀 더 쉽게 사용하고자 추상화시켰다.
나중에 필히 JPA는 따로 공부해야 한다. Spring Data JPA도 JPA를 더 잘 알아야 잘 활용할 수 있다.
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa') // Spring-data-jpa
compile('com.h2database:h2') // 로컬에서 사용할 database
}
build.gradle
프로젝트 의존성에 Spring-data-jpa와 database h2를 추가한다.
3. 도메인 등록하기
[domain] 패키지를 생성하고, 하위에 [posts]를 생성한다.
게시판과 관련된 도메인을 담을 Posts 클래스를 생성한다.
@Getter
@NoArgsConstructor
@Entity
public class Posts {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 500, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
@Builder
public Posts (String title, String content) {
this.title = title;
this.content = content;
}
}
@Entity
는 해당 클래스가 테이블과 링크되는 클래스임을 나타낸다.@Id
해당 테이블의 기본키 필드를 나타낸다.@GeneratedValue
기본키의 생성 규칙을 나타낸다. GenerationType.IDENTITY 옵션을 추가해야 자동으로 증가한다.@Column
테이블의 컬럼을 나타낸다. 선언하지 않아도 클래스 내의 모든 필드는 컬럼으로 인식된다.
기본 값 외에 필요한 옵션이 있으면 사용한다. (DB 저장할 Type이 TEXT거나, 사이즈를 지정하거나)@Builder
해당 클래스의 빌더 패턴 클래스를 생성한다. 생성자 상단에 선언하면 생성자에 포함된 필드만 빌더에 포함된다.
@Builder
를 사용하면, 생성자와 같이 생성 시점에 값을 채워주는 역할을 하지만, 빌더를 사용하면
어느 필드에 어떤 값을 채워야 하는지 명확하게 코드로 표현할 수 있다.
4. PostsRepository 생성하기
PostsRepository 인터페이스를 생성한다.
public interface PostsRepository extends JpaRepository<Posts, Long> {
}
인터페이스를 생성한 후 JpaRepository<Entity 클래스, PK 타입>을 상속하면 기본적인 CRUD 메소드가 자동으로 생성된다.
Entity 클래스와 Entity Repository는 함께 위치해야 한다.
5. 등록 수정 조회 API 만들기
API를 만들기 위해서는 3개의 클래스가 필요하다.
- Request 데이터를 받을 Dto
- API 요청을 받을 Controller
- 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service
[web]-[dto] 하위에 PostsSaveRequestDto 생성, [web]에 PostsApiController 생성,
[service]-[posts]에 PostsService 생성
각각 코드를 작성한다.
PostsApiController
@RequestBody
를 사용하면 HTTP 요청의 Body를 자바 객체로 사용할 수 있다.
Post 메서드로 해당 URI에 HTTP 요청이 오면 요청의 Body를 파라미터로 Service의 save 메서드를 호출한다.
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto) {
return postsService.save(requestDto)
}
}
PostsService
Service의 save 메서드는 받은 Body 객체를 Repository의 save 메서드로
HTTP 요청의 Body를 빌더 클래스로 빌드한 값을 파라미터로 보내고, 이에 대한 Id 값을 리턴한다.@Transactional
트랜잭션 처리를 지원한다. (선언적 트랜잭션) 나중에 트랜잭션 따로 정리 필요
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
}
PostsSaveRequestDto
Entity 클래스와 유사해보인다.
Entity 클래스는 Request, Response 클래스로 사용하면 안된다.
Entity 클래스가 변경 되면 DB와 관련된 정보들이 수정되기 때문이다.
@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
@Builder public PostsSaveRequestDto (String title, String content) {
this.title = title; this.content = content;
}
public Posts toEntity() {
return Posts.builder().title(title).content(content).build();
}
}
Dto는 Request와 Response를 위한, View를 위한 클래스로 자주 변경될 수 있는 클래스로 사용한다.
View Layer와 DB Layer는 철저하게 분리되는 것이 필요하다.
수정과 조회도 코드를 작성한다.
PostsApiController
수정과 조회의 요청에 따른 로직을 처리할 Controller 선언.
RESTful API에 따라 PUT 메서드는 수정하는데 사용된다.@PutMapping
으로 PUT 메서드의 경우 처리한다.
..
@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 PostsReponseDto findById (@PathVariable Long id) {
return postsService.findById(id);
}
..
PostsResponseDto
조회할 경우에 필요한 객체, View에 모델을 담아 보낼 객체를 위한 클래스
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
public PostsResponseDto (Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
}
}
PostsService
update에서는 쿼리를 날리는 부분이 없다. JPA의 영속성 컨텍스트 때문이다.
..
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {
Posts posts = postsRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("게시물 없음"));
posts.update(requestDto.getTitle(), requestDto.getContent());
return id;
}
public PostsResponseDto findById (Long id) {
Posts entity = postsRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("게시물 없음"));
return new PostsReponseDto(entity);
}
..
정리하자면, Controller는 요청과 응답을 처리한다.
DTO는 요청과 응답에 대해서 DB를 활용하는데 사용한다.
Service는 비지니스 로직의 순서를 정의한다.
작성된 코드에서 Repository에서 사용되는 메서드는 Spring Data JPA를 통해 이미 정의된 메서드로
이를 더 깊이 알고 다루기 위해서는 JPA를 공부해야 한다.
6. JPA Auditing으로 생성시간/수정시간 자동화 하기
'Java > Spring' 카테고리의 다른 글
[Java Spring] HTTP PUT, DELETE 사용 (0) | 2021.06.25 |
---|---|
[Java Spring] Controller 구현 후 404 Error (0) | 2021.06.21 |
[Spring-boot] H2 데이터베이스 연결하기 (0) | 2021.06.01 |
[Java Spring] 프로젝트 생성 후 테스트 코드 (0) | 2021.05.30 |
[Spring boot로 게시판 만들기] 0. 프로젝트 생성부터 index.html (1) | 2021.04.08 |