Generic type 을 이용한 RestController 코드 중복제거

By | 2020년 12월 27일
Table of Content

Generic type 을 이용한 RestController 코드 중복제거

RestController 를 작성하다 보면 95% 의 코드가 반복되는 것을 느끼게 된다.

Generic type 을 이용해 코드중복을 제거해 보자.

BaseRestController

/**
 *
 * CustomGenericService - 서비스의 공통 기능 추상화(CRUD)
 * 재정의가 필요한 기능은 @Override해서 사용할것
 *
 * @author skyer9@gmail.com
 *
 * @param <D> DTO Type
 * @param <E> Entity Type
 * @param <KD> Key DTO Type
 * @param <KE> Key Entity key type
 */

public abstract class CustomGenericService<D, E, KD, KE> {

    protected JpaRepository<E, KE> repository;
    private final String title;

    public CustomGenericService(JpaRepository<E, KE> repository, String title) {

        this.repository = repository;
        this.title = title;
    }

    // 생성
    @Transactional
    public D create(D d) {

        return toDto(repository.save(toEntity(d)));
    }

    // 체크 및 생성
    @Transactional
    public D create(KD id, D d) throws DataExistsException {

        if (get(toKeyEntity(id), false) != null) {
            throw new DataExistsException(title + "이(가) 이미 존재합니다.");
        }

        return toDto(repository.save(toEntity(d)));
    }

    // 수정
    @Transactional
    public abstract void update(KD id, D d);

    // DTO 조회
    public D select(KD id) {

        return toDto(get(toKeyEntity(id), true));
    }

    // Entity 반환
    @Transactional
    public E get(KE id, boolean throwExceptionIfNotExists) {

        E e = repository.findById(id).orElse(null);

        if (throwExceptionIfNotExists && (e == null)) {
            throw new DataNotFoundException(title + "이(가) 존재하지 않습니다.");
        } else {
            return e;
        }
    }

    public abstract SearchResponseDto search(Map<String, String> params);

    protected List<D> toDto(List<E> lst) {

        return lst.stream().map(this::toDto).collect(Collectors.toList());
    }

    public abstract D toDto(E e);
    public abstract E toEntity(D e);
    // public abstract KD toKeyDto(KE e);
    public abstract KE toKeyEntity(KD e);
}
/**
 *
 * CustomGenericService - 서비스의 공통 기능 추상화(CRUD)
 * 재정의가 필요한 기능은 @Override해서 사용할것
 *
 * @author skyer9@gmail.com
 *
 * @param <D> DTO Type
 * @param <E> Entity Type
 * @param <KD> Key DTO Type
 * @param <KE> Key Entity key type
 * @param <S> Service Type
 */

public class BaseRestController<D, E, KD, KE, S extends CustomGenericService<D, E, KD, KE>> {

    private final S service;

    public BaseRestController(S service) {
        this.service = service;
    }

    // PK 자동생성
    public ResponseEntity<?> create(D dto) {

        D created = service.create(dto);

        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, created));
    }

    // PK 수동부여(PK 체크)
    public ResponseEntity<?> create(KD kd, D dto) {

        D created = service.create(kd, dto);

        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, created));
    }

    public ResponseEntity<?> update(KD keyDto, D dto) {

        service.update(keyDto, dto);
        return ResponseEntity.ok(new ApiResponseMessage(ResponseCode.OK));
    }

    public ResponseEntity<?> select(KD keyDto) {

        D dto = service.select(keyDto);

        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, dto));
    }

    public ResponseEntity<?> search(Map<String, String> params) {

        SearchResponseDto searchResponseDto = service.search(params);

        return ResponseEntity.ok(
                new ApiResponseWithPaging(ResponseCode.OK,
                        searchResponseDto.getResults(),
                        searchResponseDto.getPageable(),
                        searchResponseDto.getTotalCount()
                )
        );
    }
}

ItemsApiController

@Api(tags = { "11. 상품 리스트" })
@RestController
@RequestMapping("/v1/items")
public class ItemsApiController extends BaseRestController<ItemsDto, Items, String, String, ItemsService> {

    public ItemsApiController(ItemsService service) {
        super(service);
    }

    @Override
    @ApiOperation(value = "상품 등록", notes = "신규 상품을 등록등록합니다.")
    @PostMapping("/")
    public ResponseEntity<?> create(@RequestBody ItemsDto dto) {
        return super.create(dto);
    }

    @Override
    @ApiOperation(value = "상품 수정", notes = "상품을 수정합니다.")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "skuCd", value = "상품코드", required = true, dataType = "int", paramType = "path", defaultValue = ""),
    })
    @PutMapping("/{skuCd}")
    public ResponseEntity<?> update(@PathVariable String skuCd, @RequestBody ItemsDto dto) {
        return super.update(skuCd, dto);
    }

    @Override
    @ApiOperation(value = "상품 조회", notes = "상품을 조회합니다.")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "skuCd", value = "상품코드", required = true, dataType = "string", paramType = "path", defaultValue = ""),
    })
    @GetMapping("/{skuCd}")
    public ResponseEntity<?> select(@PathVariable String skuCd) {
        return super.select(skuCd);
    }

    @Override
    @ApiOperation(value = "상품 검색", notes = "상품을 검색합니다.")
    @GetMapping("/")
    public ResponseEntity<?> search(@RequestParam Map<String, String> params) {
        return super.search(params);
    }
}

중복된 코드가 상당량 제거된다.

답글 남기기