JavaRush /Java Blog /Random-KO /REST API 및 다른 테스트 작업.
Денис
레벨 37
Киев

REST API 및 다른 테스트 작업.

Random-KO 그룹에 게시되었습니다
1부: 시작 어디서부터 시작해야 할까요? 이상하게도 기술 사양에서 비롯됩니다. 제출된 TOR를 읽은 후 그 내용과 고객이 기대하는 내용을 완전히 이해했는지 확인하는 것이 매우 중요합니다. 첫째, 이는 추가 구현에 중요하며, 둘째, 기대하는 바를 구현하지 않으면 이점이 없을 것입니다. 공기 낭비를 피하기 위해 간단한 기술 사양을 스케치해 보겠습니다. 그래서 나는 데이터를 보낼 수 있는 서비스를 원합니다. 데이터는 서비스에 저장되고 마음대로 나에게 반환됩니다. 또한 필요한 경우 이 데이터를 업데이트하고 삭제할 수 있어야 합니다 . 몇 문장만으로는 명확하지 않은 것 같죠? 거기로 어떻게 데이터를 보내고 싶나요? 어떤 기술을 사용해야 합니까? 이 데이터는 어떤 형식으로 되어 있나요? 들어오고 나가는 데이터의 예도 없습니다. 결론 - 기술 사양이 이미 좋지 않습니다 . 바꿔 말하면, HTTP 요청을 처리하고 전송된 데이터를 처리할 수 있는 서비스가 필요합니다. 이것은 인사 기록 데이터베이스가 될 것입니다. 우리는 직원을 두고 부서와 전문 분야로 나누어지며 직원에게 업무가 할당될 수 있습니다. 우리의 임무는 REST API를 사용하여 고용, 해고, 이동된 직원에 대한 회계 프로세스와 작업 할당 및 취소 프로세스를 자동화하는 것입니다. 1단계로 현재는 직원들만으로 작업을 진행하고 있습니다. 서비스에는 작업을 위한 여러 엔드포인트가 있어야 합니다. - POST /employee - 직원에 대한 데이터가 포함된 JSON 개체를 허용해야 하는 POST 요청입니다. 이 개체는 데이터베이스에 저장되어야 하며, 해당 개체가 데이터베이스에 이미 존재하는 경우 필드의 정보를 새 데이터로 업데이트해야 합니다. - GET /employee - 데이터베이스에 저장된 전체 직원 목록을 반환하는 GET 요청 - DELETE - DELETE /employee 특정 직원을 삭제하려면 직원 데이터 모델:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
파트 II: 작업을 위한 도구 작업 범위는 어느 정도 명확하지만 어떻게 수행할 것입니까? 분명히 테스트의 이러한 작업에는 코딩 방법을 확인하고 Spring을 사용하고 데이터베이스에 대해 약간의 작업을 수행하도록 하는 몇 가지 애플리케이션 목표가 제공됩니다. 자, 이렇게 해보자. REST API 지원과 데이터베이스를 갖춘 SpringBoot 프로젝트가 필요합니다. https://start.spring.io/ 웹사이트에서 필요한 모든 것을 찾을 수 있습니다. REST API 또는 다른 테스트 작업.  - 1 빌드 시스템, 언어, SpringBoot 버전을 선택하고 아티팩트 설정, Java 버전 및 종속성을 설정할 수 있습니다. 종속성 추가 버튼을 클릭하면 검색창이 있는 특징적인 메뉴가 나타납니다. Rest와 data라는 단어의 첫 번째 후보는 Spring Web과 Spring Data입니다. 우리는 이들을 추가할 것입니다. Lombok은 주석을 사용하여 getter 및 setter 메서드로 수 킬로미터에 달하는 코드를 제거할 수 있는 편리한 라이브러리입니다. 생성 버튼을 클릭하면 우리가 선호하는 IDE에서 이미 압축을 풀고 열 수 있는 프로젝트가 포함된 아카이브를 받게 됩니다. REST API 또는 다른 테스트 작업.  - 2 기본적으로 우리는 빌드 시스템에 대한 구성 파일이 포함된 빈 프로젝트를 받게 됩니다(제 경우에는 gradle이지만 Maven에서는 근본적인 차이가 없으며 하나의 스프링 시작 파일이 있습니다) 세심한 사람들은 두 가지에 주의할 수 있습니다 . 먼저 두 가지 설정 파일인 application.properties와 application.yml이 있습니다. 기본적으로 설정을 저장할 수 있는 빈 파일인 정확한 속성을 얻을 수 있지만 나에게는 yml 형식이 좀 더 읽기 쉬워 보입니다. 이제 비교를 보여 드리겠습니다. 왼쪽 그림이 더 컴팩트해 REST API 또는 다른 테스트 작업.  - 삼 보이지만 , 속성 경로에서 많은 양의 중복을 쉽게 볼 수 있습니다. 오른쪽 그림은 꽤 읽기 쉬운 트리 구조의 일반 yml 파일입니다. 나중에 프로젝트에서 이 파일을 사용하겠습니다. 세심한 사람들이 알아차릴 수 있는 두 번째 사실은 내 프로젝트에 이미 여러 패키지가 있다는 것입니다. 아직 정상적인 코드는 없지만 살펴볼 가치가 있습니다. 신청서는 어떻게 작성되나요? 특정 작업이 있으면 이를 분해해야 합니다. 즉, 작은 하위 작업으로 나누고 일관된 구현을 시작해야 합니다. 우리에게 요구되는 것은 무엇입니까? 클라이언트가 사용할 수 있는 API를 제공해야 하며 컨트롤러 패키지의 콘텐츠가 이 기능 부분을 담당합니다. 애플리케이션의 두 번째 부분은 데이터베이스, 즉 지속성 패키지입니다. 여기에는 데이터베이스 엔터티(엔티티)와 저장소(데이터베이스와 상호 작용할 수 있는 특수 스프링 인터페이스) 등이 저장됩니다. 서비스 패키지에는 서비스 클래스가 포함됩니다. 아래에서는 Spring형 서비스가 무엇인지에 대해 이야기하겠습니다. 그리고 마지막으로 중요한 것은 utils 패키지입니다. 예를 들어 날짜 및 시간 작업을 위한 클래스, 문자열 작업을 위한 클래스 등 모든 종류의 보조 메소드를 갖춘 유틸리티 클래스가 저장됩니다. 기능의 첫 번째 부분 구현을 시작해 보겠습니다. 파트 III: 컨트롤러
@RestController
@RequestMapping("${application.endpoint.root}")
@RequiredArgsConstructor
public class EmployeeController {

