지난 포스팅에서 설치 및 준비가 끝이났다.
그러니 이번에는 스프링 부트를 사용해 간단한 RestfulAPI를 제작해보자.
가장 먼저 할 것은 MariaDB에 대한 CRUD Restful API 개발이다.
마리아 디비는 오픈 소스의 RDBMS이다.
MySQL이 오라클에 인수된 후 MySQL의 핵심 개발자들이 제작한 오픈소스로, MySQL과 굉장히 유사하며, 오픈소스이기 때문에 무료인 덕에 상업용으로도 많이 사용되는 DB이다.
구현은 간단하게 아이디-패스워드로 구성된 테이블을 사용할 것이다.
JPA를 사용한 구현을 진행할 것이기 때문에 다음의 설정을 application.properties에 추가해주었다.
그 외의 다양한 설정은 다음 포스팅 참고
끝이났으면 일단 마리아디비에 linuxTest 이름의 데이터베이스 생성.
JPA를 사용할 예정이기에 굳이 테이블까지 만들어줄 필요는 없다. spring.jpa.hibernate.ddl-auto 설정에 따라 자동으로 존재하지 않는 테이블은 만들어지기 때문이다.
설정이 끝났으면 코딩을 시작한다.
여느 JPA 프로그램들과 마찬가지로 구조는
Domain - Repository - Service - Controller 로 작성한다.
여기서 각 클래스의 역할은 다음과 같다.
- Domain : DTO로, 데이터를 담기 위한 클래스.
- Repository : Spring Data JPA 방식으로, 데이터베이스에 접근하는 클래스.
- Service : 비즈니스 로직을 처리하는 클래스.
- Controller : 클라이언트와 데이터를 주고받는 클래스.
가장 먼저 도메인 클래스를 작성해준다.
앞서 언급한대로 아이디-비밀번호로 간단하게 이루어진 테이블을 사용할 것이다.
MariaDomain.java
package kr.co.opensource.maria;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@JsonInclude(JsonInclude.Include.NON_NULL) //Json 입력 데이터를 클래스 내 각 변수에 자동 매핑
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name="test_table2") // 매핑되는 테이블 설정
@ToString
public class MariaDomain {
@Id //기본키
@Column(name="test_key", columnDefinition="varchar(10)", nullable=false) // 칼럼 매핑 및 칼럼 정보
private String key;
@Column(name="test_val", columnDefinition="varchar(10) NOT NULL DEFAULT \"hi\"") // NULLABLE 여부는 이렇게도 설정 가능
private String val;
}
이제 앞서 만든 도메인을 DB에 연동하여 사용할 Repository 클래스를 만든다.
CrudRepository를 사용할 예정이고, 구현 시 <> 안에 사용할 도메인과 해당 도메인의 private key 타입을 넣어준다.
만약 private key가 여러 개라면 pk만 모아놓은 별도의 클래스 파일을 사용해야 한다.
MariaRepository.java
package kr.co.opensource.maria;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MariaRepository extends CrudRepository<MariaDomain, String>{
}
다음은 Repository와 연동될 Service 클래스이다.
비즈니스 로직은 전부 Service에 작성한다.
Service의 로직은 다음과 같이 설정했고, CommonCode 클래스 파일에 기재되어있다.
- 0 : 로직을 성공적으로 마쳤을 때
- 1 : 이미 존재하는 데이터 / 존재하지 않는 데이터
- 2 : JPA 내부에서 에러가 발생했을 때
MariaService.java
package kr.co.opensource.maria;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.co.opensource.CommonCode;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class MariaService {
@Autowired MariaRepository repo;
public int save(MariaDomain domain) {
log.info("MariaService.save init : {}", domain.toString());
if(repo.findById(domain.getKey()).orElse(null) != null) {
//이미 존재하는 데이터
log.info("MariaService.save data already exists : {}", domain.getKey());
return CommonCode.EXCEPTION;
}
try {
repo.save(domain);
}
catch(Exception e) {
//쿼리 과정 중 에러 발생
log.error("MariaService.save some internal error occured : {}", e.getMessage());
return CommonCode.ERROR;
}
return CommonCode.SUCCESS;
}
public int update(MariaDomain domain) {
log.info("MariaService.update init : {}", domain.toString());
if(repo.findById(domain.getKey()).orElse(null) == null) {
//존재하지 않는 데이터
log.info("MariaService.update data not exists : {}", domain.getKey());
return CommonCode.EXCEPTION;
}
try {
repo.save(domain);
}
catch(Exception e) {
//쿼리 과정 중 에러 발생
log.error("MariaService.update some internal error occured : {}", e.getMessage());
return CommonCode.ERROR;
}
return CommonCode.SUCCESS;
}
public int delete(String key) {
log.info("MariaService.delete init : {}", key);
if(repo.findById(key).orElse(null) == null) {
//존재하지 않는 데이터
log.info("MariaService.delete data not exists : {}", key);
return CommonCode.EXCEPTION;
}
try {
repo.deleteById(key);
}
catch(Exception e) {
log.error("MariaService.delete some internal error occured : {}", e.getMessage());
return CommonCode.ERROR;
}
return CommonCode.SUCCESS;
}
public MariaDomain find(String key) {
log.info("MariaService.find init : {}", key);
return repo.findById(key).orElse(null);
}
public List<MariaDomain> findAll() {
log.info("MariaService.findAll init");
Iterator<MariaDomain> domainItr = repo.findAll().iterator();
List<MariaDomain> domainList = new ArrayList();
while(domainItr.hasNext()) {
domainList.add(domainItr.next());
}
return domainList;
}
}
마지막으로 Service와 연동되며, 사용자와 직접 상호작용할 Controller 클래스를 작성한다.
어노테이션은 rest이기 때문에 당연히 @Controller 가 아닌 @RestController 어노테이션을 사용한다.
그리고 편의상 모든 작업 요청에 대한 Response Status 값은 201로 고정하였다.
MariaRestController.java
package kr.co.opensource.maria;
import java.nio.charset.Charset;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import kr.co.opensource.CommonCode;
import kr.co.opensource.ResponseMessage;
import kr.co.opensource.ResponseVO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController // REST API 용 컨트롤러. 만약 UI 기반이면 @Controller
public class MariaRestController {
@Autowired MariaService service;
@RequestMapping(value={"/maria","/maria/{key}"}, method= {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
public @ResponseBody ResponseEntity<ResponseVO> controller(HttpServletRequest req,
@PathVariable(value="key", required=false) String key,
@RequestBody(required=false) MariaDomain domain) {
log.info("MariaRestController.controller init http: {}", req.getMethod());
ResponseVO msg = new ResponseVO();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
String method = req.getMethod();
if(method.equals("GET")) {
if(key == null) {
log.info("MariaRestController.controller get all data");
List<MariaDomain> mariaDomainList = service.findAll();
msg.setStatus(HttpStatus.OK);
msg.setMessage(ResponseMessage.OK);
msg.setData(mariaDomainList);
//편의상 에러는 전부 BAD_REQUEST로 기재
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.OK);
}
log.info("MariaRestController.controller get one data : {}", key);
MariaDomain returnDomain = service.find(key);
if(returnDomain == null) {
log.warn("MariaRestController.controller not exists data : {}", key);
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
msg.setStatus(HttpStatus.OK);
msg.setMessage(ResponseMessage.OK);
msg.setData(returnDomain);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.OK);
}
else if(method.equals("POST")) {
log.info("MariaRestController.controller save data : {}", domain);
if(domain == null) {
log.error("MariaRestController.controller required parameter not exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS_BODY);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
int result = service.save(domain);
if(result == CommonCode.EXCEPTION) {
//이미 존재하는 데이터
log.warn("MariaRestController.controller already exist data : {}", domain);
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.EXISTS);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else if(result == CommonCode.ERROR) {
//쿼리 실패
log.warn("MariaRestController.controller some interner error exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.INTERNAL_ERROR);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else {
//성공
log.info("MariaRestController.controller successfully saved data : {}", domain);
msg.setStatus(HttpStatus.CREATED);
msg.setMessage(ResponseMessage.CREATED);
msg.setData(domain);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.CREATED);
}
}
else if(method.equals("PUT")) {
log.info("MariaRestController.controller update data : {}", key);
if(key == null) {
log.warn("MariaRestController.controller required parameter not exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS_KEY);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
int result = service.update(domain);
if(result == CommonCode.EXCEPTION) {
log.info("MariaRestController.controller not exists data : {}", domain);
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else if(result == CommonCode.ERROR) {
//쿼리 실패
log.warn("MariaRestController.controller some interner error exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.INTERNAL_ERROR);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else {
//성공
log.info("MariaRestController.controller successfully updated data : {}", key);
msg.setStatus(HttpStatus.OK);
msg.setMessage(ResponseMessage.OK);
msg.setData(domain);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.OK);
}
}
else if(method.equals("DELETE")) {
log.info("MariaRestController.controller update data : {}", key);
if(key == null) {
log.warn("MariaRestController.controller required parameter not exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS_KEY);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
int result = service.delete(key);
if(result == CommonCode.EXCEPTION) {
log.warn("MariaRestController.controller not exists data : {}", key);
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.NOT_EXISTS);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else if(result == CommonCode.ERROR) {
log.warn("MariaRestController.controller some interner error exists");
msg.setStatus(HttpStatus.BAD_REQUEST);
msg.setMessage(ResponseMessage.INTERNAL_ERROR);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.BAD_REQUEST);
}
else {
//성공
log.info("MariaRestController.controller successfully deleted data : {}", key);
msg.setStatus(HttpStatus.OK);
msg.setMessage(ResponseMessage.OK);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.OK);
}
}
//여기까지 올일은 사실상 없음.
//그냥 return null 하기 싫어서 기재.
msg.setStatus(HttpStatus.METHOD_NOT_ALLOWED);
msg.setMessage(ResponseMessage.NOT_ALLOWED_METHD);
msg.setData(null);
return new ResponseEntity<ResponseVO>(msg, headers, HttpStatus.METHOD_NOT_ALLOWED);
}
}
완료되었으니 이제 POSTMAN을 활용해 테스트를 수행해보자.
1) 등록
2) 수정
3) 조회
4) 삭제
전부 정상작동함을 확인할 수 있다.
'실습 > 리눅스 서버 + 스프링 부트' 카테고리의 다른 글
05. RestfulAPI_Redis CRUD (0) | 2021.05.23 |
---|---|
04. RestfulAPI_MongoDB (0) | 2021.05.23 |
02. 윈도우 세팅 및 프로젝트 생성 (0) | 2021.05.19 |
01. 리눅스 서버 세팅 (0) | 2021.05.05 |
00. VB 및 리눅스 서버 설치 (0) | 2021.05.05 |