완벽한 장면 2023. 10. 12. 20:41

1. 프로젝트 세팅

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.0.2'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    runtimeOnly 'com.h2database:h2'

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

 

application.yml

spring:
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
    defer-datasource-initialization: true
  datasource:
    url: jdbc:h2:mem:testdb
  h2:
    console:
      enabled: true

 

data.sql

INSERT INTO article (title, content) VALUES ('제목1', '내용1')
INSERT INTO article (title, content) VALUES ('제목2', '내용2')
INSERT INTO article (title, content) VALUES ('제목3', '내용3')

엔티티 구성

domain.Article

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본 생성자 protected로 만들기
public class Article {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false)
    private Long id;

    @Column(name = "title", nullable = false)
    private String title;

    @Column(name = "content", nullable = false)
    private String content;

    @Builder // 빌더 패턴 객체 생성
    public Article(String title, String content) {
        this.title = title;
        this.content = content;
    }

}

 

# 빌더 패턴의 장점

- 어느 필드에 어느 값이 매칭되는지 바로 보인다 => 코드의 가독성 증가.

@Builder 에노테이션을 사용하면 빌더 패턴을 사용하기 위한 코드를 자동으로 생성하므로 간편하게 빌더 패턴을 사용하여 객체를 만들 수 있다.

 


리포지토리 만들기

repository.BlogRepository

public interface BlogRepository extends JpaRepository<Article, Long> {
}

 

 

 


2. 블로그 글 생성 API 만들기

흐름

 

서비스, DTO 코드 작성

- DTO(Data Transfer Object)

dto.AddArticleRequest

- toEntity()는 빌더 패턴을 사용하여 DTO를 엔티티로 만들어주는 메서드

- 이것은 추후에 블로그에 글 추가할 때 저장할 엔티티로 변환하는 용도로 사용한다.

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class AddArticleRequest {

    private String title;
    private String content;

    public Article toEntity() {
        return Article.builder()
                .title(title)
                .content(content)
                .build();
    }
    /*
    이 메서드는 주로 DTO(Data Transfer Object)를
    엔티티로 변환하기 위한 유틸리티 메서드로 사용됩니다.
    주로 데이터를 전송하거나 저장하기 위한 데이터 객체와
    데이터베이스나 비즈니스 로직에서 사용되는 엔티티 객체 간의 변환을 단순화하는 데 사용됩니다.
    여기서 toEntity() 메서드는 아마도 어떤 데이터 전송 객체(DTO)의 메서드
     */
}

 

 

service.BlogService

@RequiredArgsConstructor // final이 붙거나 @NotNull이 붙은 필드의 생성자 추가
@Service
public class BlogService {

    private final BlogRepository blogRepository;

    // AddArticleRequest를 사용하여 새 게시물을 생성하고,
    // 지정된 사용자 이름으로 게시물을 저장
    public Article save(AddArticleRequest request, String username) {
        return blogRepository.save(request.toEntity(username));
    }
}

 

 

컨트롤러 코드 작성

여기에서는 /api/articles에 POST 요청이 오면 @PostMapping을 이용해 요청을 매핑한 뒤,블로그 글을 생성하는 BlogService의 save() 메서드를 호출한 뒤,생성된 블로그 글을 반환하는 작업을 할 addArticleO 메서드를 작성할 예정.

 

BlogApiController

@RequiredArgsConstructor
@RestController // HTTP Response Body에 객체 데이터를 JSON 형식으로 반환하는 컨트롤러
public class BlogApiController {

    private final BlogService blogService;


    @PostMapping("/api/articles")
    //요청 본문 값 매핑
    // HTTP POST 요청을 처리하여 블로그 글을 추가.
    public ResponseEntity<Article> addArticle(@RequestBody AddArticleRequest request, Principal principal) {
        Article savedArticle = blogService.save(request, principal.getName());
        // 요청한 자원이 성공적으로 생성되었으며 저장된 블로그 글 정보를 응답 객체에 담아 전송
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(savedArticle);
    }

  
}

 

 


Postman으로 테스트

1
2

 

3

 


테스트 코드 작성

BlogApiControllerTest.java

addArticle() 메서드 테스트 개요

@SpringBootTest
@AutoConfigureMockMvc
class BlogApiControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    BlogRepository blogRepository;

    @BeforeEach
    public void mockMvcSetUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .build();
        blogRepository.deleteAll();
    }

    @DisplayName("블로그 글 추가에 성공한다.")
    @Test
    public void addArticle() throws Exception {
        // given
        final String url = "/api/articles";
        final String title = "title";
        final String content = "content";
        final AddArticleRequest userRequest = new AddArticleRequest(title, content);

		// 객체 JSON으로 직럴화
        final String requestBody = objectMapper.writeValueAsString(userRequest);

        // when
        // 설정한 내용을 바탕으로 요청 전송
        ResultActions result = mockMvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(requestBody));

        // then
        result.andExpect(status().isCreated());

        List<Article> articles = blogRepository.findAll();

        assertThat(articles.size()).isEqualTo(1); // 크기가 1 인지 검증
        assertThat(articles.get(0).getTitle()).isEqualTo(title);
        assertThat(articles.get(0).getContent()).isEqualTo(content);
    }

}

 

 

assertThat 메서드 예시

코드 의미
assertThat(articles5ize()).isEqualTo(1); 블로그 글 크기가 1이어야 한다.
assertThat(articles.size()).isGreaterThan(2); 블로그 글 크기가 2보다 커야 한다.
assertThat(articles.size()).isLessThan(5); 블로그 글 크기가 5보다 작아야 한다.
assertThat(articles.size( )).isZero(); 블로그 글 크기가 0이어야 한다.
assertThat(article.title()).isEqualTo("제목"); 블로그 글의 title 값이 "제목"이어야 한다.
assertThat(article.title()).isNotEmpty();  블로그 글의 title값이 비어 있지 않아야 한다.
assertThat(article.title()).contains("제"); 블로그 글의 title 값이 "제"를 포함해야 한다.

 


# 직렬화와 역직렬화

728x90
반응형