• Home
  • About Me
    • Yoon's Blog photo

      Yoon's Blog

      융가의
      개발일지입니다 :)

    • Learn More
    • Email
    • Facebook
    • Instagram
    • Github
  • Posts
    • All Posts
    • All Tags
    • JAVA
      • Java
      • spring Boot
    • PHP
      • PHP 7
      • Codeigniter
      • Lalavel
    • Linux
      • Linux
      • Ubuntu
    • DataBase
      • Oracle
      • Mysql
    • Develope Enviroment
      • Eclipse
      • Docker
      • Git hub

[Spring Boot] REST API Project [3]

04 Jul 2021

Reading time ~4 minutes

이제 Log 와 Swqgger 설정이 완료됐으니 본격적으로 REST API Project를 만들어보자.

1. Application

  
package com.yoon.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.ApplicationPidFileWriter;

@SpringBootApplication
public class RestapiApplication {

	public static void main(String[] args) {
		// SpringApplication.run(RestapiApplication.class, args);   // 기존
    
		SpringApplication application = new SpringApplication(RestapiApplication.class);
		application.addListeners(new ApplicationPidFileWriter());
		application.run();
	} 
} 

2. Config

1) @PersistenceContext

  • Entity를 영구 저장하는 환경으로, 논리적인 개념이다.
  • Entity Manager로 Entity를 저장(persist()), 조회(find() 또는 JPQL, QueryDSL)하면 Entity Manager는 그 Entity를 영속성 컨테스트에 보관하고 관리한다.
  • Entity의 @Id 필드를 이용하여 Entity를 식별한다.
  • 쓰기 지연 기능이 있음. 즉 값을 변경하자마자 바로 DB에 반영하는게 아닌, SQL 저장소에 쿼리들을 저장해뒀다가 Entity Manager가 commit()을 호출하면 그때 DB에 반영(flush)된다.
  
package com.yoon.api.config;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.querydsl.jpa.impl.JPAQueryFactory;

@Configuration
public class QuerydslConfig {

   @PersistenceContext
   private EntityManager entityManager;

   @Bean
   public JPAQueryFactory jpaQueryFactory() {
      return new JPAQueryFactory(entityManager);
   }
}

3. Controller

  
package com.yoon.api.controller;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.yoon.api.dto.CommonResponseDto;
import com.yoon.api.entity.ApiHistoryEntity;
import com.yoon.api.service.ApiHistoryService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
//import lombok.RequiredArgsConstructor;

@RestController
@Api(tags = { "1.Api_History" }) //해당 클래스가 Swagger 리소스라는 것을 명시함(swagger 세팅). 주소 확인하기 ex) https://localhost:9443/swagger-ui.html#/1.Api_History
@RequestMapping("/api/v1/history")
public class ApiHistoryController {

   private final Logger logger = LoggerFactory.getLogger(this.getClass());

   @Autowired
   private ApiHistoryService apiHistoryService;

   // Create => POST Method
   @ApiOperation(value = "History 생성", notes = "History 내역을 기록한다.")
   @PostMapping("/create")
   public ResponseEntity<CommonResponseDto> createHistory(@Valid @RequestBody ApiHistoryEntity apiHistoryParam) {

      logger.info(apiHistoryParam.toString());
      apiHistoryService.createHistory(apiHistoryParam);

      return new ResponseEntity<>(new CommonResponseDto("S00", "Create 성공했습니다."), HttpStatus.OK);
   }

   // Read => GET Method
   @ApiOperation(value = "History 조회", notes = "History 내역을 조회한다.")
   @GetMapping("/read/{historyKeyParam}")
   public ResponseEntity<CommonResponseDto> readHistory(@PathVariable String historyKeyParam) {
      logger.info(historyKeyParam);

      apiHistoryService.readHistory(historyKeyParam);
      return new ResponseEntity<>(new CommonResponseDto("S00", "Read 성공했습니다."), HttpStatus.OK);
   }

   // Update => PUT Method
   @ApiOperation(value = "History 수정", notes = "History 내역을 수정한다.")
   @PutMapping("/update") // id값이필요
   public ResponseEntity<CommonResponseDto> updateHistory(@Valid @RequestBody ApiHistoryEntity apiHistoryParam) {
      logger.info(apiHistoryParam.toString());
      apiHistoryService.updateHistory(apiHistoryParam);

      return new ResponseEntity<>(new CommonResponseDto("S00", "Update 성공했습니다."), HttpStatus.OK);
   }

   // Delete => DDELETE Method
   @ApiOperation(value = "History 삭제", notes = "History 내역을 삭제한다.")
   @DeleteMapping("/delete/{historyKeyParam}") // db에 동일한 객체가 필요
   public ResponseEntity<CommonResponseDto> deleteHistory(@PathVariable String historyKeyParam) {
      logger.info(historyKeyParam);
      apiHistoryService.deleteHistory(historyKeyParam);

      return new ResponseEntity<>(new CommonResponseDto("S00", "Delete 성공했습니다."), HttpStatus.OK);
   }

}

4. Dto

  
 package com.yoon.api.dto;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class CommonResponseDto {
  
	String resultCode;
	String resultMsg;
 
} 

5. Entity

  
package com.yoon.api.entity;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

import com.sun.istack.NotNull;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
 
@Entity
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
@Table(name = "api_history")
public class ApiHistoryEntity {

	@Id   // PK 설정
	@Column(name = "seq")
	@Min(1)
	@GeneratedValue(strategy = GenerationType.IDENTITY)   //DataBase에 키 생성방법을 위임함.
	private Long seq;

