
오늘 포스팅에서는 스프링부트의 애플리케이션의 기본구조를 살펴보고 에이피아이 서버 애플리케이션을 만들고 데이터의 생성, 검색, 수정, 삭제 기능을 구현 및 H2 데이터베이스를 추가하는 작업을 보겠습니다.
목차
스프링부트 구조
먼저, 프로젝트 구조를 살펴보면 아래와 같이 세 영역으로 나뉘어 각자의 역할을 담당.

API를 통한 데이터 통신은 HTTP를 통해서 이루어지며 데이터는 다양한 형식 중 JSON 형태로 가장 많이 전송됩니다. 스프링은 제이슨 정보를 활용하기 위해 같은 유입되는 제이슨과 대칭되는 자바 POJO (엔티티)로 전환하는 데 이는 Jackson이라는 프로그램에 의해 자동으로 실행됩니다. 전환은 요청이 올 때 제이슨을 POJO 클래스의 Setter를 통해 반대로 응답은 Getter를 통해 이루어집니다.
필요 디펜던시들
디펜던시는 아래 디펜던시 코드를 pom.xml파일에 넣고 우측상단에 리프레시 버튼을 누릅니다 (아래 그림 참조).
Spring-web
HTTP요청처리를 위한 어노테이션과 웹 서버 구동을 위해 필요.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
H2
인메모리 데이터베이스 디펜던시
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
엔티티로부터 데이터베이스에 테이블 생성
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

프로젝트 구조 설정
메인 프로젝트 폴더 아래 아래와 같이 컨트롤러, 엔티티, 서비스 패키지를 생성하고 그 아래 각 클래스를 생성

