[기능 구현]
- 글 작성
- 글 목록 조회
- 달력 보기
- 검색 기능
- 수정 하기, 삭제하기
0. 화면 설계
1. react-navigation 적용하기
- RootStack: 네이티브 스택 내비게이션 사용
- MainTab: 하단 탭 내비게이션 사용
- FeedsScreen: 작성한 글 목록 형태로 보여주는 화면
- CalenderScreen: 작성한 글 달력 형태로 보여주는 화면
- SearchScreen: 글 검색할 수 있는 화면
- WriteScreen: 글 작성, 수정하는 화면
- MainTab 화면
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import React from 'react';
import CalendarScreen from './CalendarScreen';
import FeedsScreen from './FeedsScreen';
import SearchScreen from './SearchScreen';
import Icon from 'react-native-vector-icons/MaterialIcons'; // {Icon} XX
const Tab = createBottomTabNavigator();
function MainTab() {
return (
<Tab.Navigator
screenOptions={{ // tabBarOptions XX
showLabel: false,
activeTintColor: '#009688',
}}>
<Tab.Screen
name="Feeds"
component={FeedsScreen}
options={{
tabBarIcon: ({color, size}) => (
<Icon name="view-stream" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Calendar"
component={CalendarScreen}
options={{
tabBarIcon: ({color, size}) => (
<Icon name="event" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
tabBarIcon: ({color, size}) => (
<Icon name="search" size={size} color={color} />
),
}}
/>
</Tab.Navigator>
);
}
export default MainTab;
app > build.gradle에 추가
project.ext.vectoricons = [
iconFontNames: ['MaterialIcons.ttf']
]
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
2. Context API 사용하기(App, FeedScreen, CalendarScreen)
- 리액트에 내장된 기능
- Props를 사용하지 않아도 특정 값이 필요한 컴포넌트끼리 쉽게 값을 공유할 수 있게 해줌
- createContext: 새로운 Context 만들 때
- <LogContext.Provider> : Context 안에 있는 값을 사용할 컴포넌트들을 감싸주는 용도
- value(Props) : Context를 통해 여러 컴포넌트에서 공유할 수 있는 값
- 이 컴포넌트 내부에 선언 된 모든 컴포넌트에서 Context 안의 값을 사용할 수 있음
- <LogContext.Consumer>: Context 안의 값을 사용할 때
- Render Props: 컴포넌트 태그 사이에 함수 넣는 것/ 사용할 컴포넌트에서 특정 값을 밖으로 빼내와 사용할 수 있음
- Context의 값을 읽는 부분이 JSX에 있기 때문에 컴포넌트 로직 작성이 까다로움
- <LogContext.Provider> : Context 안에 있는 값을 사용할 컴포넌트들을 감싸주는 용도
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import RootStack from './screens/RootStack';
import LogContext from './contexts/LogContext';
function App() {
return (
<NavigationContainer>
<LogContext.Provider value='안녕하세요'>
<RootStack />
</LogContext.Provider>
</NavigationContainer>
);
}
export default App;
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
import LogContext from '../contexts/LogContext';
function FeedsScreen() {
return (
<View style={styles.block}>
<LogContext.Consumer>
{(value) => <Text>{value}</Text>}
</LogContext.Consumer>
</View>
);
}
const styles = StyleSheet.create({
block: {},
});
export default FeedsScreen;
- children Props: 컴포넌트 태그 사이에 넣어준 값
- useContext Hook 함수: 컴포넌트에서 JSX를 반환하기 전에 값을 조회할 수 있기 때문에 컴포넌트 로직 작성이 편리함
3. 새 글 작성하기(WriteScreen)
- FloatingWriteButton: FeedsScreen 우측 모서리에 나타나는 둥근 버튼
- <Pressable> : TochableWithoutFeedback과 비슷 but 기능 더 많음
- android_ripple(Props): 물결 효과
- pressed(Style): 컴포넌트가 눌리면 동적인 스타일 적용
- WriteScreen UI 준비
- WriteHeader : 뒤로가기, 삭제, 완료 버튼
- TransparentCircleButton({name, color, hasMarginRight, onPress})
- WriteEditor : 제목, 내용 작성
- Props 이름만 쓰고 값 지정 X --> 값이 true로 지정
- TextView 값이 true --> 여러 줄 작성 가능
- WriteHeader : 뒤로가기, 삭제, 완료 버튼
- useRef
- 함수 컴포넌트에서 컴포넌트의 레퍼런스를 선택할 수 있게 하는 Hook
- ref를 생성해 TextInput의 Prop로 지정 --> 원하는 컴포넌트의 레퍼런스 선택 가능
- .focus() : TextInput에 포커스를 잡아줌
- .blur() : TextInput에 포커스를 해제
- .clear() : TextInput의 내용을 모두 비움
- useReft로 서낵한 레퍼런스는 .current 값을 조회해 확인 가능
- keyboardAvoidingView
- 화면에서 기본적으로 보여줄 수 있는 줄 수를 초과 --> iOS에서 하단 내용 잘림
- keyboardAvoidingView로 WriteScreen 감싸줘야 글 길어져도 문제없이 글 작성 가능
- 제목, 내용 텍스트 상태 관리 --> useState 사용
- 배열 상태 관리 --> LogContext
- const [logs, setLogs] = useState([]);
- logs 배열을 객체로 감싸서 value에 넣어줌 -- 객체로 감싸는 이유: logs 배열 외에도 상태에 변화 주는 함수를 같이 넣어줄 것이므로
- uuid 라이브러리
- 고유한 값 생성
- yarn add uuid
- yarn add react-native-get-random-values
- WriteScreen에서 useContext를 사용해 LogContext값 받아오고 onSave함수 만들기
- onSave 함수 완성 --> WriteHeader에 Props로 넣기 --> 체크 버튼을 누를 때 함수 호출
- FeedsScreen에서 useContext 사용해 LogContext 값 받아오기
4. 글 목록 보여주기
- FeedListItem 컴포넌트 만들기
- log 객체 Props로 받아와서 알맞은 위치에 정보 보여줌
- truncate 함수: 줄 바꿈 문자 없애기, 글 길어지면 줄임표 붙이기
- Pressable 컴포넌트: 글 선택하면 WriteScreen 띄워서 전체 내용 볼 수 있게 해줌
- FeedList 컴포넌트 만들기
- logs 객체 Props로 받아와서 FlatList 통해 여러 항목 보여줌
- FeedScreen에서 사용
5. date-fns로 날짜 포맷팅하기
- 작성한 시간에 따라 방금 전, 3분전, 1시간 전, 3일 전, 2021년 8월 23일 07:00 이렇게 보이도록 하기
- date-fns 라이브러리 사용 : yarn add date-fns p.334
- formatDistanceToNow: 현재 시각을 기준으로 단어를 사용해 시간 나타냄
- format: 다양한 형태로 날짜 포맷팅
6. Animated로 애니메이션 적용하기
- 항목이 많아져서 스크롤해야 할 때 -> 우측 하단 버튼이 항목의 내용 가림 -> 버튼이 하단으로 사라지는 애니메이션을 적용해 자연스럽게 숨기기
- Animated 객체 사용 - value 만들어야 함
- function Sample() { const animation = useRef(new Animated.Value(1)).current; }
- Value의 생성자 함수의 인자에는 초깃값 넣어주기
- <Animated.컴포넌트 이름> 이런 형식으로 사용
- .start()로 시작
- 예외 처리
7. 작성한 글 WriteScreen으로 열기
- FeedListItem 컴포넌트에서 Pressable에 onPress Props를 설정해 컴포넌트 눌렀을 때 Props로 받아온 log 객체를 WriteScreen의 파라미터로 넣어서 화면 열기
- WriteScreen에서 log 파라미터 인식해 파라미터가 주어졌을 때 제목과 내용의 기본값 지정
- 옵셔널 체이닝 문법: ?. -- null이거나 undefined일 수 있는 객체의 프로퍼티를 에러 없이 접근 가능
- log?.body ?? '' ----> log?.body가 유효한 값이라면 해당 값 사용, 아니면 공백 문자열 사용
8. 수정 기능 구현
- WriteScreen의 onSave 함수에서 log 라우트 파라미터가 유효하면 수정하는 함수 호출, 아니면 생성하는 함수 호출
- LogContext에서 onModify 함수 구현
- WriteScreen에서 onModify 함수 사용
9. 삭제 기능 구현
- LogContext에서 onRemove 함수 구현
- WriteScreen에서 onRemove 함수 사용
- Alert.alert를 사용해 사용자에게 정말 삭제할 것인지 한 번 더 물어보기
- isEditing 값 전달 --> WriteHeader에서 이 값 확인해 삭제 버튼 보여줄지 말지 결정
- !!log : log가 유효한 값이면 true, 그렇지 않으면 false 값 전달
- isEiditing Props가 true일 때만 삭제버튼 보여주고, 버튼 눌렀을 때 onAskRemove 호출하기
10. 검색 기능 구현
- 검색 하려면 화면의 헤더 부분에 텍스트를 입력할 수 있어야 하므로, 헤더 커스터마이징 하기
- SearchHeader 컴포넌트 만들기
- MinTab 검색화면 설정 수정
- 화면 크기 조회 - dp 단위로 가져오기
- Dimensions.get 사용
- useWindowDimensions Hook 사용 - 값 바뀌면 컴포넌트에서 자동으로 반영 / 함수 컴포넌트 내부에서만 사용 가능 / 전체 화면의 크기 가져오는 기능은 없음
- SearchHeader 컴포넌트 UI 구성
- TextInput, 검색어 초기화하는 버튼 보여주기
- SearchContext 만들기
- 검색어 상태 관리
- 검색어 필터링 후 FeedList 재사용하기
- 배열 내장 함수 filter 사용 --> 검색어를 사용해 배열의 데이터 필터링 후 결과물을 FeedList 컴포넌트 재사용해 화면에 띄우기
- 검색 결과가 없을 때 안내 문구 보여주는 EmptySearchResult 컴포넌트 만들기
- 사용자가 검색어를 입력하지 않았을 때
- 검색어와 일치하는 결과물이 없을 때