예제를 만들자 뚱땅뚱땅

API 만들기 연습 #11

NEWDODORIPYO 2022. 9. 6. 09:30

조회 & 검색 그리고 유효성 검사

DeviceRepository

package com.apiservice.repository;

import com.apiservice.model.DeviceDTO;
import com.apiservice.model.DeviceVO;
import com.apiservice.model.ListDTO;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface DeviceRepository {

    void deviceInsert(DeviceVO deviceVO);

    void deviceDelete(Integer serviceNumber);

    DeviceVO getPk(DeviceDTO deviceDTO); // device 테이블의 pk 와 member_number을 검사

    List<DeviceVO> selectList(ListDTO listDTO);

    int getTotal(ListDTO listDTO);

}
  • 조회 기능을 하는 selectList
  • Total 값을 가져오는 getTotal

DeviceRepository.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.apiservice.repository.DeviceRepository">


    <sql id="search">
        <where>
            <if test="keyword != null">
                <foreach collection="types" item="item" separator="OR" open="(" close=")">
                    <if test = 'item == "n"'>
                        device_name like concat('%',#{keyword} , '%')
                    </if>
                    <if test='item =="t"'>
                        device_type like concat('%' , #{keyword} , '%')
                    </if>
                    <if test='item =="p"'>
                        member_number like concat('%' , #{keyword} , '%')
                    </if>
                    <if test='item =="r"'>
                        region like concat('%' , #{keyword} , '%')
                    </if>
                </foreach>
            </if>
        </where>

    </sql>

    <insert id="deviceInsert">
        insert into api.device( device_name, device_type, installer, in_date, region, member_number)
        value(#{deviceName},#{deviceType},#{installer},now(),#{region},#{memberNumber})
    </insert>

    <delete id="deviceDelete">
        delete
        from api.device
        where service_number = #{serviceNumber}
    </delete>


    <select id="getPk" resultType="com.apiservice.model.DeviceVO">
        select service_number , member_number  from api.device
        where service_number = #{serviceNumber}
    </select>

    <select id="selectList" resultType="com.apiservice.model.DeviceVO">
        select service_number, device_name, device_type, installer, in_date, region, member_number
        from api.device
        <include refid="search"></include>
        order by service_number desc
        limit #{skip},#{size}
    </select>

    <select id="getTotal" resultType="int">
        select count (service_number) from api.device
        <include refid="search"></include>
    </select>
</mapper>
  • 검색 기능과 조회 쿼리는 기존 코드와 동일합니다

DeviceService

package com.apiservice.service;

import com.apiservice.model.*;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public interface DeviceService {

    void register(DeviceDTO deviceDTO , ApiDTO apiDTO);

    void delete(Integer serviceNumber , DeviceDTO deviceDTO);

    ListResponseDTO<DeviceDTO> getList(ListDTO listDTO);
}

DeviceServiceImpl

package com.apiservice.service;

import com.apiservice.controller.Handler.CustomException;
import com.apiservice.model.*;
import com.apiservice.model.Error.ErrorCode;
import com.apiservice.repository.ApiRepository;
import com.apiservice.repository.DeviceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@Log4j2
@RequiredArgsConstructor
public class DeviceServiceImpl implements DeviceService{

    private final ApiRepository apiRepository;
    private final DeviceRepository deviceRepository;
    private final ModelMapper modelMapper;

    @Override
    public void register(DeviceDTO deviceDTO , ApiDTO apiDTO) {

        DeviceVO deviceVO = modelMapper.map(deviceDTO, DeviceVO.class);

        deviceRepository.deviceInsert(deviceVO);

        //api<고객> 테이블 에 존재하는 PK 인지 확인
        if(apiRepository.getPk(apiDTO) ==null){
            log.info("ErrorCode NOT_INFO");
            throw new CustomException(ErrorCode.NOT_INFO);
        }

        apiRepository.updateDevicesCount(deviceDTO.getMemberNumber(),1);

    }

    @Override
    public void delete(Integer serviceNumber , DeviceDTO deviceDTO) {

        //유효성 검사와 member_number을 매칭하기 위한 select
        DeviceVO getPk = deviceRepository.getPk(deviceDTO);

        //존재 안 하는 장치는 삭제할 수 없다
        if(getPk == null){
            throw new CustomException(ErrorCode.NOT_INFO);
        }

        log.info("deviceDelete");
        deviceRepository.deviceDelete(serviceNumber);

        apiRepository.deleteDevicesCount(getPk.getMemberNumber());

    }

    @Override
    public ListResponseDTO<DeviceDTO> getList(ListDTO listDTO) {

        List<DeviceVO> apiList = deviceRepository.selectList(listDTO);

        //deviceVO 를 DeviceDTO 타입의 객체로 맵핑
        List<DeviceDTO> dtoList =
                apiList.stream().map(deviceVO -> modelMapper.map(deviceVO,DeviceDTO.class))
                        .collect(Collectors.toList());

        return ListResponseDTO.<DeviceDTO>builder()
                .dtoList(dtoList)
                .total(deviceRepository.getTotal(listDTO))
                .build();
    }
}

이 부분에서 중요한 건 2가지가 있습니다

우선 첫번째는 당연히 조회하는 쪽 로직 이 로직은 전에 만들었던 로직과 똑같습니다 중요하다기보다는 목표인 코드이죠 그럼 다른 한 가지는 무엇이냐?

두 번째 바로 insert 쪽 유효성 검사입니다

장치를 등록할 때 존재 안 하는 고객에게는 장치를 등록할 수 없습니다 그렇기 때문에 먼저 고객이 존재하는지부터 알아야 합니다 그래서

//api<고객> 테이블 에 존재하는 PK 인지 확인
if(apiRepository.getPk(apiDTO) ==null){
log.info("ErrorCode NOT_INFO");
    throw new CustomException(ErrorCode.NOT_INFO);
}

이렇게 확인해주고 없다면 예외를 던지고 있습니다 이 코드를 위해 register에 매개변수로 ApiDTO를 받은 것이죠 apiDTO가 없다면 위 코드를 돌릴 수 없기 때문이죠 하지만 Controller에서는 매개변수로 받으면 어지러워진다….

DeviceController

package com.apiservice.controller;

import com.apiservice.model.ApiDTO;
import com.apiservice.model.DeviceDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.service.DeviceService;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/device/")
@Log4j2
@RequiredArgsConstructor
public class DeviceController {

    private final DeviceService deviceService;

    @ApiOperation("device Get list")
    @GetMapping("/list")
    public ListResponseDTO<DeviceDTO> list(ListDTO listDTO){

        log.info("Api list");

        log.info(listDTO);

        ListResponseDTO<DeviceDTO> responseDTO = deviceService.getList(listDTO);

        log.info("List Controller ");
        log.info(responseDTO);

        return responseDTO;

    }

    @ApiOperation("Device Post register")
    @PostMapping("/register")
    public ResponseEntity registerPost(DeviceDTO deviceDTO){
        log.info(deviceDTO);
        log.info("controller register");

        deviceService.register(deviceDTO , ApiDTO.builder()
                        .memberNumber(deviceDTO.getMemberNumber())
                        .build());

        return new ResponseEntity(HttpStatus.OK);
    }

    @ApiOperation("Device post delete")
    @PostMapping("/delete/{serviceNumber}")
    public ResponseEntity delete(@PathVariable("serviceNumber") Integer serviceNumber , DeviceDTO deviceDTO){
        log.info("-----------------------");
        log.info("delete");
        log.info("------------------");

        deviceService.delete(serviceNumber , deviceDTO);

        return new ResponseEntity(HttpStatus.OK);
    }
}
  • register에서는 매개변수로는 ApiDTO를 받게 되면 입력창에 api의 목록까지 전부 보인다 그렇기 매개변수로는 daviceDTO 만 받아주면 됩니다
  • service 코드를 실행할 때 member 받아줍니다

조회 같은 경우는 기존 코드와 거의 동일하기에 테스트 과정은 건너뛰고 유효성 검사를 위해 이동해보죠 <물론 만들 때는 테스트를 계속 진행하면서 했습니다😉>

DeviceDTO

package com.apiservice.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotEmpty;
import java.time.LocalDateTime;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DeviceDTO {

    private Integer serviceNumber; // 서비스 번호

    @NotEmpty(message = "장치 넘버를 입력해 주세요")
    private String deviceName;  // 장치 이름
		
		@Size(max=31)
    @NotEmpty(message = "장치 종류를 입력해 주세요")
    private String deviceType;  // 장치 종류

    @NotEmpty(message = "설치 기사 이름을 입력해 주세요")
    private String installer;   // 설치 기사

    @NotEmpty(message = "지역을 입력해 주세요")
    private String region; // 지역

    private LocalDateTime inDate; // 설치 날짜

    private Integer memberNumber; // 고객 번호

}
  • memberNumber에 조건을 안 걸어준 이유는 서비스단에서 이미 검사를 하기 때문에 굳이 걸지 않았습니다
  • deviceType는 varchar(60) 이기에 문자열의 max 크기를 정해주었습니다

DeviceController

package com.apiservice.controller;

import com.apiservice.model.ApiDTO;
import com.apiservice.model.DeviceDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.service.DeviceService;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/device/")
@Log4j2
@RequiredArgsConstructor
public class DeviceController {

    private final DeviceService deviceService;

    @ApiOperation("device Get list")
    @GetMapping("/list")
    public ListResponseDTO<DeviceDTO> list(ListDTO listDTO){

        log.info("Api list");

        log.info(listDTO);

        ListResponseDTO<DeviceDTO> responseDTO = deviceService.getList(listDTO);

        log.info("List Controller ");
        log.info(responseDTO);

        return responseDTO;

    }

    @ApiOperation("Device Post register")
    @PostMapping("/register")
    public ResponseEntity registerPost(@Validated DeviceDTO deviceDTO){
        log.info(deviceDTO);
        log.info("controller register");

        deviceService.register(deviceDTO , ApiDTO.builder()
                        .memberNumber(deviceDTO.getMemberNumber())
                        .build());

        return new ResponseEntity(HttpStatus.OK);
    }

    @ApiOperation("Device post delete")
    @PostMapping("/delete/{serviceNumber}")
    public ResponseEntity delete(@PathVariable("serviceNumber") Integer serviceNumber , DeviceDTO deviceDTO){
        log.info("-----------------------");
        log.info("delete");
        log.info("------------------");

        deviceService.delete(serviceNumber , deviceDTO);

        return new ResponseEntity(HttpStatus.OK);
    }
}
  • @Validated 걸어줍니다

여기까지 2차로 설계했던 부분의 코드가 끝났습니다 만들면서 생각지도 못한 문제들이 생겨 고생을 좀 했지만 그래도 어떻게든 만들었네요

기초 설계에서부터 조금 아쉬운 부분들이 있지만 다음에 만들 때는 이런 부분들을 보완해서 만들면 되겠죠?

'예제를 만들자 뚱땅뚱땅' 카테고리의 다른 글

API 만들기 연습 #10  (0) 2022.09.05
API 만들기 연습 #9  (0) 2022.09.02
API 만들기 연습 #8 추가 설계  (0) 2022.09.01
API 만들기 연습 #7 검색  (0) 2022.08.31
API 만들기 연습 #6 유효성 검증  (0) 2022.08.30