Spring Boot TodoList(Api Server) 만들기

min seung moon·2021년 6월 22일
4

IntelliJ

목록 보기
4/4

1. 요구 사항 정리


HTTP 상태 코드 : https://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C

2. 환경설정 및 프로젝트 세팅

01. 프로젝트 생성



  • src : 실질적인 코드 작성 및 TEST
  • build.gradle : build에 필요한 옵션 정리
  • gradle은 build.gradle에 작성된 build 구성을 기반으로 build 기능을 수행
  • view => wubdiw + gradle로 선택할 수도 있다

02. 환경 설정

-1. 기존 dependencies를 삭제 후 gradle reload



-2. 의존성(플러그인) 추가

플러그인 모음 사이트 : maven spring boot

  • spring boot 플러그인 작성
plugins {
    id 'org.springframework.boot' version '2.4.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

  • 디펜던시 작성(rest api, jpa, h2 DB)
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    runtimeOnly 'com.h2database:h2'
}

  • Reload

  • lombok 라이브러리 추가 후 리로드

    • 의존성을 추가하고 바로 사용할 수 없고 설정이 필요
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    runtimeOnly 'com.h2database:h2'

    annotationProcessor("org.projectlombok:lombok")
    compileOnly("org.projectlombok:lombok")
}


  • lombok 플러그인 설치
    • window
      • file - setting - plugins
    • OS
      • inteliJ IDEA - preferences - plugins
    • 단축키
      • shift 2번 - plugins 검색
    • install 후 재실행
    • 다시 setting(preferences) - build, execution, deployment - compiler - annotation processors
      • Enable annotation processing 체크 - apply - ok

-3. example 패키지 및 클래스 생성

  • example Package 생성
    • src - main - java - new - package 생성
    • src - main - java
      • window : alt + ins
      • os : command + n
  • TodoServerApplication Class 생성
  • src - main - java - new - class 생성
    • src - main - java
      - window : alt + ins
      - os : command + n

3. 모델 구현

  • 데이터베이스와 데이터를 주고 받는 기능 구현
  • TodoEntity, TodoRequest, TodoResponse

01. 모댈 패키지 및 파일 생성

02. TodoEntity.class

  • 실제 데이터베이스와 연관된 클래스
package org.example.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class TodoEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

//    order 키워드는 H2 데이터베이스에서 이미 예약어로 사용되기에 name을 새로 지정
    @Column(name = "todoOrder", nullable = false)
    private Long order;

    @Column(nullable = false)
    private Boolean completed;
}

03. TodoRequest.class

  • 데이터베이스로부터 요청과 관련된 클래스
package org.example.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TodoRequest {

    private String title;
    private Long order;
    private Boolean completed;
}

04. TodoResponse.class

  • 데이터베이스로부터 응답과 관련된 클래스
package org.example.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TodoResponse {
    private Long id;
    private String title;
    private Long order;
    private Boolean completed;
    private String url;

    public TodoResponse(TodoEntity todoEntity) {
        this.id = todoEntity.getId();
        this.title = todoEntity.getTitle();
        this.order = todoEntity.getOrder();
        this.completed = todoEntity.getCompleted();

        this.url = "http://localhost:8080/" + this.id;
    }
}

4. Repository 구현

  • 데이터베이스와 데이터를 주고받기 위한 인터페이스를 정의한 부분
  • 실질적인 데이터 저장소가 아닌 데이터를 주고받기 위한 인터페이스
  • TodoRepository

01. 패키지 및 클래스 생성

02. TodoRepository.class

  • 데이터 저장과 관련된 객체는 Entity이며 Reuquest와 Response는 데이터 저장과 관련이 없기에 Entity Repository만 만들면 된다
  • JpaRepository<데이터베이스와 연결될 객체, 해당 객체의 id의 필드 타입>
package org.example.repository;

import org.example.model.TodoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

// JpaRepository<데이터베이스와 연결될 객체, 해당 객체의 id의 필드 타입>
@Repository
public interface TodoRepository extends JpaRepository<TodoEntity, Long> {
}

5. Service 구현

  • 작성한 Reposity가 동작하는 코드를 작성
  • TodoService

01. 패키지 및 클래스 생성

02. TodoService.class

  • 메소드 시그니쳐 작성
    • 구현할 메소드 정리
package org.example.service;

