4개의 레이어 필요!
각각의 레이어가 각자의 역할만 담당하게 하여 유지보수가 용이함
각각의 객체를 뜻함.
todo클래스, 요청과 응답을 받는 클래스 필요
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
어노테이션, 각각의 컬럼은
@Column(nullable = false)
붙이기. (컬럼이면서, 값이 없으면 안된다는 뜻)
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.AUTO)
private Long id;
@Column(nullable = false)
private String title;
// h2에서 order가 예약어이기 때문에 따로 이름 지정
@Column(name = "todoOrder", nullable = false)
private Long order;
@Column(nullable = false)
private Boolean completed;
}
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;
}
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;
}
}
db와 데이터를 주고받기 위한 인터페이스
package org.example.repository;
import org.example.model.TodoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TodoRepository extends JpaRepository<TodoEntity, Long> {
}
이렇게 만들면 repository.save()처럼 상속했던 JapRepository에 저장되어있는 메소드들을 사용할 수 있게됨.
TodoRepository를 private final로 멤버로 선언
annotation 붙이기
기능 별로 메소드 추가
등록 메소드
// 등록
public TodoEntity add(TodoRequest todoRequest) {
TodoEntity todoEntity = new TodoEntity();
todoEntity.setTitle(todoRequest.getTitle());
todoEntity.setOrder(todoRequest.getOrder());
todoEntity.setCompleted(todoRequest.getCompleted());
return this.todoRepository.save(todoEntity);
}
todo 조회 메소드
- todoRepository의 .findById로 id값을 찾기
- 값이 없으면 notfound exception 날리기
- 이것을 바로 return값으로 지정
// 조회
public TodoEntity searchById(Long id) {
// this.todoRepository.findById(id).orElseThrow(
// () -> new IllegalArgumentException("존재하지 않습니다.")
// );
return this.todoRepository.findById(id).orElseThrow(
() -> new ResponseStatusException(HttpStatus.NOT_FOUND)
);
}
모든 목록 조회 메소드
- todoRepository의 .findAll() 반환
// 목록 전체 조회
public List<TodoEntity> searchAll() {
return todoRepository.findAll();
}
수정하기 메소드
// 수정
public TodoEntity updateById(Long id, TodoRequest todoRequest) {
TodoEntity todoEntity = this.searchById(id);
if (todoRequest.getTitle() != null) {
todoEntity.setTitle(todoRequest.getTitle());
}
if (todoRequest.getOrder() != null) {
todoEntity.setOrder(todoRequest.getOrder());
}
if (todoRequest.getCompleted() != null) {
todoEntity.setCompleted(todoRequest.getCompleted());
}
return this.todoRepository.save(todoEntity);
}
삭제하기 메소드
전체 삭제 메소드
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;
// 등록
public TodoEntity add(TodoRequest todoRequest) {
TodoEntity todoEntity = new TodoEntity();
todoEntity.setTitle(todoRequest.getTitle());
todoEntity.setOrder(todoRequest.getOrder());
todoEntity.setCompleted(todoRequest.getCompleted());
return this.todoRepository.save(todoEntity);
}
// 조회
public TodoEntity searchById(Long id) {
// this.todoRepository.findById(id).orElseThrow(
// () -> new IllegalArgumentException("존재하지 않습니다.")
// );
return this.todoRepository.findById(id).orElseThrow(
() -> new ResponseStatusException(HttpStatus.NOT_FOUND)
);
}
// 목록 전체 조회
public List<TodoEntity> searchAll() {
return todoRepository.findAll();
}
// 수정
public TodoEntity updateById(Long id, TodoRequest todoRequest) {
TodoEntity todoEntity = this.searchById(id);
if (todoRequest.getTitle() != null) {
todoEntity.setTitle(todoRequest.getTitle());
}
if (todoRequest.getOrder() != null) {
todoEntity.setOrder(todoRequest.getOrder());
}
if (todoRequest.getCompleted() != null) {
todoEntity.setCompleted(todoRequest.getCompleted());
}
return this.todoRepository.save(todoEntity);
}
// 삭제
public void deleteById(Long id) {
this.todoRepository.deleteById(id);
}
// 전체 삭제
public void deleteAll() {
this.todoRepository.deleteAll();
}
}
package org.example.controller;
import lombok.AllArgsConstructor;
import org.example.model.TodoResponse;
import org.example.repository.TodoRepository;
import org.example.service.TodoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin
@RestController
@AllArgsConstructor
@RequestMapping("/")
public class TodoController {
private final TodoService todoService;
@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<TodoResponse>> readAll() {
System.out.println("Read All");
return null;
}
@PatchMapping("{id}")
public ResponseEntity<TodoResponse> 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;
}
}
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.swing.*;
@SpringBootApplication
public class TodoServerApplication {
public static void main(String[] args) {
System.out.println("hi");
SpringApplication.run(TodoServerApplication.class, args);
}
}
todo 생성 메소드
하나만 조회 메소드
모두 조회 메소드
업데이트 메소드
하나 삭제 메소드
전체 삭제 메소드
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.repository.TodoRepository;
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
@RestController
@AllArgsConstructor
@RequestMapping("/")
public class TodoController {
private final TodoService todoService;
// 생성
@PostMapping
public ResponseEntity<TodoResponse> create(@RequestBody TodoRequest todoRequest) {
System.out.println("Create");
// 제목이 없으면 badRequest 처리
if (ObjectUtils.isEmpty(todoRequest.getTitle()))
return ResponseEntity.badRequest().build();
// order값이 없으면 기본(default)처리
if (ObjectUtils.isEmpty(todoRequest.getOrder()))
todoRequest.setOrder(0L);
// completed값이 없으면 기본(default)처리
if (ObjectUtils.isEmpty(todoRequest.getCompleted()))
todoRequest.setCompleted(false);
TodoEntity result = this.todoService.add(todoRequest);
return ResponseEntity.ok(new TodoResponse(result));
}
// 하나만 조회
@GetMapping("{id}")
public ResponseEntity<TodoResponse> readOne(@PathVariable Long id) {
System.out.println("Read One");
TodoEntity result = this.todoService.searchById(id);
return ResponseEntity.ok(new TodoResponse(result));
}
// 모두 조회
@GetMapping
public ResponseEntity<List<TodoResponse>> readAll() {
System.out.println("Read All");
List<TodoEntity> list = this.todoService.searchAll();
List<TodoResponse> response = list.stream().map(TodoResponse::new).collect(Collectors.toList());
return ResponseEntity.ok(response);
}
// 업데이트
@PatchMapping("{id}")
public ResponseEntity<TodoResponse> update(@PathVariable Long id, @RequestBody TodoRequest todoRequest) {
System.out.println("Update");
TodoEntity result = this.todoService.updateById(id, todoRequest);
return ResponseEntity.ok(new TodoResponse(result));
}
// 하나 삭제
@DeleteMapping("{id}")
public ResponseEntity<?> deleteOne(@PathVariable Long id) {
System.out.println("Delete One");
this.todoService.deleteById(id);
return ResponseEntity.ok().build();
}
@DeleteMapping
public ResponseEntity<?> deleteAll() {
System.out.println("Delete All");
this.todoService.deleteAll();
return ResponseEntity.ok().build();
}
}
주소 입력 - Body - raw - JSON 선택 후 빈칸에 아래처럼 입력 후 send 클릭
{
"title" : "테스트입니다."
}
delete 시에는 주소에
localhost:8080/아이디 번호
처럼 입력
testImplementation('org.springframework.boot:spring-boot-starter-test')
테스트할 파일 TodoService.java로 와서 alt + Insert로 test... 클릭 후 테스트할 메소드 선택하고 만들기
moc 개체를 사용하기 위해
@ExtendWith(MockitoExtension.class)
어노테이션 추가
mock과 mock을 주입받을 필드 추가
@Mock
private TodoRepository todoRepository;
@InjectMocks
private TodoService todoService;
mock을 사용하는 이유 : 외부시스템을 사용하지 않고 자체적으로 테스트 가능, 실제 db와 연결하지 않고(함부로 만지면 위험하니까) 진행해야하기 때문