어떤 SQL을 사용하느냐에 따라 페이징 방법이 살짝 다를 수 있습니다 저도 MySQL로 만 만들어 보다 최근 PGSQL로 만들어야 했을 때 참... 난감하더군요 하지만 이 글을 보고 계신 분들을 뛰어나신 분들이기에 뚱땅 뚱땅 만드실 수 있을 거라 생각합니다.
자 그럼 select 와 페이징을 만들어보겠습니다
ApiRepository
package com.apiservice.repository;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ApiVO;
import com.apiservice.model.ListDTO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ApiRepository {
void insert(ApiDTO apiDTO);
void update(ApiDTO apiDTO);
void delete(Integer memberNumber);
List<ApiVO> selectList(@Param("skip") int skip,@Param("size") int size);
}
- 데이터를 하나가 아닌 여러개를 찾기 위해 List 형식으로 만들어 주고 @Param으로 skip와 size를 받았습니다
ApiRepository. 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.ApiRepository">
<insert id="insert">
insert into api(member_id , member_pw , member_name , member_phone , advert)
values (#{memberId} , #{memberPw} , #{memberName} , #{memberPhone} , #{advert})
</insert>
<update id="update">
update api
set member_id = #{memberId},
member_pw = #{memberPw},
member_name = #{memberName},
member_phone = #{memberPhone},
advert = #{advert},
update_date = now()
where member_number = #{memberNumber}
</update>
<delete id="delete">
delete
from api
where member_number = #{memberNumber}
</delete>
<select id="selectList" resultType="com.apiservice.model.ApiVO">
select member_number ,member_id , member_pw , member_name , member_phone , advert , update_date
from api
order by member_number desc
limit #{skip},#{size}
</select>
</mapper>
- limit로 skip size를 설정해 주고
- order by를 pk 값으로 주었습니다 그리고 등록된 최신순으로 보기 위해 desc를 춉
- insert 할 때 입력되는 reg_date 빼고 전부 찾아오는 쿼리를 작성
페이지 기능을 할 ListDTO 만들기
package com.apiservice.model;
import lombok.Getter;
import lombok.ToString;
@ToString
@Getter
public class ListDTO{
private int page;
private int size;
public ListDTO(){
this.page = 1; //page 는 기본이 1 page
this.size = 10; // size 는 기본 10개
}
public void setPage(int page){
this.page = page <= 0? 1:page;
}
public void setSize(int size){
this.size = size < 10? 10:size;
}
public int getSkip(){
return (this.page - 1) * size;
}
}
- 이 DTO를 사용해서 페이징 처리를 할 생각인데 여기서 고민해볼 건 페이징 해서 데이터를 가지고 올 때 전체 데이터 값 즉 Total 값을 가져올 것인가? 하는 고민이다
ApiService
package com.apiservice.service;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import java.util.List;
public interface ApiService {
void register(ApiDTO apiDTO);
void update(ApiDTO apiDTO);
void delete(Integer memberNumber);
List<ApiDTO> getList(ListDTO listDTO);
}
- 리스트 기능해줄 getList 만들기
ApiServiceImpl
package com.apiservice.service;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ApiVO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.repository.ApiRepository;
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 ApiServiceImpl implements ApiService {
private final ApiRepository apiRepository;
private final ModelMapper modelMapper;
@Override
public void register(ApiDTO apiDTO) {
log.info("service insert");
log.info(apiDTO);
apiRepository.insert(apiDTO);
}
@Override
public void update(ApiDTO apiDTO) {
log.info("update service");
log.info(apiDTO);
apiRepository.update(apiDTO);
}
@Override
public void delete(Integer memberNumber) {
log.info("delete service");
log.info(memberNumber);
apiRepository.delete(memberNumber);
}
@Override
public List<ApiDTO> getList(ListDTO listDTO) {
List<ApiVO> apiList = apiRepository.selectList(listDTO);
return null;
}
- getList 구현
- 이후 작업을 위해 필요한 ModelMapper 설정을 추가하러 가보자
ModelMapper Maven 추가
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.0.0</version>
</dependency>
config 패키지에 ModelMapper 생성
package com.apiservice.config;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModelMapper {
@Bean
public org.modelmapper.ModelMapper getMapper() {
org.modelmapper.ModelMapper modelMapper = new org.modelmapper.ModelMapper();
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
.setMatchingStrategy(MatchingStrategies.LOOSE);
return modelMapper;
}
}
- xml로 빈 설정할 수 도있지만 xml보다 자바로 설정하는 게 짧아서 이렇게 함
ApiServiceImpl
package com.apiservice.service;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ApiVO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.repository.ApiRepository;
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 ApiServiceImpl implements ApiService {
private final ApiRepository apiRepository;
private final ModelMapper modelMapper;
@Override
public void register(ApiDTO apiDTO) {
log.info("service insert");
log.info(apiDTO);
apiRepository.insert(apiDTO);
}
@Override
public void update(ApiDTO apiDTO) {
log.info("update service");
log.info(apiDTO);
apiRepository.update(apiDTO);
}
@Override
public void delete(Integer memberNumber) {
log.info("delete service");
log.info(memberNumber);
apiRepository.delete(memberNumber);
}
@Override
public List<ApiDTO> getList(ListDTO listDTO) {
List<ApiVO> apiList = apiRepository.selectList(**listDTO**);
//apiVO 를 ApiDTO타입의 객체로 맵핑
List<ApiDTO> dtoList =
apiList.stream().map(apiVO -> modelMapper.map(apiVO,ApiDTO.class))
.collect(Collectors.toList());
return dtoList;
}
}
- ✨파라미터를 하나하나 따로 받는 거보다 DTO타입으로(객체 타입)으로 받는 것이 더 유연하다
Controller
지금까지 만든 기능을 주입해보자
package com.apiservice.controller;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.service.ApiService;
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.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/")
@Log4j2
@RequiredArgsConstructor
public class ApiController {
private final ApiService apiService;
@ApiOperation("Api Get list")
@GetMapping("/list")
public List<ApiDTO> list(ListDTO listDTO ){
log.info("board list.........");
log.info(listDTO);
List<ApiDTO> dtoList = apiService.getList(listDTO);
return dtoList;
}
@ApiOperation("Api Post register")
@PostMapping("/register")
public ResponseEntity registerPost(ApiDTO apiDTO){
log.info(apiDTO);
log.info("controller register");
apiService.register(apiDTO);
return new ResponseEntity(HttpStatus.OK);
}
@ApiOperation("api post update")
@PostMapping("/update/{memberNumber}")
public ResponseEntity update(@PathVariable("memberNumber") Integer memberNumber , ApiDTO apiDTO){
log.info("-----------------------");
log.info("update");
log.info("------------------");
apiService.update(apiDTO);
return new ResponseEntity(HttpStatus.OK);
}
@ApiOperation("api post delete")
@PostMapping("/delete/{memberNumber}")
public ResponseEntity delete(@PathVariable("memberNumber") Integer memberNumber){
log.info("-----------------------");
log.info("delete");
log.info("------------------");
apiService.delete(memberNumber);
return new ResponseEntity(HttpStatus.OK);
}
}
- 기본적인 List 기능은 끝난 것 같으니 추가적으로 total 값을 처리해보자
ApiRepository
package com.apiservice.repository;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ApiVO;
import com.apiservice.model.ListDTO;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ApiRepository {
void insert(ApiDTO apiDTO);
void update(ApiDTO apiDTO);
void delete(Integer memberNumber);
List<ApiVO>selectList(ListDTO listDTO);
int getTotal(ListDTO listDTO);
}
ApiRepository.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.ApiRepository">
<insert id="insert">
insert into api(member_id , member_pw , member_name , member_phone , advert)
values (#{memberId} , #{memberPw} , #{memberName} , #{memberPhone} , #{advert})
</insert>
<update id="update">
update api
set member_id = #{memberId},
member_pw = #{memberPw},
member_name = #{memberName},
member_phone = #{memberPhone},
advert = #{advert},
update_date = now()
where member_number = #{memberNumber}
</update>
<delete id="delete">
delete
from api
where member_number = #{memberNumber}
</delete>
<select id="selectList" resultType="com.apiservice.model.ApiVO">
select member_number ,member_id , member_pw , member_name , member_phone , advert , update_date
from api
order by member_number desc
limit #{skip},#{size}
</select>
<select id="getTotal" resultType="int">
select count (member_number) from api
</select>
</mapper>
Generic(제네릭) 구조 사용해보기
- list를 구해올 때 total 값도 같이 구해오려고 하는데 그러면 하나의 객체에서 처리하면 안 될까? 하는 생각으로 만든 제네릭 구조
ListResponseDTO
package com.apiservice.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ListResponseDTO <E>{
private List<E> dtoList;
private int total;
//- 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.
//- 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없다. 즉, 관리하기가 편하다.
//- 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.
}
ApiService
package com.apiservice.service;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import java.util.List;
public interface ApiService {
void register(ApiDTO apiDTO);
void update(ApiDTO apiDTO);
void delete(Integer memberNumber);
ListResponseDTO<ApiDTO> getList(ListDTO listDTO);
}
- 기존 List 였던걸 ListResponseDTO로 변경
ApiService.Impl
package com.apiservice.service;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ApiVO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.repository.ApiRepository;
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 ApiServiceImpl implements ApiService {
private final ApiRepository apiRepository;
private final ModelMapper modelMapper;
@Override
public void register(ApiDTO apiDTO) {
log.info("service insert");
log.info(apiDTO);
apiRepository.insert(apiDTO);
}
@Override
public void update(ApiDTO apiDTO) {
log.info("update service");
log.info(apiDTO);
apiRepository.update(apiDTO);
}
@Override
public void delete(Integer memberNumber) {
log.info("delete service");
log.info(memberNumber);
apiRepository.delete(memberNumber);
}
@Override
public ListResponseDTO<ApiDTO> getList(ListDTO listDTO) {
List<ApiVO> apiList = apiRepository.selectList(listDTO);
//apiVO 를 ApiDTO타입의 객체로 맵핑
List<ApiDTO> dtoList =
apiList.stream().map(apiVO -> modelMapper.map(apiVO,ApiDTO.class))
.collect(Collectors.toList());
return ListResponseDTO.<ApiDTO>builder()
.dtoList(dtoList)
.total(apiRepository.getTotal(listDTO))
.build();
}
}
ApiController
package com.apiservice.controller;
import com.apiservice.model.ApiDTO;
import com.apiservice.model.ListDTO;
import com.apiservice.model.ListResponseDTO;
import com.apiservice.service.ApiService;
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.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/")
@Log4j2
@RequiredArgsConstructor
public class ApiController {
private final ApiService apiService;
@ApiOperation("Api Get list")
@GetMapping("/list")
public ListResponseDTO<ApiDTO> list(ListDTO listDTO){
log.info("Api list");
log.info(listDTO);
ListResponseDTO<ApiDTO> responseDTO = apiService.getList(listDTO);
log.info("List Controller ");
log.info(responseDTO);
return responseDTO;
}
@ApiOperation("Api Post register")
@PostMapping("/register")
public ResponseEntity registerPost(ApiDTO apiDTO){
log.info(apiDTO);
log.info("controller register");
apiService.register(apiDTO);
return new ResponseEntity(HttpStatus.OK);
}
@ApiOperation("api post update")
@PostMapping("/update/{memberNumber}")
public ResponseEntity update(@PathVariable("memberNumber") Integer memberNumber , ApiDTO apiDTO){
log.info("-----------------------");
log.info("update");
log.info("------------------");
apiService.update(apiDTO);
return new ResponseEntity(HttpStatus.OK);
}
@ApiOperation("api post delete")
@PostMapping("/delete/{memberNumber}")
public ResponseEntity delete(@PathVariable("memberNumber") Integer memberNumber){
log.info("-----------------------");
log.info("delete");
log.info("------------------");
apiService.delete(memberNumber);
return new ResponseEntity(HttpStatus.OK);
}
}
swagger-ui에서 테스트해보기
- 중간중간 기능을 만들 때마다 swagger-ui로 테스트하면서 진행했지만 분량상 마지막 테스트만 올렸습니다 이걸 만들어보시는 분들은 작은 기능 하나하나 만들 때마다 꼭 테스트하면서 돌려보세요!! 나중에 에러 나면 잡기가 어려워요 ㅠ
이렇게 기본적인 페이징 그리고 total 값을 가져오는 코드까지 완성했습니다 다음에는 기존 부실했던 코드들을 보완하는 작업을 해보려 합니다
'예제를 만들자 뚱땅뚱땅' 카테고리의 다른 글
API 만들기 연습 #6 유효성 검증 (0) | 2022.08.30 |
---|---|
API 만들기 연습 #5 CustomException (0) | 2022.08.29 |
API 만들기 연습 #3 update , delete (0) | 2022.08.25 |
API 만들기 연습 #2 등록을 해보자 (0) | 2022.08.24 |
#1 CRUD 기능을 하는 API 서버 만들어보기 (0) | 2022.08.24 |