import lombok.AllArgsConstructor;
import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@AllArgsConstructor
public class TodoService {

    private final TodoRepository todoRepository;

//    1	todo 리스트 목록에 아이템을 추가
    public TodoEntity add(TodoRequest request) {
        return null;
    }
//    2	todo  리스트 목록 중 특정 아이템을 조회
    public TodoEntity searchById(Long id) {
        return null;
    }
//    3	todo 리스트 전체 목록을 조회
    public List<TodoEntity> searchAll() {
        return null;
    }
//    4	todo 리스트 목록 중 특정 아이템을 수정
    public TodoEntity updateById(Long id) {
        return null;
    }
//    5	todo 리스트 목록 중 특정 아이템을 삭제
    public void deleteById(Long id) {

    }
//    6	todo 리스트 전체 목록을 삭제
    public void deleteAll() {
        
    }
    
}

  • 메소드 구현
    • save는 제네릭으로 받은 타입(T)으로 값을 반환
package org.example.service;

import lombok.AllArgsConstructor;
import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;

@Service
@AllArgsConstructor
public class TodoService {

    private final TodoRepository todoRepository;

//    1	todo 리스트 목록에 아이템을 추가
    public TodoEntity add(TodoRequest request) {
        TodoEntity todoEntity = new TodoEntity();
        todoEntity.setTitle(request.getTitle());
        todoEntity.setOrder(request.getOrder());
        todoEntity.setCompleted(request.getCompleted());
        // <S extends T> S save(S entity);
        // save는 제네릭으로 받은 타입(T)으로 값을 반환
        return this.todoRepository.save(todoEntity);
    }
//    2	todo  리스트 목록 중 특정 아이템을 조회
    public TodoEntity searchById(Long id) {
        return  this.todoRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));

    }
//    3	todo 리스트 전체 목록을 조회
    public List<TodoEntity> searchAll() {
        return this.todoRepository.findAll();
    }
//    4	todo 리스트 목록 중 특정 아이템을 수정
    public TodoEntity updateById(Long id, TodoRequest request) {
        TodoEntity todoEntity = this.searchById(id);
        if(request.getTitle() != null) {
            todoEntity.setTitle(request.getTitle());
        }
        if(request.getOrder() != null) {
            todoEntity.setOrder(request.getOrder());
        }
        if(request.getCompleted() != null) {
            todoEntity.setCompleted(request.getCompleted());
        }
        return this.todoRepository.save(todoEntity);
    }
//    5	todo 리스트 목록 중 특정 아이템을 삭제
    public void deleteById(Long id) {
        this.todoRepository.deleteById(id);
    }
//    6	todo 리스트 전체 목록을 삭제
    public void deleteAll() {
        this.todoRepository.deleteAll();
    }

}


6. 컨트롤러 구현

  • 서버에 들어온 요청에 따른 작업과 결과를 반환
  • TodoController

01. 패키지 및 클래스 생성

02. TodoController.class

  • 시그니쳐 메소드 작성
package org.example.controller;

import lombok.AllArgsConstructor;
import org.example.model.TodoEntity;
import org.example.model.TodoResponse;
import org.example.service.TodoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@CrossOrigin
@AllArgsConstructor
@RestController
@RequestMapping("/")
public class TodoController {

    private final TodoService service;

    @PostMapping
    public ResponseEntity<TodoResponse> create() {
        System.out.println("CREATE");
        return null;
    }

    @GetMapping("{id}")
    public ResponseEntity<TodoResponse> readOne() {
        System.out.println("READ ONE");
        return null;
    }

    @GetMapping
    public ResponseEntity<List<TodoEntity>> readAll() {
        System.out.println("READ ALL");
        return null;
    }

    @PatchMapping("{id}")
    public ResponseEntity<TodoEntity> update() {
        System.out.println("UPDATE");
        return null;
    }

    @DeleteMapping("{id}")
    public ResponseEntity<?> deleteOne() {
        System.out.println("DELETE ONE");
        return  null;
    }

    @DeleteMapping
    public ResponseEntity<?> deleteAll() {
        System.out.println("DELETE ALL");
        return null;
    }
}


03. PostMan Download





04. main class 수정

  • Service 실행을 위해서 main class(TodoServiceApplication.class) 수정
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TodoServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(TodoServerApplication.class, args);
    }
}

05. 실행

