개발일지
나의 늦은 typescript 적응기 본문
Typescript를 적용해야 한다
현재 React-native를 사용하면서 사내 프로젝트를 진행중인데, typescript를 사용하지 않고 진행하다 보니 수많은 상태와, 변수 등에 둘러싸여 작업을 진행하는데 계속해서 불편함을 가지고 있었다. typescript를 도입하게 된다면, 자동완성 및 프로젝트 관리에 효율적으로 진행 할 수 있을 것 같아 타입스크립트를 공부하려 한다.
이번 포스팅을 하면서, redux-toolkit과 hooks를 이용하여, TextInput에 입력한 값을 나열하는 기본적인 프로젝트를 진행했다.
React-Native에 적용하기
npx react-native init "나의 앱 이름" --template react-native-template-typescript
Redux-toolkit 설정하기
프로젝트 폴더/src/slices 폴더를 생성해서, contentsSlice.ts 파일과 store.ts 파일을 생성했다.
// contentsSlice.ts
import {createSlice, PayloadAction} from '@reduxjs/toolkit';
export interface UpdateContents {
id: number;
contents: string | undefined;
}
const initialState = [
{
id: 0,
contents: '기본 데이터',
},
] as UpdateContents[];
export const contentsSlice = createSlice({
name: 'contents',
initialState,
reducers: {
update: (
state: UpdateContents[],
action: PayloadAction<UpdateContents>,
) => {
return [...state, action.payload];
},
},
});
export default contentsSlice.reducer;
export const {update} = contentsSlice.actions;
1. interface
인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다. TS에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있다.
- 객체의 스펙(속성과 속성의 타입)
- 함수의 파라미터
- 함수의 스펙(파라미터, 반환 타입 등)
- 배열과 객체를 접근하는 방식
- 클래스
(출처 : https://velog.io/@soulee__/TypeScript-Interface-2)
2. UpdateContents[]
UpdatteContents라는 객체의 스펙이 나열된 배열을 의미한다.
3. PayloadAction<UpdateContents>
PayloadAction은 액션의 payload 타입을 지정할 수 있게 도와주는 제네릭 이다.
제네릭이란, 단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성할 수 있고, 제네릭을 통해 여러 타입의 컴포넌트나 자신만의 타입을 사용할 수 있게 된다. Typescript에서 무언가를 반환하게 되는 경우, any타입을 사용한다면 어떤 값을 반환하더라도 에러가 나지 않지만, 반환하는 값의 타입이 어떤 값인지를 잃게 된다. 여기서 <>안에 타입 변수를 넣게 된다면 인수(반환값)의 타입을 캡쳐하고, 이 정보를 나중에 사용할 수 있도록 한다.
// store.ts
import {combineReducers, configureStore} from '@reduxjs/toolkit';
import contentsSlice from './contentsSlice';
const reducer = combineReducers({
contents: contentsSlice,
});
const store = configureStore({
reducer,
});
export type ReducerType = ReturnType<typeof reducer>;
export type AppDispatch = typeof store.dispatch;
export default store;
store.ts 는 다음을 참고하여 작성(https://redux-toolkit.js.org/usage/usage-with-typescript)
//App.tsx
import React from 'react';
import {Provider} from 'react-redux';
import store from './src/slices/store';
import Main from './src/screens/Main';
function App() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
export default App;
Provider를 이용하여 상태를 주입해준다.
Hooks 이용하기
//useUpdateContents.ts
import {useDispatch, useSelector} from 'react-redux';
import {ReducerType} from '../slices/store';
import {UpdateContents, update} from '../slices/contentsSlice';
export default function useUpdateContents() {
const dispatch = useDispatch();
const contents = useSelector<ReducerType, UpdateContents[]>(
store => store.contents,
);
const onUpdateContents = (data: UpdateContents) => {
dispatch(update(data));
};
return {
contents,
onUpdateContents,
};
}
//useInput.ts
import {useState} from 'react';
export default function useInput() {
const [value, setValue] = useState<string | undefined>('');
const onChangeText = (data: string) => {
setValue(data);
};
return {
value,
onChangeText,
};
}
contents의 상태를 업데이트하는 onUpdateContents, 현재 contents 상태값을 불러오는 contents를 반환하는 useUpdateContents.ts를 만들고, input값을 변경하고 input값을 가져오는 useInput.ts도 생성하였다.
그리고, Typescript에서의 props 연습을 위해, TextInput을 재사용 가능한 컴포넌트로 만들어 사용하였다.
// CustomInput.tsx
import {TextInput, StyleSheet} from 'react-native';
import React from 'react';
interface CustomInputProps {
value: string | undefined;
onChangeText: (data: string) => void;
onSubmitEditing: () => void;
}
export default function CustomInput({
value,
onChangeText,
onSubmitEditing,
}: CustomInputProps) {
return (
<TextInput
value={value}
onChangeText={onChangeText}
style={styles.inputContainer}
onSubmitEditing={onSubmitEditing}
/>
);
}
const styles = StyleSheet.create({
inputContainer: {
borderWidth: 1,
borderColor: '#333333',
borderRadius: 5,
width: '80%',
paddingHorizontal: 16,
paddingVertical: 5,
},
});
1. Typescript에서의 함수
Typescript에서는 함수를 사용할 때 인자를 사용한다면 인자의 타입도 설정하고, return값이 존재한다면 return값에 대한 타입 또한 지정해야한다.
위와 같은 경우는 onChangeText에서 인자를 사용하여 인자값의 타입을 string으로 사용하고, void를 사용하여 return값이 없다는 선언을 해준다.
최종 코드
// Main.tsx
import React from 'react';
import {
View,
SafeAreaView,
Text,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import CustomInput from '../components/CustomInput';
import useInput from '../hooks/useInput';
import useUpdateContents from '../hooks/useUpdateContents';
function Main() {
const userId = useInput();
const {contents, onUpdateContents} = useUpdateContents();
const onPress = () => {
const data = {
id: contents[contents.length - 1].id + 1,
contents: userId.value,
};
onUpdateContents(data);
userId.onChangeText('');
};
return (
<SafeAreaView>
<View style={styles.inputContainer}>
<CustomInput
value={userId.value}
onChangeText={userId.onChangeText}
onSubmitEditing={onPress}
/>
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<Text>버튼</Text>
</TouchableOpacity>
</View>
<View>
{contents.map(item => {
return (
<View key={item.id}>
<Text>{item.contents}</Text>
</View>
);
})}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
inputContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-around',
},
buttonContainer: {
paddingHorizontal: 10,
paddingVertical: 5,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: '#333333',
borderRadius: 5,
backgroundColor: '#e3e3e3',
},
});
export default Main;
위의 컴포넌트들과 hooks, 그리고 redux-toolkit을 이용하여 textinput에 값을 입력하고 그 값이 나열되는 프로젝트를 진행하였다.
'개발일지 > React-Native' 카테고리의 다른 글
RN - Fabric 모드 전환 시 Metro에 연결이 안되는 문제 (0) | 2024.10.30 |
---|---|
React-Navigation과 React-native-safe-area-context의 중복 여백 문제 (1) | 2024.10.17 |
React Navigation - native-stack 뒤로가기 방지 오류 (0) | 2024.09.11 |
react-native-nmap 기능 추가하기 (0) | 2022.04.26 |