이 글은 나의 토이프로젝트 제작기를 정리하는 글이다.
왜 만들때 안하고 이제 하냐면 이런 스토리가 있다...
처음에 CSR을 써보려고 시작했던 프로젝트였는데
만들고보니 굳이 CSR을 안써도 되는 컨셉의 프로젝트였던 것이다.
그래서 SPA 기반의 웹으로 다시 전환하면서 정리해보려고 한다.
*학습내용보단 제작기록을 주로 정리해두려고 한다.
<사이트 개요>
사이트의 내용은 간단하다.
- NASA의 APOD(Astronomy Picture Of the Day) OpenAPI를 사용하여 매일매일 새로운 우주(천문학) 사진을 보여준다.
- 이전 일자들의 데이터들도 불러올 수 있다.
- 우주 사진에 대한 번역된 설명을 제공한다.
<사용할 것>
NASA OpenAPI
React 18 (+ Typescript)
RTK (Redux Toolkit)
Node.js
네이버 Papago API
이렇게 사용하여 만들려고 한다.
사실 Redux도 필요없긴한데, Typescript + Redux 조합이 좀 더 익숙해졌으면해서 붙여보았다.
<제작기 (1): 리덕스 툴킷 (Redux Toolkit)>
1. Redux Toolkit + Typescript 템플릿을 사용하여 프로젝트 생성하기
# Redux + Plain JS template
npx create-react-app my-app --template redux
# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
Redux Toolkit 공식홈페이지에서 제공하는 타입스크립트 템플릿을 사용하면 큰 노력없이 프로젝트 구축이 가능하다.
직접 하나하나 세팅해도 되지만 어차피 난 신규 프로젝트이므로 템플릿을 사용하여 프로젝트를 만든 뒤 정리하겠다.

그랬더니 위와 같은 구조로 만들어졌다. 예제 파일들이나 안쓸 파일들을 슥슥 정리해서...
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── app
│ │ └── store.ts
│ ├── components
│ │ ├── Apod.tsx
│ │ └── Modal.tsx
│ ├── data // LOTTIE
│ │ └── lottie-cat.json
│ ├── features
│ │ └── apod
│ │ └── apodSlice.ts
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ ├── setupTests.ts
│ └── types
├── tsconfig.json
└── yarn.lock
주요한 파일들 리스트는 이렇게...
app 폴더 내에 store.ts는

이렇게 생겨먹었다. 리덕스 툴킷은 스토어의 설정을 조금 더 보기좋고 직관적인 형태로 할 수 있고 저런식으로 리듀서의 이름을 명명하고 넣어주기만하면 알아서 combine해준다.
리덕스는 리액트에서의 상태관리와 뗄래야 뗄 수 없는 관계지만, 치명적인 단점이 있다.
사용하기 전까지의 세팅에 많은 노력들이 들어가고 사용하는 사람마다 디렉토리 구조나, 손에 익는 사용패턴이 조금씩 달라 코드 파악을 하는 것이 복잡해지기도 한다.
조금 더 깔끔한 구조를 만들고 정형화된 틀을 만들기 위해 몇몇 작성 패턴들이 존재하지만
React의 개념 하나 잡는데도 힘든데 상태관리를 위한 툴인 Redux조차 학습이 필요하다는 것은 조금 부담스러울 수도 있다.
이런 단점을 보완(RTK는 Simple하다고 써있지만, 상대적으로 그렇단거지 마냥 심플하진 않다고 느꼈다.)하고자 리덕스 툴킷이 탄생했다.
리덕스 툴킷은 store 세팅을 쉽게해주고, reducer 세팅을 쉽게해준다. 아무튼 간단한 프로젝트이니만큼, 리듀서는 하나만 쓰고 있고

단순한 동작만을 정의해두었다.
이름과 코드를 보면 알 수 있듯 ADD_DAY와 SUB_DAY는 API를 호출할때 사용할 날짜의 조절을 위한 동작들이다.
- NASA의 APOD API는 1995년 6월 16일 이후부터 데이터를 가지고 있고 당연하게도 미래의 날짜는 데이터가 없다.
그래서 호출이 가능한 minDate와 maxDate는 각각 '1995-06-16'과 오늘 날짜로 세팅되도록 하였다.
- 만일 minDate와 maxDate 범위를 벗어난 날짜로 세팅되는 경우엔 호출대상이 되는 날짜를 변경하지 않는다.
근디...reducer는 알겄는디 extraReducer는 또 뭐시여...? 머리 돌아가부렁
RTK에선 slice라는 아주 중요한 요소가 있다. slice는 action + Reducer라고 생각하면 된다.
소스코드 상에서 apodSlice라는 변수가 createSlice라는 함수로 만들어지는 것을 볼 수가 있는데 공식문서의 createSlice 설명을 보면 아래와 같이 설명되어있다.
A function that accepts an initial state, an object of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.
createSlice는 자동으로 액션을 정의해준다. 그래서 reducer 안에 작성한 ADD_DAY, SUB_DAY, LOAD_TOGGLE_STATUS 함수는 따로 액션을 생성해주지 않더라도 해당 함수에 맞는 액션이 알아서 생성된다.
이런 식으로 createSlice 내에서 정의된 함수(와 액션)은 '내부'에서 정의된 것으로 간주하고, 자신(createSlice) 외에서 정의된 함수(와 액션)은 extraReducer로 간주하는 것이다.
이러한 구조의 이점은 각 slice마다의 독립적인 함수를 갖고 있을 수 있게 해주고 (slice 내부에서 정의된 함수)
동시에 외부에서 작성된 함수들을 slice 내에 편입시켜 관리할 수도 있다.

또 RTK에서 강제하고 있는 사항은 아니지만 비동기 호출(데이터 패칭 같은)은 extraReducer로, 동기 호출은 내부 reducer로 관리하기에 적합하게 만들어져있다.
그래서 이 프로젝트는 리듀서가 1개만 존재하므로 딱히 extraReducer가 필요없을 수도 있었겠으나
API를 호출하는 부분은 아래와 같이 따로 정의해 사용하였다.
export let FETCH_DATA = createAsyncThunk('API/FETCH_DATA',async (_targetDate: string) => {
// console.log(`http://localhost:3500/${_targetDate}`);
return axios({
method: "get",
url: `http://주소요.../${_targetDate}`
})
.then(res => {
return res.data
})
.catch(() => {
console.log('데이터 조회에 실패하였습니다.');
})
})
extraReducer는 case들을 정의할 수 있어서 API로 데이터 페칭을 할때 동작정의가 용이하다.
function App() {
const dispatch = useAppDispatch();
const targetDate = useAppSelector((state) => state.apodReducer.targetDate);
useEffect(() => {
dispatch(FETCH_DATA(targetDate));
}, [dispatch, targetDate])
const dateHandler = (flag: string) => {
if (flag === 'ADD') {
dispatch(ADD_DAY());
} else {
dispatch(SUB_DAY());
}
}
return (
<>
...
... onClick={() => dateHandler('SUB')} ...
...
</>
)
}
이렇게 정의된 slice (액션 + 함수(리듀서))들은 이렇게 간단하게 dispatch해 사용 할 수 있다.
뭐어~?! Redux Thunk도 안쓰는데 함수를 디스패치 할 수 있다구?! Redux Saga도 안쓰는데 요청 상태에 따른 동작을 관리해?!
그렇셈. RTK 하나 쓰면 요거저거 간단하게 쓸 수 있다 이것이다. 쩌러쩌러
이렇게 onClick 이벤트를 통해 발생한 dateHandler 함수를 통해 날짜 변수가 변경되고
날짜변수가 변경되면 FETCH_DATA 함수가 돌며 API를 호출하게 된다.

심플...음...

죽어라 리덕스
'프로그래밍' 카테고리의 다른 글
Reactoron으로 Redux state 모니터링 하기 + console.log 연결 (0) | 2022.08.30 |
---|---|
리액트 네이티브(React-native)의 디버깅 도구 (React-devtools, Reactoron) (4) | 2022.08.30 |
[토이프로젝트] today-woozoo 제작기 (2/2) - 이미지 로딩처리, 번역모듈 부착 (0) | 2022.07.13 |