06. postman으로 테스트하기

  • return null이기에 아무것도 나타나지 않지만 console 찍힘

07. controller 메소드 구현

package org.example.controller;

import lombok.AllArgsConstructor;
import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.model.TodoResponse;
import org.example.service.TodoService;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@CrossOrigin
@AllArgsConstructor
@RestController
@RequestMapping("/")
public class TodoController {

    private final TodoService service;

    @PostMapping
    public ResponseEntity<TodoResponse> create(@RequestBody TodoRequest request) {
        System.out.println("CREATE");

        if (ObjectUtils.isEmpty(request.getTitle()))
            return ResponseEntity.badRequest().build();

        if (ObjectUtils.isEmpty(request.getOrder()))
            request.setOrder(0L);

        if (ObjectUtils.isEmpty(request.getCompleted()))
            request.setCompleted(false);

        TodoEntity result = this.service.add(request);
        return ResponseEntity.ok(new TodoResponse(result));
    }

    @GetMapping("{id}")
    public ResponseEntity<TodoResponse> readOne(@PathVariable Long id) {
        System.out.println("READ ONE");
        TodoEntity result = this.service.searchById(id);
        return ResponseEntity.ok(new TodoResponse(result));
    }

    @GetMapping
    public ResponseEntity<List<TodoResponse>> readAll() {
        System.out.println("READ ALL");
        List<TodoEntity> list = this.service.searchAll();
        List<TodoResponse> response = list.stream().map(TodoResponse::new)
                .collect(Collectors.toList());
        return ResponseEntity.ok(response);
    }

    @PatchMapping("{id}")
    public ResponseEntity<TodoEntity> update(@PathVariable Long id, @RequestBody TodoRequest request) {
        System.out.println("UPDATE");
        TodoEntity result = this.service.updateById(id, request);
        return ResponseEntity.ok(result);
    }

    @DeleteMapping("{id}")
    public ResponseEntity<?> deleteOne(@PathVariable Long id) {
        System.out.println("DELETE ONE");
        this.service.deleteById(id);
        return  ResponseEntity.ok().build();
    }

    @DeleteMapping
    public ResponseEntity<?> deleteAll() {
        System.out.println("DELETE ALL");
        this.service.deleteAll();
        return ResponseEntity.ok().build();
    }
}



08. 테스트

  • 서버 재실행

09. 프론트엔드에 붙여서 테스트

7. 테스트 코드 작성

  • 유닛 테스트

01. 테스트에 필요한 라이브러리 추가

  • burild.gradle에 dependencies 추가
    • testImplementation('org.springframework.boot:spring-boot-starter-test')
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    runtimeOnly 'com.h2database:h2'

    annotationProcessor("org.projectlombok:lombok")
    compileOnly("org.projectlombok:lombok")

    testImplementation('org.springframework.boot:spring-boot-starter-test')
}
  • reload

02. TodoService에서 test 생성

  • test를 생성할 클래스에 와서 alt + enter로 create Test 생성
  • test할 메소드 선택 후 생성

03. TodoServiceTest.class 작성

  • Test로 Mock객체를 사용
    • 외부 시스템에 의존하지 않고 자체 테스트를 실행할 수 있기에 사용
      • 유닛 테스트는 네트워크나 데이터베이스가 연결이 안된다 하더라도 유닛 테스트는 가능해야 되기에 사용
    • 실제 데이터베이스를 사용하게 되면 테스트를 할 때 마다 db에 값이 추가되고 수정하거나 삭제되는 일이 이루어지기 때문에 DB에는 민간함 정보가 포함되어 잇는 경우도 많고 서비스에 사용되는 데이터가 함부로 변경되면 큰일이 나기에 테스트를 할 때도 실제 DB에 연결하지 않는다
  • 시그니처 메소드
package org.example.service;

import org.example.repository.TodoRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.*;

// Test로 Mock객체를 사용
@ExtendWith(MockitoExtension.class)
class TodoServiceTest {

    @Mock
    private TodoRepository todoRepository;

    @InjectMocks
    private TodoService todoService;

    @Test
    void add() {
    }

    @Test
    void searchById() {
    }
}

  • add 테스트 코드 작성 및 실행
package org.example.service;

import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalAnswers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;


// Test로 Mock객체를 사용
@ExtendWith(MockitoExtension.class)
class TodoServiceTest {