구현하기
엔티티
엔티티는 데이터를 처리하기 위한 객체로 유입되는 추후에 HTTP를 통해 들어올 때 각 데이터의 이름이 엔티티에 규정된 속성이름과 일치해야 하며, 각 속성에 접근 가능하도록 Getter, Setter를 반드시 추가필요.
package com.example.employee.entity;
public class Employee {
private int employeeId;
private String employeeName;
private String employeeCity;
public Employee(int employeeId, String employeeName, String employeeCity) {
this.employeeId = employeeId;
this.employeeName = employeeName;
this.employeeCity = employeeCity;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeeCity() {
return employeeCity;
}
public void setEmployeeCity(String employeeCity) {
this.employeeCity = employeeCity;
}
}
어노테이션
특정 코드 위에 어노테이션을 표시하면 스프링부트는 자동으로 해당 어노테이션이 가진 기능을 수행합니다.
컨트롤러에 클래스에서 사용되는 어노테이션
▶ @Controller
특정 클래스를 컨트롤러로 표시
@Controller
▶ @ResponseBody
컨트롤러 어노테이션과 함께 사용되며 HTTP 바디를 통해 정보를 전송받기 위해 필요
@ResponseBody
▶ @RestController
@Controller와 @ResponseBody의 기능을 한 번에 수행
@RestController
▶ @Autowired
서비스 클래스 디펜던시 인젝션을 위해 필요
@Autowired
서비스클래스에서 사용되는 어노테이션
▶ @Service
특정 클래스를 서비스로 표시
@Service
엔티티 클래스에서 사용되는 어노테이션
▶ @Entity
JPA 클래스 테이블 생성을 위해 필요
@Entity
▶ @Table
JPA 클래스 테이블 이름은 클래스이름으로 생성되는 데 이를 원하는 이름으로 수정할 때 사용
@Table(name="<name>")
▶ @Id
관계형 데이터베이스에 필요한 주키설정
@Id
▶ @GeneratedValue
주키 자동 생성옵션
@GeneratedValue(strategy = GenerationType.IDENTITY)
▶ @OneToOne
1대 1 관계설정 어노테이션
@OneToOne
▶ @OneTo Many
1대 다 관계설정 어노테이션
@OneToMany
▶ @ManyTo One
다대 1 관계설정 어노테이션
@ManyToOne
▶ @ManyToMany
다대 다 관계설정 어노테이션
@ManyToMany
▶ @JoinColumn
관계설정 시 추가되는 필드명은 속성명을 사용하는 데 이 필드명을 변경할 때 사용
@JoinColumn(name = "<fk_spouse>")
▶ @JoinTable
다대 다 관계설정 시 추가되는 필드명은 속성명을 사용하는 데 이 필드명을 변경할 때 사용
@JoinTable(name = "<tableName>", joinColumns = @JoinColumn(name = "<fieldName>"),
inverseJoinColumns = @JoinColumn(name = "<fieldName>"))
▶ @JsonIgnore
연결되는 엔티티가 다의 관계를 가질 때 생성되는 데이터가 무한 루프에 빠지는 현상방지
@JsonIgnore
서비스 클래스
컨트롤러 요청에 의해 데이터베이스에 접근하여 데이터를 전송받거나 수정하고 그 결과를 다시 컨트롤러로 반환
▶ 데이터 생성 (예시를 위해 서비스클래스에 직접 생성)
List<Employee> employeeList = new ArrayList<>(Arrays.asList(
new Employee(1, "John", "Washington"),
new Employee(2, "Tom", "Waterloo")
));
▶ GET (아이템 리스트)
public List<Employee> getEmployees() {
return employeeList;
}
▶ GET (특정 아이템)
public Employee getEmployee(int employeeId) {
return employeeList.stream().filter(e -> (
e.getEmployeeId() == employeeId
)).findFirst().get();
}
▶ POST
public void addEmployee(Employee employee) {
employeeList.add(employee);
}
▶ PUT
public void updateEmployee(Employee employee) {
List<Employee> tempList = new ArrayList<>();
for (Employee e: employeeList) {
if(e.getEmployeeId() == employee.getEmployeeId()) {
e.setEmployeeName(employee.getEmployeeName());
e.setEmployeeCity(employee.getEmployeeCity());
}
tempList.add(e);
}
this.employeeList = tempList;
}
▶ DELETE
public void deleteEmployee(int id) {
List<Employee> tempList = new ArrayList<>();
for (Employee e: employeeList) {
if(e.getEmployeeId() == id) {
continue;
}
tempList.add(e);
}
this.employeeList = tempList;
}
▶ 완성코드
package com.example.employee.service;
import com.example.employee.entity.Employee;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class EmployeeService {
List<Employee> employeeList = new ArrayList<>(Arrays.asList(
new Employee(1, "John", "Washington"),
new Employee(2, "Tom", "Waterloo")
));
public List<Employee> getEmployees() {
return employeeList;
}
public Employee getEmployee(int employeeId) {
return employeeList.stream().filter(e -> (
e.getEmployeeId() == employeeId
)).findFirst().get();
}
public void addEmployee(Employee employee) {
employeeList.add(employee);
}
public void updateEmployee(Employee employee) {
List<Employee> tempList = new ArrayList<>();
for (Employee e: employeeList) {
if(e.getEmployeeId() == employee.getEmployeeId()) {
e.setEmployeeName(employee.getEmployeeName());
e.setEmployeeCity(employee.getEmployeeCity());
}
tempList.add(e);
}
this.employeeList = tempList;
}
public void deleteEmployee(int id) {
List<Employee> tempList = new ArrayList<>();
for (Employee e: employeeList) {
if(e.getEmployeeId() == id) {
continue;
}
tempList.add(e);
}
this.employeeList = tempList;
}
}
컨트롤러 클래스
컨트롤러는 위 그림에서 처럼 프레젠테이션 영역에 속하며 HTTP요청의 엔드포인트(주소)를 생성하고 해당 엔드포인트에 대한 요청을 서비스로 전달합니다. 이후 서비스에서 처리된 정보를 프론트로 전송합니다.
▶ GET (아이템 리스트)
@RequestMapping("/employees")
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
또는
@GetMapping("/employees")
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
▶ GET (특정 아이템)
@RequestMapping("/employees/{id}")
public Employee getEmployee(@PathVariable int id) {
return employeeService.getEmployee(id);
}
또는
@GetMapping("/employees/{id}")
public Employee getEmployee(@PathVariable int id) {
return employeeService.getEmployee(id);
}
▶ POST
@RequestMapping(value = "/employees", method = RequestMethod.POST)
public void addEmployee(@RequestBody Employee employee) {
employeeService.addEmployee(employee);
}
또는
@PostMapping("/employees")
public void addEmployee(@RequestBody Employee employee) {
employeeService.addEmployee(employee);
}
▶ PUT
@RequestMapping(value = "/employees/{id}", method = RequestMethod.PUT)
public void updateEmployee(@PathVariable int id, @RequestBody Employee employee) {
employeeService.updateEmployee(employee);
}
또는
@PutMapping("/employees/{id}")
public void updateEmployee(@PathVariable int id, @RequestBody Employee employee) {
employeeService.updateEmployee(employee);
}
▶ DELETE
@RequestMapping(value = "/employees/{id}", method = RequestMethod.DELETE)
public void deleteEmployee(@PathVariable int id) {
employeeService.deleteEmployee(id);
}
또는
@DeleteMapping("/employees/{id}")
public void deleteEmployee(@PathVariable int id) {
employeeService.deleteEmployee(id);
}
▶ 완성코드
package com.example.employee.controller;
import com.example.employee.entity.Employee;
import com.example.employee.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@ResponseBody
//@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@RequestMapping("/employees")
//@GetMapping("/employees")
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
@RequestMapping("/employees/{id}")
//@GetMapping("/employees/{id}")
public Employee getEmployee(@PathVariable int id) {
return employeeService.getEmployee(id);
}
@RequestMapping(value = "/employees", method = RequestMethod.POST)
//@PostMapping("/employees")
public void addEmployee(@RequestBody Employee employee) {
employeeService.addEmployee(employee);
}
@RequestMapping(value = "/employees/{id}", method = RequestMethod.PUT)
//@PutMapping("/employees/{id}")
public void updateEmployee(@PathVariable int id, @RequestBody Employee employee) {
employeeService.updateEmployee(employee);
}
@RequestMapping(value = "/employees/{id}", method = RequestMethod.DELETE)
//@DeleteMapping("/employees/{id}")
public void deleteEmployee(@PathVariable int id) {
employeeService.deleteEmployee(id);
}
}
데이터베이스(H2) 추가하기
속성설정
application.properties파일을 열고 아래 데이터베이스 설정 코드를 삽입합니다.
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:<dbName>
spring.datasource.username=<name>
spring.datasource.password=<password>
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

