์ƒˆ์†Œ์‹

๋ฐ˜์‘ํ˜•
Spring

์Šคํ”„๋ง๋ถ€ํŠธ ํŽ˜์ด์ง•์ฒ˜๋ฆฌํ•˜๊ธฐ(JPA + RESTAPI)

  • -
๋ฐ˜์‘ํ˜•

๐ŸฅดPaging?

๊ธ€ ๋ชฉ๋ก์ด๋‚˜ ์œ ์ € ๋ชฉ๋ก ๋“ฑ์„ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด ํŽ˜์ด์ง•์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ๋‚˜๋ˆ  ํ•ด๋‹น ํŽ˜์ด์ง€์— ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ๋ณด๋‚ด๊ธฐ๋„ ํ•˜๊ณ , ์ •๋ ฌ ๊ธฐ์ค€์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์Šคํ”„๋ง์—์„œ๋Š” ๊ฐ„ํŽธํ•˜๊ฒŒ๋„ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ Page, Pageable์ด ์กด์žฌํ•œ๋‹ค. ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ๊ตฌํ˜„ ์ค‘์— ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ์ƒ๊ฒจ์„œ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์ •๋ฆฌํ•ด ๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

 

๐Ÿ›ฐ๏ธController

๋จผ์ € ์ปจํŠธ๋กค๋Ÿฌ์—์„œ์˜ Paging์ฒ˜๋ฆฌ์ด๋‹ค.

๋ณดํ†ต ์š”์ฒญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Pageable์˜ page, size, sort 3๊ฐ€์ง€์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์š”์ฒญ๋ฐ›๋Š”๋‹ค.

/api/admin/users?page=3&size=1&sort=id,DESC

→ 3๋ฒˆ์งธ ํŽ˜์ด์ง€ / ํŽ˜์ด์ง€๋‹น ์‚ฌ์ด์ฆˆ 1 / id๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ

 

@GetMapping("api/admin/users")
    public ResponseEntity<Map<String, Object>> readAllUser(Pageable pageable) {
        Page<ReadUserInfoResponseDTO> responseDTO = userService.readAllUser(pageable);
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "์ „์ฒด ์œ ์ € ์กฐํšŒ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
        result.put("data", responseDTO);
        return ResponseEntity.ok().body(result);
    }

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด @RequestBody ๋˜๋Š” @RequestParam ์—†์ด ๋‹จ๋…์œผ๋กœ Pageable๋งŒ ๋“ค์–ด๊ฐ€ ์žˆ๋Š”๋ฐ,

์ด๋Š” ์š”์ฒญํ•  ๋•Œ, PageableHandlerMethodArgumentResolver ์—์„œ Pageable ๊ฐ์ฒด๋ฅผ ํ™•์ธํ•˜๊ณ  ์ง์ ‘ ๋ณ€ํ™˜ํ•˜์—ฌ Pageable์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค์ธ PageRequest๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋„ฃ์–ด์ค€๋‹ค.(์•„๋ž˜ ์ฝ”๋“œ ํ™•์ธ)

/*
	 * (non-Javadoc)
	 * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return Pageable.class.equals(parameter.getParameterType());
	}

/*
	 * (non-Javadoc)
	 * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
	 */
	@Override
	public Pageable resolveArgument(MethodParameter methodParameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {

		String page = webRequest.getParameter(getParameterNameToUse(getPageParameterName(), methodParameter));
		String pageSize = webRequest.getParameter(getParameterNameToUse(getSizeParameterName(), methodParameter));

		Sort sort = sortResolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
		Pageable pageable = getPageable(methodParameter, page, pageSize);

		if (sort.isSorted()) {
			return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
		}

		return pageable;
	}

 

๐Ÿค–Service

public Page<ReadUserInfoResponseDTO> readAllUser(Pageable pageable) {
        Page<User> users = userRepository.findByDeleted(pageable, false);
        return users.map(ReadUserInfoResponseDTO::toDTO);
    }

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์„œ๋น„์Šค๋‹จ์—์„œ dto๋ณ€ํ™˜์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— repository์—์„œ ๋ฐ›์•„์˜จ ๋ชฉ๋ก์„ Page์˜ map๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด, dto๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

๐Ÿ“ฆRepository

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    Page<User> findByDeleted(Pageable pageable, boolean deleted);
}

Jpa์—์„œ Page๋กœ ๋ฐ›์•„์˜ค๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. ์‹ค์ œ๋กœ ์ฟผ๋ฆฌ๋กœ ๋‚ ๋ฆด ๋•Œ 2๋ฒˆ์˜ ์š”์ฒญ์ด ๋‚˜๊ฐ€๋Š”๋ฐ, ํ•œ ๋ฒˆ์€ ์ •๋ ฌ๋œ ๊ฐ์ฒด๋“ค์„ ๋ฐ›์•„์˜ค๋Š” ์š”์ฒญ, ๋‹ค๋ฅธ ํ•œ ๋ฒˆ์€ ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•œ count ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

 

spring.jpa.show-sql=true ํ–ˆ์„ ๋•Œ, ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฟผ๋ฆฌ)

Hibernate:
select
user0_.user_id as user_id1_11_,
user0_.created_date as created_2_11_,
user0_.modified_date as modified3_11_,
user0_.deleted as deleted4_11_,
user0_.email as email5_11_,
user0_.github_id as github_i6_11_,
user0_.phone_number as phone_nu7_11_,
user0_.profile_img_url as profile_8_11_,
user0_.role as role9_11_,
user0_.username as usernam10_11_
from
user user0_
where user0_.deleted=?
order by user0_.user_id desc limit ?,
?
Hibernate:
select
count(user0_.user_id) as col_0_0_
from user user0_
where user0_.deleted=?

 

๐Ÿฆฟ๊ธฐ๋ณธ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •ํ•˜๊ธฐ

์œ„์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ํŽ˜์ด์ง•์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, pageableํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„ฃ์ง€ ์•Š๋”๋ผ๋„ ๊ธฐ๋ณธ ๊ฐ’์„ ๋ณ€๊ฒฝํ•จ์œผ๋กœ์จ ๋‚˜ํƒ€๋‚ด ์ฃผ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

@GetMapping("api/admin/users")
    public ResponseEntity<Map<String, Object>> readAllUser(
					 @PageDefault(size=100, sort="id", direction = Sort.Direction.DESC) Pageable pageable) {
        Page<ReadUserInfoResponseDTO> responseDTO = userService.readAllUser(pageable);
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "์ „์ฒด ์œ ์ € ์กฐํšŒ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
        result.put("data", responseDTO);
        return ResponseEntity.ok().body(result);
    }
๋ฐ˜์‘ํ˜•
Contents

ํฌ์ŠคํŒ… ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค

์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต๊ฐ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.