    @Mock
    private TodoRepository todoRepository;

    @InjectMocks
    private TodoService todoService;

    @Test
    void add() {
        // TodoRepository가 save 메소드를 호출해서 TodoEntity 값을 받으면
        // 받은 Entity 값을 반환
        when(this.todoRepository.save(any(TodoEntity.class)))
                .then(AdditionalAnswers.returnsFirstArg());

        TodoRequest expected = new TodoRequest();
        expected.setTitle("Test Title");

        TodoEntity actual = this.todoService.add(expected);

        assertEquals(expected.getTitle(), actual.getTitle());
    }

    @Test
    void searchById() {
        
    }
}


  • searchById 테스트 코드 작성 및 테스트
package org.example.service;

import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalAnswers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Optional;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;


// Test로 Mock객체를 사용
@ExtendWith(MockitoExtension.class)
class TodoServiceTest {

    @Mock
    private TodoRepository todoRepository;

    @InjectMocks
    private TodoService todoService;

    @Test
    void add() {
        // TodoRepository가 save 메소드를 호출해서 TodoEntity 값을 받으면
        // 받은 Entity 값을 반환
        when(this.todoRepository.save(any(TodoEntity.class)))
                .then(AdditionalAnswers.returnsFirstArg());

        TodoRequest expected = new TodoRequest();
        expected.setTitle("Test Title");

        TodoEntity actual = this.todoService.add(expected);

        assertEquals(expected.getTitle(), actual.getTitle());
    }

    @Test
    void searchById() {
        // findById는 옵션널을 반환하기에 옵션널로 리턴값을 넣어준다
        TodoEntity entity = new TodoEntity();
        entity.setId(123L);
        entity.setTitle("TITLE");
        entity.setOrder(0L);
        entity.setCompleted(false);
        Optional<TodoEntity> optional = Optional.of(entity);

        // 어떠한 id값이 주어졌을 때 optional 값을 리턴
        given(this.todoRepository.findById(anyLong()))
            .willReturn(optional);

        // service에서 searchById를 했을 때 값을 받은 값과 optional 값을 비교
        TodoEntity actual = this.todoService.searchById(123L);
        TodoEntity expected =optional.get();
        assertEquals(expected.getId(), actual.getId());
        assertEquals(expected.getTitle(), actual.getTitle());
        assertEquals(expected.getOrder(), actual.getOrder());
        assertEquals(expected.getCompleted(), actual.getCompleted());
    }
}


  • searchByIdFailed 실패 테스트 코드 추가 작성 및 실행
package org.example.service;

import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalAnswers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.server.ResponseStatusException;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;


// Test로 Mock객체를 사용
@ExtendWith(MockitoExtension.class)
class TodoServiceTest {

    @Mock
    private TodoRepository todoRepository;

    @InjectMocks
    private TodoService todoService;

    @Test
    void add() {
        // TodoRepository가 save 메소드를 호출해서 TodoEntity 값을 받으면
        // 받은 Entity 값을 반환
        when(this.todoRepository.save(any(TodoEntity.class)))
                .then(AdditionalAnswers.returnsFirstArg());

        TodoRequest expected = new TodoRequest();
        expected.setTitle("Test Title");

        TodoEntity actual = this.todoService.add(expected);

        assertEquals(expected.getTitle(), actual.getTitle());
    }

    @Test
    void searchById() {
        // findById는 옵션널을 반환하기에 옵션널로 리턴값을 넣어준다
        TodoEntity entity = new TodoEntity();
        entity.setId(123L);
        entity.setTitle("TITLE");
        entity.setOrder(0L);
        entity.setCompleted(false);
        Optional<TodoEntity> optional = Optional.of(entity);

        // 어떠한 id값이 주어졌을 때 optional 값을 리턴
        given(this.todoRepository.findById(anyLong()))
            .willReturn(optional);

        // service에서 searchById를 했을 때 값을 받은 값과 optional 값을 비교
        TodoEntity actual = this.todoService.searchById(123L);
        TodoEntity expected =optional.get();
        assertEquals(expected.getId(), actual.getId());
        assertEquals(expected.getTitle(), actual.getTitle());
        assertEquals(expected.getOrder(), actual.getOrder());
        assertEquals(expected.getCompleted(), actual.getCompleted());
    }

