일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- cursor문
- 객체 비교
- 다형성
- NestedFor
- 오라클
- abstract
- 인터페이스
- 자동차수리시스템
- 생성자오버로드
- 한국건설관리시스템
- 사용자예외클래스생성
- 컬렉션 타입
- EnhancedFor
- 제네릭
- GRANT VIEW
- exception
- 어윈 사용법
- 집합_SET
- 정수형타입
- 예외미루기
- Java
- 대덕인재개발원
- oracle
- 환경설정
- 참조형변수
- 추상메서드
- 자바
- 컬렉션프레임워크
- 메소드오버로딩
- 예외처리
- Today
- Total
거니의 velog
(8) 스프링 부트와 API 서버 3 본문
3. 서비스 계층과 DTO 처리
* 엔티티 객체는 단순한 자바의 인스턴스가 아니라 JPA를 통해서 관리되고 있는 객체(영속 객체)이다. 따라서 실제 데이터를 서비스할 때는 엔티티 객체의 내용물을 복사해서 사용하는 DTO를 이용한다.
* 프로젝트 내에서 dto 패키지를 생성하고 TodoDTO를 생성한다.
package com.unlimited.mallapi.dto;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TodoDTO {
// 할일 번호
private Long tno;
// 할일 제목
private String title;
// 작성자
private String writer;
// 완료 여부
private boolean complete;
// 마감일, JSON 형식 지정
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate dueDate;
}
* TodoDTO는 Lombok의 기능을 활용해서 getter/setter를 생성하고 날짜는 화면에서 쉽게 처리하도록 @JsonFormat을 이용해서 '2024-02-28' 과 같은 포맷으로 구성한다.
(1) 서비스 선언
* 서비스 계층은 DTO 타입으로 데이터를 주고 받도록 구성한다. service 패키지를 구성하고 TodoService.java 인터페이스와 TodoServiceImpl.java 클래스를 추가한다.
* TodoService 인터페이스에는 등록 기능을 선언한다. 등록 기능은 반환값으로 새로 등록된 Todo의 번호를 반환하도록 한다.
package com.unlimited.mallapi.service;
import com.unlimited.mallapi.dto.TodoDTO;
public interface TodoService {
Long register(TodoDTO todoDTO);
}
* TodoServiceImpl은 TodoService 인터페이스의 구현체로 아직 내용은 없이 구성만을 추가한다.
package com.unlimited.mallapi.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.unlimited.mallapi.dto.TodoDTO;
import lombok.extern.log4j.Log4j2;
@Service
@Transactional(rollbackFor = Exception.class)
@Log4j2
public class TodoServiceImpl implements TodoService {
@Override
public Long register(TodoDTO todoDTO) {
log.info("............");
return null;
}
}
(2) ModelMapper 라이브러리
* 서비스 계층의 파라미터와 리턴 타입은 DTO를 이용하지만 내부적으로는 엔티티 객체를 사용해야 하는 경우가 많기 때문에 'DTO <-> 엔티티' 처리를 수월하게 할 수 있는 ModelMapper 를 활용하는 것이 편리하다.
* build.gradle 파일에 ModelMapper 라이브러리를 추가한다.
dependencies {
(...)
implementation 'org.modelmapper:modelmapper:3.1.1'
}
[NOTE]
* VSCode에서는 라이브러리를 추가한 후에는 'Clean.Server Workspace'를 해주는 것이 안전하다.
* 프로젝트에는 config 패키지를 추가하고 RootConfig.java 파일을 추가한다.
* RootConfig 는 스프링에서 설정 파일의 역할을 하는 @Configuration 어노테이션을 추가하고 ModelMapper를 설정한다.
package com.unlimited.mallapi.config;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RootConfig {
// ModelMapper 빈 등록
@Bean
public ModelMapper getMapper() {
// ModelMapper 객체 생성
ModelMapper modelMapper = new ModelMapper();
// ModelMapper 설정
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true) // 필드 매칭 활성화
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE) // 필드 접근 레벨 설정
.setMatchingStrategy(MatchingStrategies.LOOSE); // 매칭 전략 설정
// 생성한 ModelMapper 반환
return modelMapper;
}
}
4. 서비스 계층의 구현
* 서비스 계층의 구현은 TodoDTO 타입으로 파라미터나 리턴 타입을 처리한다. 그리고 TodoRepository로 Todo 엔티티 객체를 처리해야 하기 때문에 ModelMapper로 간단하게 처리하는 방법을 사용한다.
(1) 등록 기능의 구현
* TodoServiceImpl의 등록 기능은 다음과 같이 작성된다. 서비스 객체를 구성할 때는 항상 트랜잭션 처리를 설정해 두고 작업해야 한다.
package com.unlimited.mallapi.service;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.unlimited.mallapi.domain.Todo;
import com.unlimited.mallapi.dto.TodoDTO;
import com.unlimited.mallapi.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Service
@Transactional(rollbackFor = Exception.class)
@Log4j2
@RequiredArgsConstructor // 생성자 자동 주입
public class TodoServiceImpl implements TodoService {
// ModelMapper와 TodoRepository를 생성자 주입
private final ModelMapper modelMapper; // DTO와 Entity 간 변환을 위한 ModelMapper
private final TodoRepository todoRepository; // 할일 데이터 처리를 위한 TodoRepository
// 할일 등록 메서드
@Override
public Long register(TodoDTO todoDTO) {
log.info("할일 등록 서비스 호출");
// TodoDTO를 Todo 엔티티로 변환
Todo todo = modelMapper.map(todoDTO, Todo.class);
// TodoRepository를 통해 할일 저장
Todo savedTodo = todoRepository.save(todo);
// 저장된 할일의 번호 반환
return savedTodo.getTno();
}
}
* test 폴더에는 service 패키지를 추가하고 TodoServiceTests.java 파일을 추가해서 작성된 TodoService의 테스트를 진행한다.
package com.unlimited.mallapi.service;
import java.time.LocalDate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.unlimited.mallapi.dto.TodoDTO;
import lombok.extern.log4j.Log4j2;
@SpringBootTest
@Log4j2
public class TodoServiceTests {
// TodoService 주입
@Autowired
private TodoService todoService;
// 할일 등록 테스트
@Test
public void testRegister() {
// 할일 DTO 생성
TodoDTO todoDTO = TodoDTO.builder()
.title("서비스 테스트") // 할일 제목 설정
.writer("tester") // 작성자 설정
.dueDate(LocalDate.of(2024, 2, 28)) // 마감일 설정
.build();
// 할일 등록 서비스 호출
Long tno = todoService.register(todoDTO);
// 등록된 할일의 번호 출력
log.info("tno : {}", tno);
}
}
* 테스트 코드를 실행해서 새로운 번호와 Todo 데이터가 생성되는지 확인하고 데이터베이스에서도 결과를 확인해야 한다.
(2) 조회 기능의 구현
* TodoService에는 TodoDTO를 반환하는 조회용 메서드를 추가한다.
package com.unlimited.mallapi.service;
import com.unlimited.mallapi.dto.TodoDTO;
public interface TodoService {
Long register(TodoDTO todoDTO);
TodoDTO get(Long tno);
}
* TodoServiceImpl 에서의 구현은 Todo 엔티티 객체를 구하고 이를 ModelMapper를 이용해 TodoDTO로 변환해서 반환한다.
package com.unlimited.mallapi.service;
import java.util.Optional;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.unlimited.mallapi.domain.Todo;
import com.unlimited.mallapi.dto.TodoDTO;
import com.unlimited.mallapi.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Service
@Transactional(rollbackFor = Exception.class)
@Log4j2
@RequiredArgsConstructor // 생성자 자동 주입
public class TodoServiceImpl implements TodoService {
// ModelMapper와 TodoRepository를 생성자 주입
private final ModelMapper modelMapper; // DTO와 Entity 간 변환을 위한 ModelMapper
private final TodoRepository todoRepository; // 할일 데이터 처리를 위한 TodoRepository
// 할일 등록 메서드
@Override
public Long register(TodoDTO todoDTO) {
log.info("할일 등록 서비스 호출");
// TodoDTO를 Todo 엔티티로 변환
Todo todo = modelMapper.map(todoDTO, Todo.class);
// TodoRepository를 통해 할일 저장
Todo savedTodo = todoRepository.save(todo);
// 저장된 할일의 번호 반환
return savedTodo.getTno();
}
// 특정 번호의 할일 조회 메서드
@Override
public TodoDTO get(Long tno) {
// TodoRepository를 통해 특정 번호의 할일 조회
// import java.util.Optional;
Optional<Todo> result = todoRepository.findById(tno);
// 조회된 할일이 없으면 예외를 던짐
Todo todo = result.orElseThrow();
// 조회된 할일을 TodoDTO로 변환
TodoDTO dto = modelMapper.map(todo, TodoDTO.class);
// 변환된 TodoDTO 반환
return dto;
}
}
* 테스트 코드에서 현재 데이터베이스에 있는 번호로 확인해 보자.
package com.unlimited.mallapi.service;
import java.time.LocalDate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.unlimited.mallapi.dto.TodoDTO;
import lombok.extern.log4j.Log4j2;
@SpringBootTest
@Log4j2
public class TodoServiceTests {
// TodoService 주입
@Autowired
private TodoService todoService;
// 할일 등록 테스트
@Test
public void testRegister() {
// 할일 DTO 생성
TodoDTO todoDTO = TodoDTO.builder()
.title("서비스 테스트") // 할일 제목 설정
.writer("tester") // 작성자 설정
.dueDate(LocalDate.of(2024, 2, 28)) // 마감일 설정
.build();
// 할일 등록 서비스 호출
Long tno = todoService.register(todoDTO);
// 등록된 할일의 번호 출력
log.info("tno : {}", tno);
}
// 특정 번호의 할일 조회 테스트
@Test
public void testGet() {
// 조회할 할일 번호 설정
Long tno = 101L; // 방금 테스트 코드로 등록한 101번을 조회해 보자.
// 할일 조회 서비스 호출
TodoDTO todoDTO = todoService.get(tno);
// 조회된 할일 정보 출력
log.info(todoDTO);
}
}
(3) 수정/삭제 기능의 구현
* TodoService에 수정 기능과 삭제 기능을 선언한다.
package com.unlimited.mallapi.service;
import com.unlimited.mallapi.dto.TodoDTO;
public interface TodoService {
Long register(TodoDTO todoDTO);
TodoDTO get(Long tno);
void modify(TodoDTO todoDTO);
void remove(Long tno);
}
* TodoServiceImpl에서 수정 기능의 구현은 기존의 Todo를 먼저 로딩한 후에 TodoDTO에서 변경된 내용(title, complete, dueDate)을 반영하고 다시 저장하는 방식으로 구현된다.
package com.unlimited.mallapi.service;
import java.util.Optional;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.unlimited.mallapi.domain.Todo;
import com.unlimited.mallapi.dto.TodoDTO;
import com.unlimited.mallapi.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Service
@Transactional(rollbackFor = Exception.class)
@Log4j2
@RequiredArgsConstructor // 생성자 자동 주입
public class TodoServiceImpl implements TodoService {
// ModelMapper와 TodoRepository를 생성자 주입
private final ModelMapper modelMapper; // DTO와 Entity 간 변환을 위한 ModelMapper
private final TodoRepository todoRepository; // 할일 데이터 처리를 위한 TodoRepository
// 할일 등록 메서드
@Override
public Long register(TodoDTO todoDTO) {
log.info("할일 등록 서비스 호출");
// TodoDTO를 Todo 엔티티로 변환
Todo todo = modelMapper.map(todoDTO, Todo.class);
// TodoRepository를 통해 할일 저장
Todo savedTodo = todoRepository.save(todo);
// 저장된 할일의 번호 반환
return savedTodo.getTno();
}
// 특정 번호의 할일 조회 메서드
@Override
public TodoDTO get(Long tno) {
// TodoRepository를 통해 특정 번호의 할일 조회
// import java.util.Optional;
Optional<Todo> result = todoRepository.findById(tno);
// 조회된 할일이 없으면 예외를 던짐
Todo todo = result.orElseThrow();
// 조회된 할일을 TodoDTO로 변환
TodoDTO dto = modelMapper.map(todo, TodoDTO.class);
// 변환된 TodoDTO 반환
return dto;
}
// 할일 수정 메서드
@Override
public void modify(TodoDTO todoDTO) {
// TodoRepository를 통해 수정할 할일 조회
Optional<Todo> result = todoRepository.findById(todoDTO.getTno());
// 조회된 할일이 없으면 예외를 던짐
Todo todo = result.orElseThrow();
// 할일 정보 변경
todo.changeTitle(todoDTO.getTitle());
todo.changeDueDate(todoDTO.getDueDate());
todo.changeComplete(todoDTO.isComplete());
// 변경된 할일을 TodoRepository를 통해 저장
todoRepository.save(todo);
}
// 할일 삭제 메서드
@Override
public void remove(Long tno) {
// TodoRepository를 통해 할일 삭제
todoRepository.deleteById(tno);
}
}
* 앞의 기능들과 동일하게 테스트 코드를 통해서 동작 여부를 확인해 두는 것이 좋다.
[수정 기능 테스트]
@Test
public void testModify() {
Long tno = 101L;
TodoDTO getResult = todoService.get(tno);
log.info(getResult);
if (getResult != null) {
log.info("수정할 데이터가 있어유~");
}
}
@Test
public void testModify() {
Long tno = 101L;
TodoDTO getResult = todoService.get(tno);
log.info(getResult);
if (getResult != null) {
log.info("수정할 데이터가 있어유~");
TodoDTO todoDTO = TodoDTO.builder()
.tno(tno)
.title("수정된 제목이어유~ 일단 ㄱㄱ")
.writer(getResult.getWriter())
.dueDate(LocalDate.of(2023, 12, 12))
.complete(true)
.build();
todoService.modify(todoDTO);
log.info(todoDTO);
}
}
[삭제 기능 테스트]
@Test
public void testDelete() {
Long tno = 101L;
TodoDTO getResult = todoService.get(tno);
log.info(getResult);
if (getResult != null) {
todoService.remove(tno);
}
}
'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글
(10) 스프링 부트와 API 서버 5 (0) | 2024.02.28 |
---|---|
(9) 스프링 부트와 API 서버 4 (0) | 2024.02.28 |
(7) 스프링 부트와 API 서버 2 (0) | 2024.02.28 |
(6) 스프링 부트와 API 서버 1 (0) | 2024.02.26 |
(5) React-Router 3 (0) | 2024.02.26 |