Development

도서 정리 프로젝트(4)

granpa CLOCK 2022. 6. 26. 17:09

메인페이지 목록 출력

페이지에 접속 했을 때, 기본적으로 도서 목록을 표시해 주는 메인페이지.

Redux 비동기 feature 작성

Redux에서 비동기 통신을 활용할 것이다.

리덕스에 내장되어 있는 createAsyncThunk 을 사용한다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from '../api/axios'; // 커스텀 axios 호출

// createAsyncThunk('명칭/개요', 실행할 함수) 의 형태
export const fetchFrontEnd = createAsyncThunk('get/frontEnd', 
  async(_query, _page) => {
  const res = await axios.get('', {
    params: {
      query: _query,
      page: _page
    }
  })

  if (res.data) {
    return res.data.documents;
  } else {
    return console.error('오류 발생');
  }
})

// books에 값을 저장할 것이다.
const initialState = {
  books: [],
  loading: 'Loading...'
}

const bookSlice = createSlice({
  name: 'frontEnd',
  initialState,
  reducers : { },
  extraReducers: (builder) => {
    builder.addCase(fetchFrontEnd.fulfilled, (state, action) => {
      state.books.push(action.payload)
    })
  }
})
// store.js
import { configureStore } from "@reduxjs/toolkit";
import frontEndReducer from './features/frontEnd';

export const store = configureStore({
  reducer : {
    frontEnd : frontEndReducer,
  },
})

Slice 를 작성하여 활용하는 중, 불편한 상황에 직면했다.

// 호출 코드
import { useDispatch, useSelector } from 'react-redux';
import { fetchFrontEnd } from '../features/frontEnd';

const frontEnd = useSelector((store) => store.frontEnd.books);
const dispatch = useDispatch();
let pageCount = 1;

useEffect(() => {
  dispatch(fetchFrontEnd('프론트엔드', pageCount));
}, [pageCount]);

console.log(frontEnd);

books가 배열로 감싸고, books에 배열묶음이 할당되는 것

순회하기에 복잡한 접근을 하게 된다.

배열 묶음 반환

 

그래서 initialState의 books에 값을 할당하는 로직을 수정한다.

// 수정 전
const bookSlice = createSlice({
  name: 'frontEnd',
  initialState,
  reducers : { },
  extraReducers: (builder) => {
    builder.addCase(fetchFrontEnd.fulfilled, (state, action) => {
      state.books.push(action.payload)
    })
  }
})

//수정 후
const bookSlice = createSlice({
  name: 'frontEnd',
  initialState,
  reducers : { },
  extraReducers: (builder) => {
    builder.addCase(fetchFrontEnd.fulfilled, (state, action) => {
      const bookList = action.payload;
      [...bookList].map((book) => {
        state.books.push(book) // 순회하여 하나씩 books에 push
      })
    })
  }
})
  • addCase는 fetchFrontEnd가 fulfilled를 반환할 경우, state와 action을 반환
  • action은 아래와 같은 값을 지니고 있다.

console.log(action);

  • 우리가 필요한 것은 payload
  • books에 배열 묶음을 push하는 것이 아니라, 순회하며 하나씩 push 하도록 변경
  • 익숙한 배열의 형태를 받을 수 있다.

배열


컴포넌트에 적용

이제 이것을 활용하여 목록을 순회하고 화면에 출력해주기만 하면 된다.

// BookList.js  
  return (
    <section>
      <WrapBookList>
      <H2Title>도서 목록</H2Title>
        <ListCategory>
          <li><button type='button' name='frontend'>프론트엔드</button></li>
          <li><button type='button' name='webDev'>웹개발</button></li>
          <li><button type='button' name='webDesign'>웹디자인</button></li>
        </ListCategory>
        <SecFrontend>
          <H3hide>프론트엔드 도서 목록</H3hide>
          <ListBook>
            {
              frontEnd.map((book) => {
                return (
                  <Book key={book.isbn} info={book} />
                )
              })
            }
          </ListBook>
        </SecFrontend>
      </WrapBookList>
    </section>
  )

Book 컴포넌트에 각각의 props를 내려준다.

(이 부분은 전역상태로 전달/관리하는 좋은 방법이 있을까?)

// Book.js
export default function Book({info}) {
  const date = info.datetime.slice(0,10);

  return (
    <ItmeBook>
      <button>
        <WrapBook>
          <WrapDetail>
            <h4>{info.title}</h4>
            <p>
              {info.contents}
            </p>
            <small>{date}</small>
            <strong>{info.price} 원</strong>
          </WrapDetail>
          <ImgBook src={info.thumbnail} alt='' />
        </WrapBook>
      </button>
    </ItmeBook>
  )
}

받은 props를 필요한 위치에 맞게 정보를 출력해주면 끝이다.


API 연결 후 페이지

출력된 페이지

'Development' 카테고리의 다른 글

리액트 폴더 구조(Architecture)  (0) 2023.01.08
Chart.js 사용하기  (0) 2022.12.19
도서 정리 프로젝트(3)  (0) 2022.06.13
도서 정리 프로젝트(2)  (0) 2022.06.12
도서 정리 프로젝트(1)  (0) 2022.06.11