    @Test
    public void searchByIdFailed() {
        given(this.todoRepository.findById(anyLong()))
                .willReturn(Optional.empty());
        assertThrows(ResponseStatusException.class, () -> {
            this.todoService.searchById(123L);
        });
    }
}

04. TodoController에서 test 생성

05. TodoControllerTest.class 작성

  • 시그니처 메소드 및 셋팅
package org.example.controller;

import org.example.model.TodoEntity;
import org.example.service.TodoService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;


@WebMvcTest(TodoController.class)
class TodoControllerTest {

    @Autowired
    MockMvc mvc;

    @MockBean
    TodoService todoService;

    private TodoEntity expected;

    // 테스트 진행 전 expected의 값 초기화
    @BeforeEach
    void setup() {
        this.expected = new TodoEntity();
        this.expected.setId(123L);
        this.expected.setTitle("TEST TITLE");
        this.expected.setOrder(0L);
        this.expected.setCompleted(false);
    }

    @Test
    void create() {
    }

    @Test
    void readOne() {
    }
}

  • create test 코드 작성
package org.example.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.service.TodoService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


@WebMvcTest(TodoController.class)
class TodoControllerTest {

    @Autowired
    MockMvc mvc;

    @MockBean
    TodoService todoService;

    private TodoEntity expected;

    // 테스트 진행 전 expected의 값 초기화
    @BeforeEach
    void setup() {
        this.expected = new TodoEntity();
        this.expected.setId(123L);
        this.expected.setTitle("TEST TITLE");
        this.expected.setOrder(0L);
        this.expected.setCompleted(false);
    }

    @Test
    void create() throws Exception{
        // title만 request로 받아오고 나머지는 expected에 설정한 값 사용
        when(this.todoService.add(any(TodoRequest.class)))
                .then((i) -> {
                    TodoRequest request = i.getArgument(0, TodoRequest.class);
                    return new TodoEntity(this.expected.getId(),
                                        request.getTitle(),
                                        this.expected.getOrder(),
                                        this.expected.getCompleted()
                                        );
                });

        TodoRequest request = new TodoRequest();
        request.setTitle("ANY TITLE");

        // 작성한 request를 body에 넣어줘야 하는데 object type 자체만으로는 넣어줄 수 없기에
        // objectmapper을 이용해 body에 넣어준다
        ObjectMapper mapper = new ObjectMapper();
        // request가 string 형태로 바뀜
        String content = mapper.writeValueAsString(request);

        this.mvc.perform(post("/")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(content))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.title").value("ANY TITLE"));
    }

    @Test
    void readOne() {
    }
}


8. 리펙토링

  • controller 패키지 명을 web으로 변경
    • 그러면 test 디렉토리의 패키지명도 자동으로 바뀐다
  • repository 패키지를 service 패키지 하위로 이동
  • System.out.println()을 log로 바꿔준다
    • 서비스 제품의 경우 console이 아닌 log를 찍어주는게 낫다
    • @Slf4j 어노테이션을 추가해준다
    • replace로 System.out.println을 log.info로 변경
    • TodoController.java
  • TodoEntity를 TodoModel로 변경

  • 리펙토링 후 테스트 코드 실행하여 정상작동하는지 확인

9. 디버깅

01. 이해

  • 컴퓨터 프로그래밍 개바 단계 중에 발생하는 시스템의 논리적인 오류나 비정상적인 버그를 찾아내 그 원인을 수정하는 작업

-1. Stem Into

  • 메소드 안으로 들어가서 메소드를 한 줄씩 진행시키는 방법

-2. Step Over

  • 함수 내부까지 들어가지 않고 바로 실행하고 다음라인으로 이동

-3. Force Stem Into

  • 내가 작성한 부분까지는 보통 Step Into가 가능
  • 하지만 subpart library는 Step Into를 해도 메소드 내부에 들어가지 않기에 Force Stem Into를 이용해 강제로 메소드 내부에 들어간다

-4. Step Out

  • 현재 함수의 나머지 부분을 실행시키고 함수를 호출 시키는 것 까지 return할 때 사용하기 좋음

-5. Drop Frame

  • Stem Into로 들어오기 전으로 이동

02. TodoServer 디버깅

  • TodoController.java의 create와 update에 브레이킹 포인트 주기

  • TodoServiceApplication 디버깅진행
  • post man 서버에 데이터 전송

profile
아직까지는 코린이!

0개의 댓글