※ 아래 옵션은 콘솔에 sql명령어를 표시하는 옵션과 표시되는 명령어 포맷 옵션
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
디펜던시 추가 (위 디펜던시 항목 참조)
엔티티 어노테이션 추가
제이피아이을 활용하여 테이블을 생성하기 위해 @Entity, @Id 어노테이션과 기본 컨스트럭터를 아래와 같이 추가필요
package com.example.testemployee.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int employeeId;
private String employeeName;
private String employeeCity;
public Employee() {
}
public Employee(int employeeId, String employeeName, String employeeCity) {
this.employeeId = employeeId;
this.employeeName = employeeName;
this.employeeCity = employeeCity;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeeCity() {
return employeeCity;
}
public void setEmployeeCity(String employeeCity) {
this.employeeCity = employeeCity;
}
}
연결확인
설정 후 앱을 가동하고 브라우저에 아래 주소로 접속
http://localhost:8080/h2-console
URL, 이름, 비밀번호를 application.properties에 입력한 값과 동일하게 하고 연결 선택


레포지토리 추가
레포지토리 패키지를 추가하고 그 아래 JPA레포지토리를 확장할 인터페이스를 생성합니다.

package com.example.employee.repository;
import com.example.employee.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
}
서비스 클래스 수정
서비스 클래스에 생성한 레포지토리 인터페이스를 속성으로 추가하고 @Autowired 어노테이션을 추가 후 각 메서드 내 코드를 아래와 같이 교체
package com.example.employee.service;
import com.example.employee.entity.Employee;
import com.example.employee.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class EmployeeService {
@Autowired
EmployeeRepository employeeRepository;
public List<Employee> getEmployees() {
return employeeRepository.findAll();
}
public Employee getEmployee(int employeeId) {
return employeeRepository.findById(employeeId).orElseThrow(() -> new RuntimeException("Not found"));
}
public void addEmployee(Employee employee) {
employeeRepository.save(employee);
}
public void updateEmployee(Employee employee) {
/*
* jpa는 저장 명령어만 존재
* 전송하는 ID가 DB에 존재하는 경우 업데이트
* 존재하지 않는 경우 새 아이템 저장
*/
employeeRepository.save(employee);
}
public void deleteEmployee(int id) {
employeeRepository.delete(employeeRepository.getReferenceById(id));
}
}
케스케이드
케스케이드는 부모엔티티와 자식엔티티에 생성되는 데이터의 일관성을 유지하기 위한 옵션으로 아래와 같이 세 가지 옵션이 존재합니다.
▶ ALL
부모 엔티티에 실행되는 모든 명령어를 자식 엔티티에도 실행 (생성, 삭제, 수정,...)
@OneToOne(cascade = CascadeType.ALL)
▶ PERSIST
부모 엔티티에 실행되는 저장 명령어만 자식 엔티티에도 실행 (나머지 명령어 실행 불가)
@OneToOne(cascade = CascadeType.PERSIST)
▶ REMOVE
부모 엔티티에 실행되는 삭제 명령어만 자식 엔티티에도 실행 (나머지 명령어 실행 불가)
@OneToOne(cascade = CascadeType.REMOVE)
▶ REFRESH
부모 엔티티에 실행되는 갱신 명령어만 자식 엔티티에도 실행 (나머지 명령어 실행 불가)
@OneToOne(cascade = CascadeType.REFRESH)
▶ DETACH
부모 엔티티에 실행되는 디타치 명령어만 자식 엔티티에도 실행 (나머지 명령어 실행 불가)
@OneToOne(cascade = CascadeType.DETACH)
▶ MERGE
부모 엔티티에 실행되는 합치기 명령어만 자식 엔티티에도 실행 (나머지 명령어 실행 불가)
@OneToOne(cascade = CascadeType.MERGE)
※ 중괄호를 사용하여 복수의 케스테이드 옵션 추가도 가능
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
펫치타입
펫치타입은 부모엔티티 데이터를 부를 때 연결된 자식엔티티도 같이 호출하는 옵션으로 아래와 같이 두 가지 옵션이 존재합니다.
▶ EAGER
부모 엔티티의 데이터를 호출할 때 연결된 자식 데이터도 같이 호출 (one to one, many to one 관계에서 기본옵션)
@OneToMany(fetch = FetchType.EAGER)
▶ LAZY
부모 엔티티의 데이터를 호출할 때 연결된 자식 데이터 호출하지 않음 (one to many, many to many 관계에서 기본옵션)
@OneToOne(FetchType.LAZY)
관계추가하기
다음으로 데이터베이스에 관계를 추가해 보겠습니다.
▶ 1대 1 관계
참조 엔티티를 생성하고 생성자, 엔티티가 가지는 속성 및 Getter, Setter를 추가한 뒤 주 엔티티를 속성으로 추가하고 아래와 같이 @OneToOne어노테이션에 'mappedBy'옵션을 사용하여 주 엔티티에 추가할 보조엔티티 필드와 연결 (해당 작업 생략 시 주 엔티티에서 보조만 참조가능한 일방향 관계 성립) 및 Getter, Setter 추가.
@OneToOne(mappedBy = "spouse")
private Employee employee;