    private final EmployeeService employeeService;

    @GetMapping("${application.endpoint.employee}")
    public ResponseEntity<List<Employee>> getEmployees() {
        return ResponseEntity.ok().body(employeeService.getAllEmployees());
    }
}
이제 EmployeeController 클래스는 다음과 같습니다. 여기에는 주목할 만한 중요한 사항이 많이 있습니다. 1. 클래스 위의 주석인 첫 번째 @RestController는 애플리케이션에 이 클래스가 엔드포인트가 될 것임을 알려줍니다. 2. @RequestMapping은 필수는 아니지만 엔드포인트에 대한 특정 경로를 설정할 수 있는 유용한 주석입니다. 저것들. 이를 노크하려면 localhost:port/employee가 아니라 이 경우 localhost:8086/api/v1/employee에 요청을 보내야 합니다. 실제로 이러한 api/v1과 직원은 어디서 왔습니까? application.yml에서 자세히 살펴보면 다음 줄을 찾을 수 있습니다.
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
보시다시피, application.endpoint.root 및 application.endpoint.employee와 같은 변수가 있습니다. 이것들은 제가 주석에 쓴 내용과 정확히 같습니다. 이 방법을 기억하는 것이 좋습니다. 확장하거나 다시 작성하는 데 많은 시간을 절약할 수 있습니다. 기능 - 전체 프로젝트를 하드코딩하지 않고 구성에 모든 것을 포함하는 것이 항상 더 편리합니다. 3. @RequiredArgsConstructor는 불필요한 작성을 피할 수 있는 편리한 라이브러리인 Lombok 주석입니다. 이 경우 주석은 클래스에 모든 필드가 final 로 표시된 공개 생성자가 있다는 사실과 동일합니다.
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
그런데 하나의 주석으로 충분하다면 왜 그런 것을 작성해야 할까요? :) 그런데, 축하합니다. 가장 비공개적인 이 최종 필드는 악명 높은 종속성 주입에 지나지 않습니다. 그럼 실제로 EmployeeService는 어떤 분야인가요? 이는 이 엔드포인트에 대한 요청을 처리하는 프로젝트의 서비스 중 하나가 될 것입니다. 여기서의 아이디어는 매우 간단합니다. 각 클래스에는 고유한 작업이 있어야 하며 불필요한 작업으로 인해 과부하가 걸려서는 안 됩니다. 이것이 컨트롤러라면 요청을 받고 응답을 보내는 일을 맡게 되지만 우리는 추가적인 서비스에 처리를 맡기는 편이 낫습니다. 이 클래스에서 마지막으로 남은 것은 위에서 언급한 서비스를 이용하여 우리 회사의 전체 직원 목록을 반환하는 유일한 메소드이다. 목록 자체는 ResponseEntity라는 엔터티로 래핑됩니다. 나중에 필요한 경우 자동화 시스템이 이해할 수 있는 응답 코드와 메시지를 쉽게 반환할 수 있도록 이렇게 합니다. 예를 들어 ResponseEntity.ok()는 모든 것이 괜찮다고 말하는 200번째 코드를 반환하지만, 예를 들어 내가 반환하면
return ResponseEntity.badRequest().body(Collections.emptyList());
그러면 클라이언트는 코드 400(bad reuqest)과 응답에 빈 목록을 받게 됩니다. 일반적으로 요청이 잘못된 경우 이 코드가 반환됩니다. 하지만 하나의 컨트롤러로는 애플리케이션을 시작하기에 충분하지 않습니다. 우리의 의존성은 우리가 이것을 하는 것을 허용하지 않을 것입니다. 왜냐하면 우리는 여전히 기반을 가지고 있어야 하기 때문입니다. :) 음, 다음 부분으로 넘어가겠습니다. 파트 IV: 단순 지속성 우리의 주요 작업은 애플리케이션을 시작하는 것이므로 지금은 몇 개의 스텁으로 제한하겠습니다. Employee 유형의 객체 목록을 반환하는 Controller 클래스를 이미 본 적이 있습니다. 이것이 데이터베이스에 대한 엔터티가 됩니다. Demo.persistence.entity 패키지 에서 생성해 보겠습니다 . 앞으로 엔터티 패키지는 데이터베이스의 다른 엔터티로 보완될 수 있습니다.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
이것은 문처럼 단순한 클래스이며, 그 주석은 정확히 다음과 같이 말합니다. 이것은 데이터베이스 엔터티 @Entity이고 데이터 @Data가 있는 클래스입니다 - Lombok. 도움이 되는 Lombok은 우리에게 필요한 모든 getter, setter, 생성자를 생성해 줍니다. 글쎄, 케이크 위의 작은 체리는 @Accessors(chain = true) 입니다 . 사실 이것은 빌더 패턴의 숨겨진 구현입니다. 생성자를 통하지 않고 메서드를 통해 할당하려는 여러 필드가 있는 클래스가 있다고 가정합니다. 다른 순서로, 아마도 동시에 모두는 아닐 수도 있습니다. 애플리케이션에 어떤 종류의 논리가 포함될지 결코 알 수 없습니다. 이 주석은 이 작업의 핵심입니다. 한번 보자:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
우리 수업에 이 모든 필드가 있다고 가정해 봅시다😄 할당할 수도 있고 할당할 수 없으며 여러 곳에 혼합할 수도 있습니다. 단지 3개의 속성만 있는 경우 이는 뛰어난 것으로 보이지 않습니다. 그러나 예를 들어 50개와 같이 훨씬 더 많은 수의 속성을 가진 클래스가 있습니다. 그리고 다음과 같이 작성하십시오.
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
별로 예쁘지 않죠? 또한 생성자에 따라 변수를 추가하는 순서도 엄격히 지켜야 합니다. 그러나 나는 빗나가서 요점으로 돌아가겠습니다. 이제 여기에는 고유 식별자라는 하나의 (필수) 필드가 있습니다. 이 경우 데이터베이스에 저장될 때 자동으로 생성되는 Long 유형의 번호입니다. 따라서 @Id 주석은 이것이 고유 식별자임을 명확하게 나타냅니다. @GeneratedValue는 고유한 생성을 담당합니다. 자동으로 생성되지 않는 필드에 @Id를 추가할 수 있지만 고유성 문제는 수동으로 처리해야 한다는 점은 주목할 가치가 있습니다. 고유한 직원 식별자는 무엇입니까? 예를 들어 이름 + 부서... 그러나 사람의 이름이 동일하고 같은 부서에서 일할 가능성이 작지만 있습니다. 이는 결정이 나쁘다는 것을 의미합니다. 고용 날짜, 도시와 같은 다른 필드를 추가하는 것이 가능할 수 있지만 이 모든 것이 논리를 너무 복잡하게 만드는 것 같습니다. 여러 필드가 동시에 고유한 것이 어떻게 가능할까요? 나는 대답한다 – 아마도. 궁금하다면 @Embeddable 및 @Embedded와 같은 검색어를 검색해 보세요. 이제 본질은 끝났습니다. 이제 간단한 저장소가 필요합니다. 다음과 같이 보일 것입니다:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
예, 그게 전부입니다. EmployeeRepository라고 부르는 인터페이스는 두 가지 유형의 매개변수가 있는 JpaRepository를 확장합니다. 첫 번째는 작동하는 데이터 유형을 담당하고 두 번째는 키 유형을 담당합니다. 우리의 경우에는 Employee와 Long입니다. 지금은 그것으로 충분합니다. 애플리케이션을 시작하기 전 마지막 작업은 서비스입니다.
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
이미 익숙한 필수ArgsConstructor와 새로운 @Service 주석이 있습니다. 이는 일반적으로 비즈니스 논리 계층을 나타냅니다. Spring 컨텍스트를 실행할 때 이 주석이 표시된 클래스는 Bean으로 생성됩니다. EmployeeController 클래스에서 최종 속성인 EmployeeService를 생성하고 필수ArgsConstructor를 첨부한 경우(또는 직접 생성자를 생성한 경우) Spring은 애플리케이션을 초기화할 때 이 위치를 찾아 클래스 객체를 이 변수에 넣습니다. 여기서 기본값은 Singleton입니다. 이러한 모든 링크에는 하나의 개체가 있으므로 애플리케이션을 디자인할 때 이를 고려하는 것이 중요합니다. 실제로 그게 전부입니다. 응용 프로그램을 시작할 수 있습니다. 구성에 필요한 설정을 입력하는 것을 잊지 마세요. REST API 또는 다른 테스트 작업.  - 4 데이터베이스를 설치하는 방법, 사용자를 생성하는 방법, 데이터베이스 자체를 만드는 방법은 설명하지 않겠습니다. 하지만 URL에서 두 개의 추가 매개변수(useUnicore=true 및 CharacterEncoding=UTF-8)를 사용한다는 점만 참고하겠습니다. 이는 텍스트가 모든 시스템에서 어느 정도 동일하게 표시되도록 수행되었습니다. 그러나 너무 게으른 나머지 데이터베이스를 조작할 수 없고 실제로 작업 코드를 살펴보고 싶다면 빠른 해결 방법이 있습니다. 1. build.gradle에 다음 종속성을 추가합니다.
implementation 'com.h2database:h2:2.1.214'
2. application.yml에서 여러 속성을 편집해야 합니다. 단순화를 위해 스프링 섹션의 전체 예를 제공하겠습니다.
spring:
  application:
    name: "employee-management-service"
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.H2Dialect
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:./mydb
    username: sa
    password:
데이터베이스는 프로젝트 폴더의 mydb 파일에 저장됩니다 . 하지만 저는 완전한 데이터베이스를 설치하는 것을 추천하고 싶습니다 😉 주제에 대한 유용한 기사: H2 데이터베이스를 사용한 Spring Boot 만일을 대비해 종속성 불일치를 제거하기 위해 build.gradle의 전체 버전을 제공하겠습니다.
plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'java'
}

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

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'mysql:mysql-connector-java:8.0.30'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
시스템을 시작할 준비가 되었습니다. REST API 또는 다른 테스트 작업.  - 5 적절한 프로그램에서 엔드포인트로 GET 요청을 보내 시스템을 확인할 수 있습니다. 이 특별한 경우에는 일반 브라우저가 가능하지만 앞으로는 Postman이 필요할 것입니다. REST API 또는 다른 테스트 작업.  - 6 예, 사실 아직 비즈니스 요구 사항을 구현하지 않았지만 필요한 기능으로 시작하고 확장할 수 있는 애플리케이션이 이미 있습니다. 계속: REST API 및 데이터 검증
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION