▶ 초기 설정 및 Redux
부분을 타입스크립트로 변경 시 자바스크립트와 차이점이 무엇인지 작성하기 위함
▶ typescript
로 바꿀 때 일부만 추가될 뿐 엄청나게 차이나지 않음
▶ next
로 작성되므로 _app.tsx
에서 redux
에서 가져온 wrapper
로 감싸짐
▶ 결과는 이전 포스팅에서 확인 가능함
▶ []
로 닫힌 부분 : 폴더
▶ .tsx
나 .ts
로 쓰여진 부분 : 파일
[compontents]
ㄴ[post]
ㄴQuotes.tsx
[pages]
ㄴ[page]
ㄴindex.js //화면
[redux]
ㄴ[feature] //toolkit부분
ㄴindex.ts //rootReducer 존재
ㄴquoteSlice.ts
ㄴ[sagas]
ㄴrootSaga.ts //rootSaga 존재
ㄴquoteSaga.ts
ㄴstore.ts //RootSate 존재
type.ts //타입지정
◆ 주로 타입 지정과 관련됨
▶ type.ts
: 타입 지정
▶ store.ts
: 코드 추가됨(store쪽과 interface)
▶ quoteSaga.ts
, quoteSlice.ts
▶ Quotes.tsx
export interface Quote {
name: string,
content: string
}
export interface quoteProps {
quoteResult: null | Quote,
showQuoteLoading: boolean,
showQuoteComplete: boolean,
showQuoteError: any,
}
export interface quoteResponse {
data: string;
}
▶ interface RootState 추가됨
import { configureStore } from "@reduxjs/toolkit";
import createSagaMiddleware from "redux-saga";
import { createWrapper } from "next-redux-wrapper";
import rootReducer from "./feature";
import rootSaga from "./sagas/rootSaga";
import { quoteProps } from "../type";
const isDev = process.env.NODE_ENV === "development";
const createStore = () => {
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const store = configureStore({
reducer: rootReducer,
middleware: middlewares,
devTools: isDev,
});
(store as any).sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
const wrapper = createWrapper(createStore, {
debug: isDev,
});
//타입스크립트에서 추가되는 부분
export interface RootState {
quote: quoteProps;
}
export default wrapper;
▶ 자바스크립트 파일과 동일함
import { combineReducers } from "redux";
import { HYDRATE } from "next-redux-wrapper";
import quote from "./quoteSlice";
const rootReducer = (state, action) => {
switch (action.type) {
case HYDRATE:
// console.log("HYDRATE", action);
return action.payload;
default: {
const combinedReducer = combineReducers({
quote,
});
return combinedReducer(state, action);
}
}
};
export default rootReducer;
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { quoteProps } from "../../type";
const initialState:quoteProps = {
quoteResult: null,
showQuoteLoading: false,
showQuoteComplete: false,
showQuoteError: null,
};
export const quoteSlice = createSlice({
name: "quote",
initialState,
reducers: {
showQuoteRequest: (state: quoteProps, action:PayloadAction<null>) => {
state.showQuoteLoading = true;
state.showQuoteError = null;
state.showQuoteComplete = false;
},
showQuoteSuccess: (state: quoteProps, action:PayloadAction<null>) => {
const data = action.payload;
state.showQuoteLoading = false;
state.showQuoteComplete = true;
state.quoteResult = data;
},
showQuoteError: (state, action) => {
state.showQuoteLoading = true;
state.showQuoteError = action.payload.error;
},
}
});
export const { showQuoteRequest, showQuoteSuccess, showQuoteError } =
quoteSlice.actions;
export default quoteSlice.reducer;
▶ 자바스크립트 파일과 동일함
import { all } from "redux-saga/effects";
import { quoteSaga } from "./quoteSaga";
import axios from "axios";
axios.defaults.baseURL = "http://localhost:3005";
axios.defaults.withCredentials = true;
export default function* rootSaga() {
yield all([
...quoteSaga,
]);
}
▶ quoteResponse를 불러와서 적용함
import axios from "axios";
import { takeLatest, put, fork, call } from "redux-saga/effects";
import { quoteResponse } from "../../type";
import {
showQuoteRequest,
showQuoteSuccess,
showQuoteError,
} from "../feature/quoteSlice";
function showQuoteAPI() {
return axios.get<quoteResponse>("/quote");
}
function* showQuote() {
try {
const response = yield call(showQuoteAPI);
yield put(showQuoteSuccess(response.data));
} catch (error) {
if (axios.isAxiosError<quoteResponse>(error)) {
yield put(showQuoteError(error.response));
console.log(error);
}
}
}
function* show_Quote_Req() {
yield takeLatest(showQuoteRequest.type, showQuote);
}
export const quoteSaga = [fork(show_Quote_Req)];
import React from "react";
import Head from "next/head";
import "../styles/globals.css"; //tailwindcss 적용
import wrapper from "../redux/store"; //redux 적용
import { ThemeProvider } from "next-themes"; //darkmode적용
const App = ({ Component }) => {
return (
<>
<Head>
<title>engWord</title>
</Head>
<ThemeProvider attribute="class">
<Component />
</ThemeProvider>
</>
);
};
export default wrapper.withRedux(App);
▶ 다른 컴포넌트도 존재하는데 아직 수정하지 않아서 충돌 생김
▶ 자바스크립트나 타입스크립트나 크게 차이 존재하지 않음
import axios from "axios";
import Head from "next/head";
import dynamic from "next/dynamic";
import wrapper from "../../redux/store";
const Qutoes = dynamic(import("../../components/post/Quotes"));
return (
<>
<Qutoes />
</>
);
};
export const getServerSideProps = wrapper.getServerSideProps(
async (context) => {
const cookie = context.req ? context.req.headers.cookie : "";
axios.defaults.headers.Cookie = "";
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
//context.store.dispatch(loadMyInfoRequest());
//context.store.dispatch(loadPostsRequest());
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
}
);
export default Index;
▶ useSelector
사용
▶ 핵심은 useSelector
사용 전RootState
으로 타입을 표기해야 함
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { showQuoteRequest } from "../../redux/feature/quoteSlice";
import { RootState } from "../../redux/store";
const Quotes = () => {
const dispatch = useDispatch();
const { quoteResult } = useSelector((state: RootState) => state.quote);
useEffect(() => {
dispatch(showQuoteRequest());
}, []);
return (
<div>
<div>
<h2>추천 영어 문장</h2>
</div>
<div>
<div>
<p>
{quoteResult?.content === undefined ? "영어 인용문 찾아 오는 중" : quoteResult?.content}
</p>
<p>{quoteResult?.name === undefined ? null : `-${quoteResult?.name}-`}</p>
</div>
</div>
</div>
)
};
export default Quotes;