AWS Back Day 82. "Spring Boot를 활용한 도서 대여 웹 애플리케이션의 좋아요 기능 구현과 렌탈 정보 불러오기, 대여 및 반납 기능 구현"

이강용·2023년 4월 27일
0

Spring Boot

목록 보기
17/20

좋아요 기능 구현

BookService ( getLikeStatus 수정)

public int getLikeStatus(int bookId,int userId) {
		Map<String, Object> map = new HashMap<>();
		map.put("bookId", bookId);
		map.put("userId", userId);
		
		return bookRepository.getLikeStatus(map);
	}

BookController (getLikeStatus 메서드 추가)

@GetMapping("/book/{bookId}/like/status")
	public ResponseEntity<?> getLikeStatus(@PathVariable int bookId, @RequestParam int userId){
		return ResponseEntity.ok().body(bookService.getLikeStatus(bookId,userId));
	}
  • Front

BookDetail.js (getLikeStatus 추가)

const getLikeStatus = useQuery(["getLikeStatus"], async() => {
        const option = {

            params: {
                userId: queryClient.getQueryData("principal").data.userId
            },

            headers:{
                Authorization: localStorage.getItem("accessToken")
            }
        }

        const response = await axios.get(`http://localhost:8080/book/${bookId}/like/status`, option);
        return response;
    });
  • Back

좋아요, 좋아요 해제 메서드
BookRepository (추가)

public int setLike(Map<String, Object> map);
public int disLike(Map<String, Object> map);

BookMapper ( insert(좋아요 추가), delete(좋아요 해제) )