	@Column(name = "history_key")
	@NotNull
	@NotBlank
	private String historyKey;

	@Column(name = "req_date")
	private LocalDateTime reqDate;

	@Column(name = "data")
	private String data;

	@Column(name = "status")
	private String status;

	@Column(name = "expire_date")
	private LocalDateTime expireDate;

}
 

* Q Class File 생성하기

우리는 QueryDSL의 내부동작을 완벽하게 알지는 못하지만, 사용하기 전 전처리과정이 필요하다.
바로 위에서 만든 @Entity 를 읽어 필요한 Q Class File 들을 미리 만들어 두는것!
이렇게 만들어진 Q class file(이하 Q 파일)들은 쿼리를 type safe하게 짤 수 있도록 도와주기 때문이다.

[ Q Class File 만드는방법 ]

  • 1) Gradle Tasks 에서 Project 클릭 -> build 우클릭 -> Run Gradle Tasks 후 Project Refresh
  • 2) Q Class File이 생성됐는지 위치 확인하기 (src/main/generated)

6. Repository

1) Optional

  • null이 올 수 있는 값을 감싸는 Wrapper 클래스.
  • 즉, NPE(NullPointerException)로부터 자유로워지기 위해 나온 Wrapper 클래스.
  • 메소드의 반환 값이 절대 null이 아니라면 Optional을 사용하지 않는 것이 성능저하가 적다.
  • 한마디로, Optional은 method의 결과가 null이 될 수 있으며, 클라이언트가 이 상황을 처리해야 할 때 사용하는 것이 좋다.
  • 보통 JpaRepository를 상속받은 Repository는 default return type이 Optional이다.
  
package com.yoon.api.repository;

import java.util.Optional;

import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;

import com.yoon.api.entity.ApiHistoryEntity;

@Repository
public interface ApiHistoryRepository extends JpaRepository<ApiHistoryEntity, Long>, QuerydslPredicateExecutor<ApiHistoryEntity> {

	Optional<ApiHistoryEntity> findByHistoryKey(@Param("historyKey") String historyKey);

}

7. Repository Support

  • Querydsl을 이용하여 실제 조회할 쿼리를 작성하는 클래스를 작성한다.
  
package com.yoon.api.repository.support;
 
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.yoon.api.entity.ApiHistoryEntity;

import java.time.LocalDateTime; 
import javax.transaction.Transactional;

import static com.yoon.api.entity.QApiHistoryEntity.apiHistoryEntity;   //static으로 정의. Querydsl 을 사용하기위한  Q Class!! 
 
@Repository
public class ApiHistoryRepositorySupport extends QuerydslRepositorySupport {
	
	private final JPAQueryFactory jpaQueryFactory;  //QuerydslConfig 에서 @Bean으로 등록.

	public ApiHistoryRepositorySupport(JPAQueryFactory jpaQueryFactory) { 
		super(ApiHistoryEntity.class);
		this.jpaQueryFactory = jpaQueryFactory;
	}
 
	@Transactional
	public void updateByHistoryKey(String historyKey) {
		 jpaQueryFactory.update(apiHistoryEntity)
		 .where(apiHistoryEntity.historyKey.eq(historyKey))
		 .set(apiHistoryEntity.expireDate, LocalDateTime.now()).set(apiHistoryEntity.status, "Update").execute();
	}

	@Transactional
	public void deleteByHistoryKey(String historyKey) {  
		// JPADeleteClause deleteClause 
		 jpaQueryFactory.delete(apiHistoryEntity).where(apiHistoryEntity.historyKey.eq(historyKey)).execute();
	}

}  

8. Service

  
package com.yoon.api.service;

import com.yoon.api.entity.ApiHistoryEntity;

public interface ApiHistoryService {

	public boolean createHistory(ApiHistoryEntity apiHistoryParam);
	public ApiHistoryEntity readHistory(String historyKeyParam);
	public boolean updateHistory(ApiHistoryEntity apiHistoryParam);
	public boolean deleteHistory(String historyKeyParam);
	
}

9. Service Impl

  
package com.yoon.api.service.impl;

import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.yoon.api.entity.ApiHistoryEntity;
import com.yoon.api.repository.ApiHistoryRepository;
import com.yoon.api.repository.support.ApiHistoryRepositorySupport;
import com.yoon.api.service.ApiHistoryService;

@Service
public class ApiHistoryServiceImpl implements ApiHistoryService {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private ApiHistoryRepository apiHistoryRepo;

	@Autowired
	private ApiHistoryRepositorySupport apiHistoryRepoSupport;  
    
	@Override
	public boolean createHistory(ApiHistoryEntity apiHistoryParam) {
		apiHistoryRepo.save(apiHistoryParam);
		
		return true;
	}

	@Override
	public ApiHistoryEntity readHistory(String historyKeyParam) { 
		ApiHistoryEntity result = apiHistoryRepo.findByHistoryKey(historyKeyParam).orElse(new ApiHistoryEntity());
		logger.info(result.toString()); 
		return result;
	}

	@Override
	public boolean updateHistory(ApiHistoryEntity apiHistoryParam) {
		apiHistoryRepoSupport.updateByHistoryKey(apiHistoryParam.getHistoryKey());
		return true;
	}

	@Override
	public boolean deleteHistory(String historyKeyParam) {
		apiHistoryRepoSupport.deleteByHistoryKey(historyKeyParam);
		return true;
	}

}

참고

  • https://victorydntmd.tistory.com/207
  • https://noep.github.io/2017/05/03/springboot-querydsl/


SpringBootJavaREST APIJPAHibernateSwaggerEclipseLombok Share Tweet +1