독학을 하다 처음으로 진행한 프로젝트가 드디어 마무리가 되었다. 한 2개월 정도 걸렸던 것 같다.
공부한 것을 처음으로 만들어보니, 로직 구현에만 집중을 해도 버거운 시간들이었던 것 같다.
그래서 전 프로젝트에서 아쉬웠던 점들이 있었는데
- 테스트 코드를 작성하며 유닛 테스트를 해보지 못한점
- RESTful한 API를 제대로 설계하지 못한 점
- Entity 클래스에 @Setter, @Data등을 사용하여 리팩토링에 어려움이 있었던 점
그래서 위의 아쉬운 점들을 연습하고 보완하기 위해 아주 간단하게 CRUD의 기능만 있는 게시판을 연습해 보았다.
웹 서비스의 가장 기초가 되는 것이 CRUD 이니 더욱 집중해 보고자 하였다.
프로젝트 및 언어 버전은 Java 11 / Spring Boot 2.7.9 를 사용하였다.
Dependency는 간단하게
- Lombok
- H2 DataBase
- JPA
- Web
정도만 적용하였다.
우선 패키지 구조는 계층형 구조를 선택하였다.
보통 패키지는 크게 도메인형 구조, 계층형 구조로 나뉘는데
도메인형 구조는 복잡한 구조에서 제공하는 기능을 명확한 기준으로 분리할 수 있지만, 나는 Post 하나뿐이다.
그래서 계층형 구조가 가지는
- 복잡도가 낮고 기능이 적어 하나의 계층에 속하는 클래스 수가 적고,
- 추후에 모듈별로 분리하게 될 일이 없을 것 같은 작은 규모의 프로젝트
이러한 특징들 때문에 패키지는 계층형 구조로 가져갔다.
이제 가장 기본인 엔티티와, Respotitory, DTO부터 살펴보자
Post
package hongxeob.restapi_crud;
import hongxeob.restapi_crud.request.PostConvert;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
@Builder
public Post(Long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
public PostConvert toConvert() {
return PostConvert.builder()
.title(title)
.content(content)
.build();
}
public void update(PostConvert postConvert) {
this.title= postConvert.getTitle();
this.content = postConvert.getContent();
}
}
- PK는 id로 잡았고 제목을 뜻하는 title, 내용의 content를 부여하였다.
- Entity 클래스에서는 @Setter를 사용하지 않고, 생성자를 따로 사용했다.
- 빌더 패턴을 사용했으며, 사용한 이유는 많은 이유가 있지만
- 가독성 증가, 필요한 파라미터만 생성 가능의 이유가 크다!
- toConvert() 메소드는 Service에서 update 메소드를 생성할 때 사용한다.
- update() 메소드 또한 Service에서 update 메소드를 생성할 때 사용한다.
PostRepository
package hongxeob.restapi_crud.repository;
import hongxeob.restapi_crud.Post;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Post, Long> {
}
- 스프링 데이터 JPA를 사용했다.
- 스프링 데이터 JPA는 기본적인 CRUD 기능을 제공해준다.
Service 클래스를 만들기 전에 먼저 등록,수정,응답에 필요한 클래스(DTO)를 만들어준다.
먼저 등록(Create),수정(Update) 그리고 형변환에 관련된 Convert를 생성한다.
CreatePostDto
package hongxeob.restapi_crud.request;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class CreatePostDto {
private String title;
private String content;
@Builder
public CreatePostDto(String title, String content) {
this.title = title;
this.content = content;
}
}
- Post 생성시에 사용할 DTO
UpdatePostDto
package hongxeob.restapi_crud.request;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class UpdatePostDto {
private String title;
private String content;
@Builder
public UpdatePostDto(String title, String content) {
this.title = title;
this.content = content;
}
}
- update시에는 제목과 내용을 수정할 수 있다.
- 위 CreatePostDto와 같은 형태이지만, 나눈 이유는 나중에 서비스에서 기능이 추가 되거나 조건이 변경될 수 있기 때문이다.
- ex) 하나의 DTO로 사용했는데, 나중에 그DTO에 address라는 필드가 추가되고, 이것은 수정이 불가능 하다면? -> 다시 리팩토링 해야한다.
PostConvert
package hongxeob.restapi_crud.request;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class PostConvert {
private String title;
private String content;
@Builder
public PostConvert(String title, String content) {
this.title = title;
this.content = content;
}
}
Update시 Service단에서 사용된다. 사용 용도는
- 수정할 게시물을 찾는다 : 형태는 Post
- Post에는 setter가 없으므로 값 변경이 불가능하다.
- Post를 PostConvert 형태로 변경한다.
- 이후 set메소드를 사용해 UpdatePostDto 형태로 날라온 파라미터에 입력된 값으로 변경한다.
- Post의 update() 메소드를 통해 수정한다.
이부분은 Service 로직에서 어떻게 흘러가는지 확인해보자.
이제 요청에 응답을 해주는 클래스를 만들자.
PostResponse
package hongxeob.restapi_crud.response;
import hongxeob.restapi_crud.Post;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class PostResponse {
private Long id;
private String title;
private String content;
@Builder
public PostResponse(Long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
public PostResponse(Post post) {
this.id = post.getId();
this.title = post.getTitle();
this.content = post.getContent();
}
}
엔티티인 Post와 다를게 없지만, 이후 서비스가 변경될 것을 대비해 Response 클래스를 따로 만든다.
PostListResponse
package hongxeob.restapi_crud.response;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter
public class PostListResponse {
List<PostResponse> postList;
@Builder
public PostListResponse(List<PostResponse> postList) {
this.postList = postList;
}
}
- 클래스 이름 그대로 모든 게시물을 Response 할 때 쓰이는 클래스이다.
- List로 받은 전체 게시물을 Object로 변환해 전달해줄 때 사용하는 클래스이다.
- 종종 repository의 findAll() 메소드 사용시, 반환타입인 List<ResponseDto>을 그대로 응답으로 넘기는 경우가 있다.
- 하지만 List 형태로 넘겨주는 것보단 Object 형태로 넘겨주는 것이 좋다.
- 프론트와 협업시 백엔드가 넘겨준 데이터를 프론트쪽이 파싱을 할 때, 어떤 것은 List형태, 어떤 것은 Object 형태이면 번거롭기 때문이다. 그래서 Object형태로 통일해 준다.
여기까지 프로젝트 기본 세팅 및 Entity,Dto,Repository 설계를 해 보았다.
다음 장에서는 Service와 Controller를 설계해보자!
'프로젝트 > RestApi_CRUD' 카테고리의 다른 글
RestApi 게시판 CRUD 연습 (5) - PostMan (0) | 2023.03.16 |
---|---|
RestApi 게시판 CRUD 연습 (4) - 유닛 테스트 : Controller 테스트 with MockMvc (0) | 2023.03.16 |
RestApi 게시판 CRUD 연습 (3) - 유닛 테스트 : Service 테스트 (0) | 2023.03.15 |
RestApi 게시판 CRUD 연습 (2) - Service / Controller (0) | 2023.03.14 |