생성한 속성 Getter, Setter 추가
양방향 관계가 성립 후 보조 엔티티 캐스케이드를 추가하면 보조엔티티를 통해 주엔티티를 저장하거나 삭제도 가능
@OneToOne(mappedBy = "spouse", cascade = CascadeType.ALL)
private Employee employee;
삭제 시 주 엔티티 데이터를 남겨두고 싶은 경우, 케스케이드에서 delete를 제외
@OneToOne(mappedBy = "employeeDetail", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private Employee employee;
삭제 메서드에서 널 값지정으로 두 엔티티 간 링크 제거
public void deleteEmployeeDetail(int id) {
EmployeeDetail employeeDetail = entityManager.find(EmployeeDetail.class, id);
employeeDetail.getEmployee().setEmployeeDetail(null);
entityManager.remove(employeeDetail);
}
주 엔티티로 이동하여 생성한 엔티티를 속성에 @OneToOne 어노테이션을 추가하고 주 엔티티의 데이터가 삭제될 때 연결된 엔티티의 데이터도 삭제되도록 'cascade' 옵션을 추가 (@JoinColumn 어노테이션은 필드명 수정 옵션) 및 생성한 속성 Getter, Setter 추가
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fk_spouse")
private Spouse spouse;

▶ 다대 1 관계
연결할 엔티티를 생성하고 생성자, 엔티티가 가지는 속성 및 Getter, Setter를 추가한 뒤 1의 관계 쪽 엔티티를 속성으로 추가하고 아래와 같이 @ManyToOne어노테이션에 옵션을 사용하여 연결 및 Getter, Setter를 추가. 다의 관계를 가지는 엔티티는 'cascade'옵션을 사용할 때 데이터 생성 시 무한루프에 빠지는 것을 방지하기 위해 @JsonIgnore 어노테이션 추가필요

추가된 필드를 포함하는 컨스트럭터 생성
▶ 1대 다 관계
1의 관계 쪽 엔티티에 다수의 엔티티를 아래와 같이 리스트로 추가하고 @OneToMany 어노테이션으로 연결 (주 엔티티의 데이터가 삭제될 때 연결된 엔티티의 데이터도 삭제되도록 'cascade' 옵션을 추가) 및 Getter, Setter 추가
@OneToMany(cascade = CascadeType.ALL)
private List<Address> addresses;
추가로 서비스 클래스의 주 데이터 생성하는 코드를 자식 엔티티 값이 반영되도록 아래와 같이 수정
package com.example.employee.service;
import com.example.employee.entity.Address;
import com.example.employee.entity.Employee;
import com.example.employee.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class EmployeeService {
@Autowired
EmployeeRepository employeeRepository;
public List<Employee> getEmployees() {
return employeeRepository.findAll();
}
public Employee getEmployee(int employeeId) {
return employeeRepository.findById(employeeId).orElseThrow(() -> new RuntimeException("Not found"));
}
//updated
public void addEmployee(Employee employee) {
ArrayList<Address> addresses = new ArrayList<>();
for (Address address : employee.getAddresses()) {
addresses.add(new Address(
address.getZip(),
address.getCity(),
address.getState(),
address.getCountry()
));
}
employee.setAddresses(addresses);
employeeRepository.save(employee);
}
public void updateEmployee(Employee employee) {
employeeRepository.save(employee);
}
public void deleteEmployee(int id) {
employeeRepository.delete(employeeRepository.getReferenceById(id));
}
}
@OneToMany(mappedBy = "employee", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<Project> projects;
public void add(Project project) {
if (project == null) {
projects = new ArrayList<>();
}
projects.add(project);
project.setEmployee(this);
}
▶ 다대 다 관계
연결할 엔티티를 생성하고 생성자, 엔티티가 가지는 속성 및 Getter, Setter를 추가한 뒤 연결할 엔티티를 속성으로 추가하고 아래와 같이 @ManyToMany어노테이션에 'mappedBy'옵션을 사용하여 주 엔티티에 추가할 보조엔티티 필드와 연결 (해당 작업 생략 시 주 엔티티에서 보조만 참조가능한 일방향 관계 성립) 및 Getter, Setter 추가.

주 엔티티에 생성한 엔티티를 아래와 같이 리스트로 추가하고 @ManyToMany 어노테이션으로 연결 (주 엔티티의 데이터가 삭제될 때 연결된 엔티티의 데이터도 삭제되도록 'cascade' 옵션을 추가) 및 Getter, Setter 추가
@ManyToMany(cascade = CascadeType.ALL)
private List<Project> projects;
※ 아래 코드사용하여 필드명 변경가능
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "fk_employee"),
inverseJoinColumns = @JoinColumn(name = "fk_project"))
private List<Project> projects;
다대 다 관계에서 데이터가 생성 또는 삭제될 때 연결된 두 테이블 모두 수정사항을 반영하기 위해 아래 두 함수를 주 테이블에 추가.
public void removeProject(Project project) {
this.projects.remove(project);
project.getEmployee().remove(project);
}
public void addProject(Project project) {
this.projects.add(project);
project.getEmployee().add(this);
}
에러처리
잘못된 요청 등으로 인해 에러가 발생한 경우 에러 응답은 기본적으로 HTML형태로 전송됩니다. HTML형태로 전송되는 에러 메시지는 가독성이나 활용성이 떨어지기 때문에 제이슨 형태의 응답으로 바꾸어보겠습니다.
어노테이션
▶ @ExceptionHandler
에러처리
@ExceptionHandler
▶ @ControllerAdvice
글로벌 에러 처리
@ControllerAdvice
엔티티 생성
에러 정보를 담은 POJO클래스 생성

