๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
TIL

ํ”„๋กœ์ ํŠธ | ํ”„๋กœ์ ํŠธ ์ค‘ ํ•ด๊ฒฐ/๊ณ ๋ฏผํ•œ ๋ฌธ์ œ๋“ค ์ •๋ฆฌ! (feat. JPQL, Querydsl)

by bamDal 2025. 2. 20.

 

์„œ๋ก 

์Œ.. ํฌ๊ฒŒ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์ง„ ์•Š์•˜๊ณ , ์ด์ „ ํ”„๋กœ์ ํŠธ์— ๋น„ํ•ด ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ์— ๋ช‡ ์‹œ๊ฐ„ ๊ฑธ๋ฆฌ๋Š” ์ผ๋„ ์—†์–ด์„œ ๋ฌด์ฒ™ ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ„๋‹จํ•œ ๊ฒƒ๋“ค์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ๋œ ์ ๋“ค์ด ์ฐธ ๋งŽ๋‹ค........

 

 

1. ์„ฑ๋Šฅ ๊ฐœ์„  

์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘? ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์œผ๋กœ ์กฐํšŒํ•˜๋˜ ๋ถ€๋ถ„์„ JPQL๋กœ ์กฐํšŒํ•˜๋ฉด์„œ select ๋ฌธ์ด 2๊ฐœ์—์„œ 1๊ฐœ๋กœ ์ค„์—ˆ๋‹ค.
์™ ์ง€ ์„ฑ๋Šฅ ๊ฐœ์„ ์€ ์—„์ฒญ๋‚œ ์ฐจ์ด๋กœ ๊ฐœ์„ ํ•ด์•ผํ•˜๋Š” ๋Š๋‚Œ์ธ ๋“ฏํ•˜์—ฌ..     ์ด๋Ÿฐ ๊ฑธ ์„ฑ๋Šฅ ๊ฐœ์„ ์ด๋ผ๊ณ  ํ‘œํ˜„ํ•ด๋„ ๋ ์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ..? 

— ์ดˆ๊ธฐ ์ฝ”๋“œ

// Service
UUID ownerUser = menu.getRestaurant().getOwner().getUserId();

 

์‹คํ–‰๋˜๋Š” select ๋ฌธ๋“ค

Hibernate: 
    select
        m1_0.rm_id,
        m1_0.created_at,
        m1_0.created_by,
        m1_0.deleted_at,
        m1_0.deleted_by,
        m1_0.is_public,
        m1_0.modified_at,
        m1_0.modified_by,
        m1_0.res_id,
        m1_0.rm_description,
        m1_0.rm_image_url,
        m1_0.rm_name,
        m1_0.rm_price 
    from
        p_restaurant_menu m1_0 
    where
        m1_0.rm_id=?
Hibernate: 
    select
        r1_0.res_id,
        r1_0.created_at,
        r1_0.created_by,
        r1_0.deleted_at,
        r1_0.deleted_by,
        r1_0.modified_at,
        r1_0.modified_by,
        r1_0.res_owner_id,
        r1_0.res_address,
        r1_0.res_image_url,
        r1_0.res_name,
        r1_0.res_phone 
    from
        p_restaurant r1_0 
    where
        r1_0.res_id=?

 

 

— ์ˆ˜์ • ํ›„

// Service
UUID ownerUser = menuRepository.getRestaurantOwnerId(menu.getRmId());

// Repository
@Query("Select u.userId from User u join Restaurant r on r.owner.userId = u.userId join Menu m on m.restaurant.resId = r.resId where m.rmId = :rmId ")
UUID getRestaurantOwnerId(@Param("rmId") UUID rmId);

 

์‹คํ–‰๋˜๋Š” select ๋ฌธ

Hibernate: 
    select
        u1_0.user_id 
    from
        p_users u1_0 
    join
        p_restaurant r1_0 
            on r1_0.res_owner_id=u1_0.user_id 
    join
        p_restaurant_menu m1_0 
            on m1_0.res_id=r1_0.res_id 
    where
        m1_0.rm_id=?

 

โš ๏ธ ๊ธฐ์กด ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ 

๊ฐ€๋…์„ฑ์ด ์ข‹์ง€ ์•Š๊ณ ,

์—ฐ๊ด€๋œ ํ…Œ์ด๋ธ”์„ ํƒ€๊ณ , ํƒ€๊ณ  ๊ฐ€์„œ userId๋ฅผ ์กฐํšŒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์—ˆ๋‹ค.
select๋ฌธ์ด 2๋ฒˆ๋งŒ ๋‚˜๊ฐ„ ๊ฒƒ ๋ณด๋ฉด ๋‹คํ–‰ํžˆ ๋ฐœ์ƒํ•˜์ง„ ์•Š์€ ๋“ฏ ํ•˜์ง€๋งŒ!

 

์ข‹์€ ์‹œ๋„์˜€๋‹ค.

 


 

2. Querydsl BooleanExpression 

JPA Querydsl์€ ํ”„๋กœ์ ํŠธํ•˜๋ฉด์„œ ์ฃผ๋จน๊ตฌ๊ตฌ์‹์œผ๋กœ ๊ฒฝํ—˜ํ•ด๋ณธ ๊ฑฐ๋ผ ๋†“์น˜๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์ด ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค.

	@Override
	public List<Menu> findMenusByResIdAndRole(UUID resId, UserDetailsImpl userDetails) {
		QMenu menu = QMenu.menu;
		UserRoleEnum role = userDetails.getRole();
		UUID userId = userDetails.getUserId();
		boolean isOwner = isOwner(userId, resId);

		BooleanExpression condition = menu.restaurant.resId.eq(resId);
		// role๊ณผ OWNER id ์ผ์น˜ ์—ฌ๋ถ€์— ๋”ฐ๋ฅธ condition ์ถ”๊ฐ€
		if (role.equals(UserRoleEnum.CUSTOMER) || (role.equals(UserRoleEnum.OWNER) && !isOwner)) {
			log.info("role : {}", role);
			// condition.and(menu.isPublic.eq(true)); - ํ‹€๋ฆฐ ์ฝ”๋“œ
			condition = condition.and(menu.isPublic.eq(true));
		}

		return jpaQueryFactory.selectFrom(menu).where(condition).fetch();
	}

BooleanExpression์„ ๋ฐ˜ํ™˜ํ•˜๋Š” condition ์—๋‹ค๊ฐ€ ์กฐ๊ฑด์— ๋”ฐ๋ผ์„œ condition์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜๋‹ค.

BoleanExpression์€ ๋ถˆ๋ณ€๊ฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กญ๊ฒŒ ์ €์žฅํ•ด์ค˜์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ 

condition.and(menu.isPublic.eq(true)); ๊ฐ€ ์•„๋‹Œ

condition = condition.and(menu.isPublic.eq(true)); ๋กœ ์ ์–ด์„œ ๊ผญ ๊ฐ’์„ ๋‹ค์‹œ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค!!!!โญโญโญโญโญโญโญโญโญ

 

 


 

3. HTTP ์‘๋‹ต๊ฐ’์— ๋Œ€ํ•œ ๊ณ ๋ฏผ 

PATCH ๋ฉ”์„œ๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” api์™€ ์‚ญ์ œํ•˜๋Š” api ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ ๋ฐ˜ํ™˜๊ฐ’์„ ์–ด๋–ป๊ฒŒ ๋ณด๋‚ด์•ผํ• ์ง€ ์กฐ๊ธˆ ๊ณ ๋ฏผํ–ˆ๋‹ค.

- ์ˆ˜์ •์˜ ๊ฒฝ์šฐ, ์ˆ˜์ •ํ•œ ๊ฐ’์„ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์•„์„œ 200 OK๋กœ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ์‘๋‹ต์— ๋ณด๋ƒˆ๋‹ค.