<insert id="setLike">
		insert into book_like_tb
		values (0, #{bookId}, #{userId})
</insert>
	
<insert id="disLike">
		delete
		from
			book_like_tb
		where
			book_id = #{bookId}
		and user_id + #{userId}
</insert>

BookService (좋아요, 좋아요 해제 메서드 추가)

public int setLike(int bookId,int userId) {
		Map<String, Object> map = new HashMap<>();
		map.put("bookId", bookId);
		map.put("userId", userId);
		
		return bookRepository.setLike(map);
	}
	
 public int disLike(int bookId,int userId) {
		Map<String, Object> map = new HashMap<>();
		map.put("bookId", bookId);
		map.put("userId", userId);

		return bookRepository.disLike(map);
	}

BookController PostMapping(좋아요), deleteMapping(좋아요 해제) 추가

@PostMapping("/book/{bookId}/like")
	public ResponseEntity<?> setLike(@RequestBody int bookId,Map<String, Integer> requestMap){
		return ResponseEntity.ok().body(bookService.setLike(bookId, requestMap.get("userId")));
	}
	
@DeleteMapping("/book/{bookId}/like")
	public ResponseEntity<?> disLike(@RequestBody int bookId,@RequestParam int userId){
		return ResponseEntity.ok().body(bookService.disLike(bookId, userId));
	}
  • Front

BookDetail.js (좋아요, 좋아요 취소 기능 구현)

 const setLike = useMutation(async() => {

        const option = {
            headers:{
                "Content-Type": "application/json",
                Authorization: localStorage.getItem("accessToken")
            }
        }
        // axios.post (url, data, option);
        return await axios.post(`http://localhost:8080/book/${bookId}/like`, JSON.stringify({
            userId: queryClient.getQueryData("principal").data.userId
        }), option);
    },{
        onSuccess: () => { //delete cash
            queryClient.invalidateQueries(["getLikeCount"]);
            queryClient.invalidateQueries(["getLikeStatus"]);
        }
    }); //react to mutation exceptions get requset

const disLike = useMutation(async() => {

        const option = {

            params: {
                userId: queryClient.getQueryData("principal").data.userId
            },

            headers:{
                Authorization: localStorage.getItem("accessToken")
            }
        }
        // axios.post (url, data, option);
        return await axios.delete(`http://localhost:8080/book/${bookId}/like`, option);
    },{
        onSuccess: () => { //delete cash
            queryClient.invalidateQueries(["getLikeCount"]);
            queryClient.invalidateQueries(["getLikeStatus"]);
        }
    }); 




<div>
  {getLikeStatus.isLoading 
   ? "" 
   : getLikeStatus.data.data === 0 
   // Call the mutate above
   ? (<button onClick={()=>{setLike.mutate()}}>추천하기</button>) 
   : (<button onClick={()=>{disLike.mutate()}}>추천취소</button>)}
</div>

렌탈 정보 불러오기

components > UI > BookDetail > Rental
RentalList.js

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import React from 'react';


const table = css`
    border: 1px solid #dbdbdb;

`;

const thAndTd = css`
    border: 1px solid #dbdbdb;
    padding: 5px 10px;
    text-align: center;
`;

const RentalList = ( { bookId }) => {
    return (
        <>
          <table css={table}>
            <tr>
                <th css={thAndTd}>도서번호</th>
                <th css={thAndTd}>도서명</th>
                <th css={thAndTd}>상태</th>
            </tr>
          </table>  
        </>
    );
};

export default RentalList;

DB 테이블 생성
book_retal_tb 생성

BookDetail.js (컴포넌트 렌더링 추가)

<div>
  <RentalList bookId={bookId} />
</div>

  • MySQL
    Query문 작성
select
	blt.book_list_id,
    bt.book_name,
    brt.user_id
from
	book_list_tb blt
    left outer join book_tb bt on(bt.book_id = blt.book_id)
    left outer join book_rental_tb brt on(brt.book_list_id = blt.book_list_id)
  • Back

BookRepository (getRentalListByBookId 메서드 추가)

public List<RentalList> getRentalListByBookId(int bookId);

entity
RentalList (entity 생성)

package com.toyproject.bookmanagement.entity;

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

@NoArgsConstructor
@AllArgsConstructor
@Data
public class RentalList {
	private int bookListId;
	private String bookName;
	private int userId;

}

BookMapper.xml (getRentalListByBookId select문 추가)

<select id="getRentalListByBookId" parameterType="Integer" resultType="com.toyproject.bookmanagement.entity.RentalList" >
		select
			blt.book_list_id as bookListId,
		    bt.book_name as bookName,
		    brt.user_id as userId
		from
			book_list_tb blt
		    left outer join book_tb bt on(bt.book_id = blt.book_id)
		    left outer join book_rental_tb brt on(brt.book_list_id = blt.book_list_id)
	    where
	    	bt.book_id = #{bookId}
</select>

dto > book
RentalListRespDto (생성)

package com.toyproject.bookmanagement.dto.book;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class RentalListRespDto {
	private int bookListId;
	private String bookName;
	private int userId;
	private boolean rentalStatus;
}

BookService ( getRentalListByBookId 메서드 추가)

public List<RentalListRespDto> getRentalListByBookId(int bookId){
		
		List<RentalListRespDto> list = new ArrayList<>();
		bookRepository.getRentalListByBookId(bookId).forEach(rentalData -> {
			list.add(rentalData.toDto());
		});
		 return list;
}

RentalList toDto 메서드 추가

public RentalListRespDto toDto() {
		return RentalListRespDto.builder()
				.bookListId(bookListId)
				.bookName(bookName)
				.userId(userId)
				.rentalStatus(userId == 0)
				.build();
	}

BookController (getRentalListByBookId 메서드 추가)

@GetMapping("/book/{bookId}/rental/list")
	public ResponseEntity<?> getRentalListByBookId(@PathVariable int bookId){
		return ResponseEntity.ok().body(bookService.getRentalListByBookId(bookId));
}
  • Front

RentalList.js (getRentalList 변수 추가)

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import axios from 'axios';
import React from 'react';
import { useQuery } from 'react-query';


const table = css`
    border: 1px solid #dbdbdb;

`;

const thAndTd = css`
    border: 1px solid #dbdbdb;
    padding: 5px 10px;
    text-align: center;
`;

const RentalList = ( { bookId }) => {

    const getRentalList = useQuery(["getRentalList"], async()=> {
        const option = {
            headers: {
                Authorization: localStorage.getItem("accessToken")
            }
        }
        return await axios.get(`http://localhost:8080/book/${bookId}/rental/list`,option);
    });

    if(getRentalList.isLoading) {
        return <div>불러오는 중...</div>
    }


    return (
        <>
          <table css={table}>
            <thead>
                <tr>
                    <th css={thAndTd}>도서번호</th>
                    <th css={thAndTd}>도서명</th>
                    <th css={thAndTd}>상태</th>
                </tr>
            </thead>
            <tbody>
                {getRentalList.data.data.map(rentalData => {
                    // Compare key values and Rerendering
                    return (<tr key={rentalData.bookListId}> 
                        <td css={thAndTd}>{rentalData.bookListId}</td>
                        <td css={thAndTd}>{rentalData.bookName}</td>
                        {rentalData.rentalStatus 
                            ? (<td css={thAndTd}>대여가능</td>) 
                            : (<td css={thAndTd}>대여중</td>)}
                    </tr>)
                })}
            </tbody>
          </table>  
        </>
    );
};

export default RentalList;

대여, 반납 기능 구현

  • Front

RentalList.js

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import axios from 'axios';
import React from 'react';
import { useMutation, useQuery, useQueryClient } from 'react-query';


const table = css`
    border: 1px solid #dbdbdb;

`;

const thAndTd = css`
    border: 1px solid #dbdbdb;
    padding: 5px 10px;
    text-align: center;
`;

const RentalList = ( { bookId }) => {
    const queryClient = useQueryClient();
    const getRentalList = useQuery(["getRentalList"], async()=> {
        const option = {
            headers: {
                Authorization: localStorage.getItem("accessToken")
            }
        }
        return await axios.get(`http://localhost:8080/book/${bookId}/rental/list`,option);
    });
    


    const rentalBook = useMutation(async(bookListId)=> {
        const option = {
            headers: {
                "Content-Type": "application/json",
                Authorization: localStorage.getItem("accessToken")
            }
        }
        return await axios.post(`http://localhost:8080/book/rental/${bookListId}`,JSON.stringify({
            userId: queryClient.getQueryData("principal").data.userId
        }),option);
        
    },{
        onSuccess: () => {
            queryClient.invalidateQueries("getRentalList");
        }
    });

    const returnBook = useMutation(async(bookListId)=> {
        const option = {

            params: {
                userId: queryClient.getQueryData("principal").data.userId
            },
            headers: {
                
                Authorization: localStorage.getItem("accessToken")
            }
        }
        return await axios.delete(`http://localhost:8080/book/rental/${bookListId}`,option);
    },{
        onSuccess: () => {
            queryClient.invalidateQueries("getRentalList");
        }
    });



    if(getRentalList.isLoading) {
        return <div>불러오는 중...</div>
    }


    return (
        <>
          <table css={table}>
            <thead>
                <tr>
                    <th css={thAndTd}>도서번호</th>
                    <th css={thAndTd}>도서명</th>
                    <th css={thAndTd}>상태</th>
                </tr>
            </thead>
            <tbody>
                {getRentalList.data.data.map(rentalData => {
                    // Compare key values ​​and Rerendering
                    return (<tr key={rentalData.bookListId}> 
                        <td css={thAndTd}>{rentalData.bookListId}</td>
                        <td css={thAndTd}>{rentalData.bookName}</td>
                        {rentalData.rentalStatus  
                            ? (<td css={thAndTd}>대여가능 <button onClick={() => {rentalBook.mutate(rentalData.bookListId)}}>대여</button></td>) 
                            : (<td css={thAndTd}>대여중 {rentalData.userId === queryClient.getQueryData("principal").data.userId
                                ? (<button onClick={()=>{returnBook.mutate(rentalData.bookListId)}}>반납</button>): ""}</td>)}
                    </tr>)
                })}
            </tbody>
          </table>  
        </>
    );
};

export default RentalList;
  • Back

BookController

@PostMapping("/book/rental/{bookListId}")
	public ResponseEntity<?> rentalBook(@PathVariable int bookListId,@RequestBody Map<String, Integer> requestMap){
		return ResponseEntity.ok().body(bookService.rentalBook(bookListId, requestMap.get("userId")));
	}
	
@DeleteMapping("/book/rental/{bookListId}")
	public ResponseEntity<?> returnBook(@PathVariable int bookListId, int userId){
		return ResponseEntity.ok().body(bookService.returnBook(bookListId, userId));
	}

BookRepository (rental, return 추가)

public int rentalBook(Map<String, Object> map);
public int returnBook(Map<String, Object> map);

BookMapper.xml (rental, return 추가)

<insert id="rentalBook">
		insert into book_rental_tb
		values (0, #{bookListId}, #{userId})
</insert>
	
<insert id="returnBook">
		delete
		from
			book_rental_tb
		where
			book_list_id = #{bookListId}
		and user_id = #{userId}
</insert>

BookService (rental, return Book 추가)

public int rentalBook(int bookListId,int userId) {
		Map<String, Object> map = new HashMap<>();
		map.put("bookListId", bookListId);
		map.put("userId", userId);
		
		
		return bookRepository.rentalBook(map);
	}
	
	public int returnBook(int bookListId,int userId) {
		Map<String, Object> map = new HashMap<>();
		map.put("bookListId", bookListId);
		map.put("userId", userId);
		
		return bookRepository.returnBook(map);
	}

profile
HW + SW = 1

0개의 댓글