▶ 코드
package com.example.employee.common;
public class ErrorRes {
private int status;
private String message;
private long timestamp;
public ErrorRes() {
}
public ErrorRes(int status, String message, long timestamp) {
this.status = status;
this.message = message;
this.timestamp = timestamp;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
에러처리 클래스 생성
에러처리 클래스 생성하고 'RuntimeException'을 확장

컨스트럭터 추가

▶ 코드
package com.example.employee.common;
public class NotFound extends RuntimeException {
public NotFound(String message) {
super(message);
}
public NotFound(String message, Throwable cause) {
super(message, cause);
}
public NotFound(Throwable cause) {
super(cause);
}
}
컨트롤러 클래스에 에러처리 메서드 추가
@ExceptionHandler
public ResponseEntity<ErrorRes> handleError(NotFound ex) {
ErrorRes err = new ErrorRes();
err.setStatus(HttpStatus.NOT_FOUND.value());
err.setMessage(ex.getMessage());
err.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<>(err, HttpStatus.NOT_FOUND);
}
@ExceptionHandler
public ResponseEntity<ErrorRes> handleException(Exception ex) {
ErrorRes err = new ErrorRes();
err.setStatus(HttpStatus.BAD_REQUEST.value());
err.setMessage(ex.getMessage());
err.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<>(err, HttpStatus.BAD_REQUEST);
}
글로벌 에러처리
위와 같이 컨트롤러에 추가하는 방식은 컨트롤러마다 일일이 작업이 필요한데 @ControllerAdvice 어노테이션을 사용하여 애플리케이션 단위에서 에러처리가 가능합니다.
에러처리 클래스를 생성하고 @ControllerAdvice 어노테이션추가, 컨트롤러에 작성했던 에러처리 메서드를 이 클래스로 옮깁니다.

▶ 코드
package com.example.employee.common;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public ResponseEntity<ErrorRes> handleError(NotFound ex) {
ErrorRes err = new ErrorRes();
err.setStatus(HttpStatus.NOT_FOUND.value());
err.setMessage(ex.getMessage());
err.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<>(err, HttpStatus.NOT_FOUND);
}
@ExceptionHandler
public ResponseEntity<ErrorRes> handleException(Exception ex) {
ErrorRes err = new ErrorRes();
err.setStatus(HttpStatus.BAD_REQUEST.value());
err.setMessage(ex.getMessage());
err.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<>(err, HttpStatus.BAD_REQUEST);
}
}
이상으로 스프링 부트를 활용하여 에이피아이 애플리케이션을 만들어 보았습니다.
참고
Commands
www.h2database.com
'백엔드 > 자바' 카테고리의 다른 글
스프링 부트 엑츄에이터 (0) | 2023.10.01 |
---|---|
데이터베이스 조작 도구 (JPA, 하이버네이, 마이바티스) (0) | 2023.09.04 |
스프링빈 빈 등록 (디펜던시 인젝션) (0) | 2023.08.30 |
스프링부트 뷰 템플릿 (타임리프, 제이에스피) (0) | 2023.08.23 |
스프링 부트 프로젝트 만들기 (0) | 2023.08.03 |