- ์‚ญ์ œ์˜ ๊ฒฝ์šฐ, ์‚ญ์ œํ•œ ๊ฐ’์ด ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜๋  ํ•„์š”๊ฐ€ ์—†์œผ๋‹ˆ๊นŒ 204 NO CONTENT๊ฐ€ ๋งž๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ, ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ ์‘๋‹ต ๊ฐ’์„ ๋ณด๋‚ผ ๋•Œ CommonResponse ์— ๋‹ด์•„์„œ ๋ณด๋‚ด๊ณ , ์ด๊ณณ์—๋Š” SUCCESS CODE์™€ result๊ฐ€ ๋‹ด๊ธฐ๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์—๋„ CommonResponse์— ๋‹ด์•„์„œ ๋ณด๋‚ด๋Š” ๊ฒŒ ๋งž๋Š”์ง€..? ๊ทธ๋ƒฅ nocontent().build()๋กœ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉด ๋˜๋Š”์ง€ ๊ณ ๋ฏผํ–ˆ๋‹ค. ์ ํ•ฉํ•œ ๋‹ต์ด ๋ญ”์ง€๋Š” ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค.. ๊ทธ๋ƒฅ ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ–ˆ์„ ๋ฟ! nocontent().build()๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

 

 

 

 

< 200๋ฒˆ๋Œ€ HTTP ์ƒํƒœ ์ฝ”๋“œ >

  • 200 OK
    • ์š”์ฒญ ์„ฑ๊ณต, ์š”์ฒญ์ด ์ •์ƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋จ
  • 201 Created
    • ์š”์ฒญ ์„ฑ๊ณต, ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค๊ฐ€ ์ƒ์„ฑ๋จ
  • 202 Accepted
    • ์š”์ฒญ ์ˆ˜๋ฝ, ์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜๋ฝ๋˜์—ˆ์ง€๋งŒ, ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์Œ (๋น„๋™๊ธฐ์  ์ฒ˜๋ฆฌ)
  • 204 No Content
    • ์š”์ฒญ ์„ฑ๊ณต, ๋ฐ˜ํ™˜ํ•  ๋‚ด์šฉ์ด ์—†์Œ (์ฃผ๋กœ DELETE ์š”์ฒญ์— ์‘๋‹ต)

 

 

Exception ๋˜ํ•œ ๋ฌด์‹ ๊ฒฝํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋‹ค๊ฐ€ ์ˆ˜์ •ํ–ˆ๋‹ค.

< Exception HTTP ์ƒํƒœ ์ฝ”๋“œ >

  • 401 UNAUTHORIZED 
    • ์ธ์ฆ์ด ํ•„์š”ํ•œ ์š”์ฒญ์ด๊ฑฐ๋‚˜ ์ธ์ฆ์— ์‹คํŒจํ•จ
  • 403 FORBIDDEN 
    • ๋ฆฌ์†Œ์Šค๋Š” ์กด์žฌํ•˜์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์Œ
  • 404 NOT_FOUND 
    • ์š”์ฒญํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ

 

๋Š๋‚€ ์ 

์˜ˆ์ „์—๋Š” ๊ทธ๋ƒฅ OK๋กœ ๋ชจ๋‘ ๋ณด๋‚ด๋ฒ„๋ ธ๊ณ , ๊ฐ๊ฐ์˜ ์ฐจ์ด๋„ ์ž˜ ์•Œ์ง€ ๋ชปํ–ˆ๋Š”๋ฐ (์•Œ์•„๋ณผ ์ƒ๊ฐ๋„ ์•ˆํ•˜๊ณ  ๊ทธ๋ƒฅ ๊ธฐ๋Šฅ ๊ตฌํ˜„์—๋งŒ ์ดˆ์ ์„ ๋‘์—ˆ๋˜ ์˜›๋‚ ..ใ… ) ์ง„์ž‘ ์ข€ ์ฐพ์•„๋ณผ ๊ฑธ ๊ทธ๋žฌ๋‹ค ใ…Žใ…Ž..............